07 · Metaobjects
Contenido desacoplado del catálogo — la content platform.
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, nombresmulti_line_text_field— texto largo sin HTMLrich_text_field— texto enriquecido con formatonumber_integer— números enteros (altitud en metros, año de fundación)file_reference— imágenes y otros archivos de la biblioteca de mediosmetaobject_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 -%}
· {{ 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
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.valuesrequiere 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
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 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):
name—single_line_text_field— nombre de la fincaregion—single_line_text_field— región y país (ej: “Cundinamarca, Colombia”)altitude_masl—number_integer— altitud en metros sobre el nivel del margrower—single_line_text_field— nombre del caficultor responsablephoto—file_reference— imagen de la fincadescription—rich_text_field— descripción editorial de la finca
Tipo region (región cafetera):
name—single_line_text_field— nombre de la regióncountry—single_line_text_field— paísmap_image—file_reference— mapa o imagen representativatasting_profile—multi_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 -%}
· {{ 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
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.
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.
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.
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
- 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 · ¿Lo tenés claro?
-
1. ¿Cuándo usas metaobjetos en lugar de metafields?
Metafield = extender entidad existente con columnas. Metaobject = crear una entidad nueva con identidad propia y referenciable desde múltiples productos. La Finca de Brew Atlas la referencian varios productos, por eso es metaobject y no metafield del product.
-
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?
Los metaobjetos nacen privados. Para exponerlos al Storefront API y a Liquid hay que activar explícitamente 'Storefront access' en la definición del tipo. Sin ese flag, existen pero son invisibles fuera del Admin.
-
3. Un Product debe referenciar a una Finca. ¿Cuál es el modelado correcto?
`metaobject_reference` (o `list.metaobject_reference` para múltiples) es el tipo oficial para relaciones. El Admin renderiza un selector de metaobjetos del tipo declarado, y la API devuelve el objeto completo con `.reference` o `.references`.
-
4. En Liquid tienes `finca = product.metafields.custom.finca.value`. ¿Cómo accedes al campo 'altitud' del metaobjeto?
Cada campo del metaobjeto devuelve un wrapper con `.value` (valor tipado), `.type`, etc. No hay shortcut a `finca.altitud` directo — hay que ir por `.value`, igual que con metafields.
-
5. Borraste la entrada 'La Esperanza' del metaobjeto finca pero 5 productos la referenciaban. ¿Qué pasa?
Shopify no hace cascade delete ni bloqueo: el metaobjeto desaparece y las referencias se convierten en blank. Por eso la guardia `{% if finca != blank %}` es obligatoria en todo Liquid que acceda a `metaobject_reference`.
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.