Ir al contenido principal

Sección 07

07 · Metaobjects

Contenido desacoplado del catálogo — la content platform.

En una frase

Los metaobjects son entidades de primera clase que tú defines. No son productos, no son clientes, no son pedidos — son los modelos que Shopify no tiene en su catálogo estándar y que tu negocio necesita: Finca, Región, Caficultor, Autor, Testimonio. Con metaobjetos, Shopify actúa como tu content platform.

Qué es y por qué existe

Los metafields de la sección anterior extienden entidades que ya existen en Shopify. Un Product ya existe; tú le agregas columnas. Pero a veces necesitas una entidad completamente nueva que no tiene ningún análogo en el modelo estándar de Shopify.

Para Brew Atlas, la Finca es una entidad de negocio real. Una finca tiene nombre, región, altitud, propietario, fotografía, y descripción. No es un producto — no se vende directamente. No es una colección — no agrupa variantes. Es una entidad independiente con sus propios campos, que se puede referenciar desde múltiples productos (porque un caficultor puede producir varios tipos de café).

Los metaobjetos son la respuesta de Shopify a este problema. Son entidades custom que tú defines con el tipo (machine name), los campos (mismo catálogo de tipos que los metafields), y opcionalmente una URL pública.

Shopify introdujo los metaobjetos en 2022 como parte de su apuesta por ser una content platform — la tesis de que los comerciantes no necesitan Contentful o Strapi si Shopify puede albergar ese contenido editorial. En 2026, la apuesta se consolidó: los metaobjetos tienen acceso desde Liquid, desde Storefront GraphQL, desde Admin GraphQL, y pueden tener páginas propias con templates dedicados.

Estructura de un metaobjeto

Cada metaobjeto tiene dos capas:

Definición de tipo (MetaobjectDefinition): el esquema. Define el type (ej: finca), los campos disponibles, y los display settings. Esto se crea una vez y determina qué datos puede tener cada entrada del tipo.

Entrada (Metaobject entry): una instancia del tipo. Una finca específica — “La Palma y El Tucán” — es una entrada del tipo finca. Tiene un handle (slug URL-safe) y valores para cada campo de la definición.

Los tipos tienen nombres en snake_case, en minúsculas, sin espacios. Las entradas tienen handles únicos dentro del tipo. Si creas una entrada con el handle la-palma-el-tucan del tipo finca, su referencia completa es finca/la-palma-el-tucan.

Campos y tipos

Los campos de un metaobjeto usan exactamente el mismo catálogo de tipos que los metafields. Los más usados en contextos de content:

  • single_line_text_field — texto corto, títulos, nombres
  • multi_line_text_field — texto largo sin HTML
  • rich_text_field — texto enriquecido con formato
  • number_integer — números enteros (altitud en metros, año de fundación)
  • file_reference — imágenes y otros archivos de la biblioteca de medios
  • metaobject_reference — referencia a otro metaobjeto (composición de entidades)
  • list.metaobject_reference — lista de referencias a metaobjetos

La posibilidad de que un metaobjeto referencie a otro metaobjeto es lo que permite construir grafos de contenido. Una Finca puede referenciar una Región; un Caficultor puede pertenecer a varias Fincas. Eso es un grafo de contenido completamente modelado dentro de Shopify.

Linking con productos vía metafield

La conexión entre un producto y un metaobjeto se establece a través de un metafield de tipo metaobject_reference. En la sección anterior definiste product.custom.farm como metaobject_reference. Ese metafield apunta a una entrada específica del tipo finca.

Desde el panel de Admin, cuando el comerciante edita un producto, ve el campo “Finca” (que tiene la definición del metafield) y puede seleccionar una entrada de las fincas disponibles. Shopify renderiza ese campo como un picker de metaobjetos — el comerciante no tiene que escribir ningún ID ni handle manualmente.

Desde Liquid, el valor de ese metafield es directamente el objeto de la finca referenciada:

{%- comment -%} ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid — tarjeta de procedencia {%- endcomment -%}
{%- assign finca = product.metafields.custom.farm.value -%}
 
{%- if finca != blank -%}
  <aside class="provenance">
    <h4 class="provenance__name">{{ finca.name.value }}</h4>
    <p class="provenance__location">
      {{ finca.region.value }}
      {%- if finca.altitude_masl.value != blank -%}
        &nbsp;· {{ finca.altitude_masl.value }} msnm
      {%- endif -%}
    </p>
    {%- if finca.grower.value != blank -%}
      <p class="provenance__grower">{{ finca.grower.value }}</p>
    {%- endif -%}
    {%- if finca.description.value != blank -%}
      <div class="provenance__description rte">
        {{ finca.description.value }}
      </div>
    {%- endif -%}
  </aside>
{%- endif -%}

Observa que el acceso a los campos de un metaobjeto desde Liquid usa la sintaxis finca.nombre_del_campo.value. El objeto de metaobjeto no expone sus campos como propiedades directas — son accesibles como un objeto con .value.

Acceso directo a todos los metaobjetos

Cuando necesitas listar todos los metaobjetos de un tipo (por ejemplo, renderizar la página de “Nuestros orígenes” con todas las fincas), Liquid expone shop.metaobjects.

Edita ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/farms-list.liquid:

{%- comment -%} ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/farms-list.liquid {%- endcomment -%}
{%- assign all_fincas = shop.metaobjects.finca.values -%}
 
<div class="farms-grid">
  {%- for finca in all_fincas -%}
    <article class="farm-card">
      {%- if finca.photo.value != blank -%}
        <img
          src="{{ finca.photo.value | image_url: width: 600 }}"
          alt="{{ finca.name.value | escape }}"
          width="600"
          height="400"
          loading="lazy"
        />
      {%- endif -%}
      <h3>{{ finca.name.value }}</h3>
      <p>{{ finca.region.value }}</p>
    </article>
  {%- endfor -%}
</div>

Acceso desde Storefront GraphQL

Guarda esta query en ~/proyectos/shopify/brew-atlas/brew-atlas-app/src/queries/metaobjects-fincas.graphql:

# ~/proyectos/shopify/brew-atlas/brew-atlas-app/src/queries/metaobjects-fincas.graphql
query ListFincas($first: Int!) {
  metaobjects(type: "finca", first: $first) {
    edges {
      node {
        handle
        fields {
          key
          value
          type
          reference {
            ... on MediaImage {
              image {
                url
                altText
              }
            }
          }
        }
      }
    }
  }
}

La respuesta devuelve los campos como un array — no como un objeto con propiedades nombradas. Para convertirlo a un mapa más cómodo en TypeScript:

Guarda esta utilidad en ~/proyectos/shopify/brew-atlas/brew-atlas-app/src/utils/parse-metaobject.ts:

// ~/proyectos/shopify/brew-atlas/brew-atlas-app/src/utils/parse-metaobject.ts
type MetaobjectField = { key: string; value: string | null; type: string };
 
export function fieldsToMap(fields: MetaobjectField[]) {
  return Object.fromEntries(fields.map((f) => [f.key, f.value]));
}
// Uso: const finca = fieldsToMap(node.fields);
// finca.name, finca.region, etc.

Rutas dedicadas para metaobjetos

Puedes asignar un template de Liquid a un tipo de metaobjeto, haciendo que cada entrada tenga su propia URL pública:

  • URL resultante: /metaobjects/finca/la-palma-el-tucan
  • Template en el theme: templates/metaobject.finca.json

Esta funcionalidad convierte a los metaobjetos en páginas de contenido independientes. Una Finca puede tener su propia página de destino con galería, descripción completa, y los cafés que produce. El template se gestiona como cualquier otro template de OS 2.0 — JSON con sections, editable desde el Theme Editor.

Para habilitar la ruta, en la definición del tipo debes activar “Storefront access” y configurar el URL handle. En el Admin de tu dev store: Contenido → Metaobjetos → [tipo] → Configuración → URL de storefront.

Para incluir esas páginas en el sitemap, el theme tiene que declarar que ese tipo de metaobjeto es indexable. Con la configuración correcta, Shopify las incluye automáticamente en sitemap.xml.

Puentes mentales

Desde Angular/React/Node

Los metaobjetos son a Shopify lo que las entidades custom son a Contentful o Strapi — pero nativas, sin tener que mantener un CMS externo. Si alguna vez usaste Contentful para modelar “Autor” o “Testimonio” junto a una tienda de e-commerce, los metaobjetos resuelven ese problema directamente dentro de Shopify.

Si vienes de Angular o React, la composición de metaobjetos (un metaobjeto que referencia otro) es análoga a las relaciones entre modelos en un ORM: Product tiene un farm_id que apunta a Farm. La diferencia es que en Shopify esa relación se resuelve con referencias tipadas, no con joins de SQL.

En términos de backend, un tipo de metaobjeto es como un schema de Mongoose o un model de Sequelize — defines la estructura una vez, y todas las instancias deben cumplirla.

Estado 2026

Los metaobjetos tienen API estable en 2026. Los puntos clave del estado actual:

  • Storefront access: por defecto, los metaobjetos NO son accesibles desde el Storefront API ni desde Liquid. Para cada tipo, tienes que activar “Storefront access” explícitamente en la definición. Sin eso, las queries al Storefront API devuelven null.
  • Liquid access: la sintaxis shop.metaobjects.type.values requiere que el tipo tenga storefront access habilitado.
  • SEO e indexación: los metaobjetos con template asignado y storefront access pueden ser indexados por buscadores. Sin el template, la URL existe pero devuelve 404.
  • Límites: una tienda puede tener hasta 200 tipos de metaobjeto y hasta 100.000 entradas por tipo (en planes básicos — consulta la documentación para límites actualizados de planes avanzados).

Sintaxis y anatomía

Definición de tipo y lectura completa con template de página dedicada:

# Mutation Admin API: crear definición del tipo Finca
mutation CreateFincaDefinition($definition: MetaobjectDefinitionCreateInput!) {
  metaobjectDefinitionCreate(definition: $definition) {
    metaobjectDefinition {
      type
      name
      fieldDefinitions { key type { name } }
    }
    userErrors { field message }
  }
}

Con variables:

{
  "definition": {
    "type": "finca",
    "name": "Finca",
    "displayNameKey": "name",
    "access": {
      "storefront": "PUBLIC_READ"
    },
    "fieldDefinitions": [
      { "name": "Nombre",      "key": "name",          "type": "single_line_text_field" },
      { "name": "Región",      "key": "region",         "type": "single_line_text_field" },
      { "name": "Altitud msnm","key": "altitude_masl",  "type": "number_integer" },
      { "name": "Caficultor",  "key": "grower",         "type": "single_line_text_field" },
      { "name": "Foto",        "key": "photo",          "type": "file_reference" },
      { "name": "Descripción", "key": "description",    "type": "rich_text_field" }
    ]
  }
}

Crear una entrada (Admin API):

mutation CreateFincaEntry($metaobject: MetaobjectCreateInput!) {
  metaobjectCreate(metaobject: $metaobject) {
    metaobject {
      handle
      fields { key value }
    }
    userErrors { field message }
  }
}

Con variables:

{
  "metaobject": {
    "type": "finca",
    "handle": "la-palma-el-tucan",
    "fields": [
      { "key": "name",         "value": "La Palma y El Tucán" },
      { "key": "region",       "value": "Cundinamarca, Colombia" },
      { "key": "altitude_masl","value": "1800" },
      { "key": "grower",       "value": "Felipe Sardi" }
    ]
  }
}

Proyecto · Brew Atlas

Nota

Esta sección no requiere comandos de terminal. Los tipos de metaobjeto se crean desde el Admin de tu dev store (Contenido → Metaobjetos → Agregar definición) y el archivo del theme se edita en tu scaffold local.

Brew Atlas · Paso 7

Brew Atlas define dos tipos de metaobjeto que representan las entidades de negocio de la plataforma de origen de café.

Qué vas a crear/tocar (archivos):

  • ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid — agregar la tarjeta de procedencia de la finca al PDP.
  • ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/farms-list.liquid — sección que lista todas las fincas disponibles.

Dónde crear los tipos de metaobjeto: Navega en el Admin a Contenido → Metaobjetos → Agregar definición y crea los dos tipos:

Tipo finca (granja / finca productora):

  • namesingle_line_text_field — nombre de la finca
  • regionsingle_line_text_field — región y país (ej: “Cundinamarca, Colombia”)
  • altitude_maslnumber_integer — altitud en metros sobre el nivel del mar
  • growersingle_line_text_field — nombre del caficultor responsable
  • photofile_reference — imagen de la finca
  • descriptionrich_text_field — descripción editorial de la finca

Tipo region (región cafetera):

  • namesingle_line_text_field — nombre de la región
  • countrysingle_line_text_field — país
  • map_imagefile_reference — mapa o imagen representativa
  • tasting_profilemulti_line_text_field — descripción del perfil de sabor típico de la región

Recuerda activar Storefront access → PUBLIC_READ en cada tipo para que sean accesibles desde Liquid y la Storefront API.

Comandos a ejecutar: ninguno. Todo el trabajo de esta sección es en el Admin UI y en los archivos del theme.

Edita ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid para agregar la tarjeta de procedencia de la finca:

{%- comment -%} ~/proyectos/shopify/brew-atlas/brew-atlas-theme/sections/pdp.liquid — tarjeta de procedencia {%- endcomment -%}
{%- assign finca = product.metafields.custom.farm.value -%}
 
{%- if finca != blank -%}
  <aside class="provenance" aria-label="Procedencia del café">
    {%- if finca.photo.value != blank -%}
      <div class="provenance__image">
        <img
          src="{{ finca.photo.value | image_url: width: 400 }}"
          alt="{{ finca.name.value | escape }}"
          width="400"
          height="300"
          loading="lazy"
        />
      </div>
    {%- endif -%}
 
    <div class="provenance__info">
      <h4 class="provenance__name">{{ finca.name.value }}</h4>
      <p class="provenance__location">
        {{ finca.region.value }}
        {%- if finca.altitude_masl.value != blank -%}
          &nbsp;· {{ finca.altitude_masl.value }} msnm
        {%- endif -%}
      </p>
 
      {%- if finca.grower.value != blank -%}
        <p class="provenance__grower">
          <span class="label">Caficultor</span>
          {{ finca.grower.value }}
        </p>
      {%- endif -%}
 
      {%- if finca.description.value != blank -%}
        <div class="provenance__description rte">
          {{ finca.description.value }}
        </div>
      {%- endif -%}
    </div>
  </aside>
{%- endif -%}

Errores comunes

Cuidado

Eliminar una entrada de metaobjeto NO pone en null las referencias que apuntan a ella. Si borras la finca “La Palma” y hay 10 productos que la referencian vía product.custom.farm, esos metafields siguen teniendo el ID de la entrada eliminada — pero cuando los accedes desde Liquid, devuelven blank. Siempre verifica != blank antes de acceder a propiedades de un metaobjeto referenciado, especialmente en snippets que pueden ejecutarse para muchos productos.

Cuidado

Los metaobjetos NO son accesibles desde el Storefront API ni desde Liquid por defecto. Si creas el tipo y las entradas pero olvidas activar “Storefront access: PUBLIC_READ” en la definición del tipo, todas tus queries al Storefront API y todos tus accesos desde Liquid van a devolver null. Este es el error más frecuente al configurar metaobjetos por primera vez.

Nota

El campo displayNameKey en la definición determina qué campo usa Shopify como label en la Admin UI y en los pickers. Si no lo defines, la Admin UI muestra el handle de la entrada. Para entidades como Finca o Región, apuntarlo al campo name mejora significativamente la experiencia del comerciante cuando selecciona una finca desde el metafield del producto.

Tip

Para depurar el contenido de un metaobjeto desde Liquid, usa {{ product.metafields.custom.farm.value | json }}. Verás el objeto completo de la finca referenciada con todos sus campos. Es la forma más rápida de confirmar que el storefront access está habilitado y que los datos existen correctamente.

Checklist senior

Checklist senior

  • Puedes explicar la diferencia entre metafields (extienden entidades existentes) y metaobjetos (crean entidades nuevas)
  • Sabes que los metaobjetos requieren activar “Storefront access” explícitamente para ser accesibles desde Liquid y el Storefront API
  • Puedes escribir la mutation Admin API para crear una definición de tipo con campos tipados
  • Entiendes cómo se establece la relación Product → Finca usando un metafield de tipo metaobject_reference
  • Sabes la sintaxis de Liquid para acceder a los campos de un metaobjeto (finca.nombre_campo.value) y por qué no se accede directamente
  • Puedes listar todas las entradas de un tipo con shop.metaobjects.tipo.values
  • Sabes que borrar una entrada de metaobjeto deja referencias huérfanas y siempre verificas != blank
  • Puedes asignar un template a un tipo de metaobjeto para darle una URL pública y SEO

Quiz

Quiz · ¿Lo tenés claro?

5 preguntas · respondé para marcar la sección como completada.

  1. 1. ¿Cuándo usas metaobjetos en lugar de metafields?

  2. 2. Creaste un tipo de metaobjeto `finca` con `generate_types`, lo poblaste desde el Admin, pero no aparece en Liquid ni en el Storefront API. ¿Qué falta?

  3. 3. Un Product debe referenciar a una Finca. ¿Cuál es el modelado correcto?

  4. 4. En Liquid tienes `finca = product.metafields.custom.finca.value`. ¿Cómo accedes al campo 'altitud' del metaobjeto?

  5. 5. Borraste la entrada 'La Esperanza' del metaobjeto finca pero 5 productos la referenciaban. ¿Qué pasa?

Siguiente

Con el modelo de datos completo — productos con metafields, fincas como metaobjetos, relaciones entre ellos — el siguiente paso es aprender a consumir esos datos desde fuera del theme. El Storefront GraphQL API es el gateway que permite a cualquier cliente externo (una app mobile, un storefront headless, un script de Node) leer y operar sobre la tienda de Shopify.