Ir al contenido principal

Sección 11

11 · Admin Apps · Polaris · App Bridge

Apps embebidas en el admin de Shopify.

En una frase

Las apps embebidas en el Admin de Shopify son aplicaciones web que se renderizan dentro de un iframe en el panel de administración del comerciante. Usan Polaris como sistema de diseño obligatorio, App Bridge para interactuar con el shell del Admin (modales, barra de navegación, toasts), y session tokens JWT para autenticar las llamadas al servidor sin redirigir al comerciante fuera de la app. El scaffold oficial es el template de React Router v7 del CLI.

Nota

Esta sección es conceptual + opcional a nivel scaffold. El plan de la app Admin de Brew Atlas (rutas, loaders, acciones, componentes de Polaris a usar) vive en ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/PLAN.md dentro del repo. El scaffold real del proyecto (npm init @shopify/app@latest) ya lo ejecutaste en la sección 02 si vienes del flujo completo. Si solo quieres leer esta sección para entender el stack y el modelo mental, no tienes que ejecutar ningún comando. Si quieres reproducir el scaffold desde cero en otro directorio, está marcado abajo como Referencia.

Qué vas a ejecutar en esta sección

  1. npm init @shopify/app@latest — Scaffold de la app desde cero (referencia — ya lo ejecutaste en seccion 02)
  2. shopify app dev — Dev server con tunnel automatico — desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/

Buscá los bloques con badge ✓ Ejecutar en el contenido. Los bloques con 📖 Referencia son ilustrativos — no los ejecutes.

Esta sección no introduce comandos obligatorios nuevos — el scaffold de la app ya se generó en la sección 02. El plan de implementación vive en ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/PLAN.md. Los comandos de arriba son referencia para scaffoldear en otro directorio.

Qué es y por qué existe

Cuando un comerciante instala una app de Shopify, la espera es que esa app aparezca integrada en su panel de administración — no que lo redirija a un sitio externo con otra interfaz. Las apps embebidas cumplen esa expectativa: se renderizan dentro del Admin de Shopify en una región controlada por Shopify, con la barra de navegación y el aspecto visual del Admin como contexto.

Técnicamente, la app embebida es una aplicación web estándar (en este caso, un servidor Node.js con React Router v7) que Shopify carga dentro de un <iframe>. El comerciante navega a https://admin.shopify.com/store/tu-tienda/apps/tu-app y ve tu interfaz dentro del shell del Admin. La barra lateral de Shopify, la barra superior, el usuario autenticado — todo eso es del Admin. Tu app ocupa el contenido central.

La razón por la que Shopify prefiere este modelo sobre “apps standalone” es la confianza del comerciante: el comerciante sabe que está dentro de Shopify. No hay redirecciones a dominios desconocidos para operaciones críticas. Shopify también puede aplicar su Content Security Policy al iframe para prevenir ciertos ataques.

El stack en 2026

El template oficial del CLI genera un proyecto con esta pila:

  • Node.js backend con React Router v7 como framework full-stack
  • Polaris 13.x como sistema de diseño — el mismo que usa el Admin de Shopify
  • App Bridge (web components) para acciones nativas del Admin: modales, toasts, resource pickers, navegación
  • Session token authentication: el Admin inyecta un JWT en la app; tu servidor lo verifica e intercambia por un access token para llamar al Admin GraphQL API
  • Prisma (incluido en el template por defecto) para persistencia local

Generar el scaffold

El CLI de Shopify genera todo el boilerplate con un comando. Desde ~/proyectos/shopify/brew-atlas/ (o cualquier directorio donde quieras crear el scaffold):

Referencia Scaffold - solo si aun no lo corriste en seccion 02

npm init @shopify/app@latest

El CLI pregunta el template — elegí react-router (o remix, es el mismo). Nombre: brew-atlas-app. El flag --template react-router es equivalente a --template remix — desde finales de 2025, el template de Remix se renombró a React Router v7. El alias remix sigue funcionando y apunta al mismo template.

Una vez generado, el servidor de desarrollo se levanta desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/:

Referencia Dev server (referencia - lo corriste en seccion 02)

shopify app dev

Estructura del proyecto generado

El template produce esta estructura (simplificada a los archivos relevantes):

// ~/proyectos/shopify/brew-atlas/brew-atlas-app/ (directorio raiz del app generado por CLI)
brew-atlas-app/
├── shopify.app.toml          ← config del app, scopes, webhooks
├── shopify.server.ts         ← integración shopify-app-remix
├── prisma/
│   └── schema.prisma         ← esquema SQLite por defecto
├── app/
│   ├── shopify.config.ts     ← config de la sesión y OAuth
│   ├── db.server.ts          ← cliente Prisma
│   └── routes/
│       ├── app.tsx           ← layout raíz con providers de Polaris y App Bridge
│       ├── app._index.tsx    ← ruta principal del dashboard
│       └── webhooks.tsx      ← handler de webhooks
└── extensions/               ← aquí van las extensiones del app

La separación entre shopify.server.ts y app/shopify.config.ts es importante: shopify.server.ts es la instancia del SDK de Shopify para el servidor, configurada con las credenciales de la app. app/shopify.config.ts consume esa instancia y la exporta con los helpers de autenticación que usan tus loaders y actions.

shopify.app.toml — configuración central

El archivo shopify.app.toml es donde declaras los scopes, los webhooks, las URLs del app, y las extensiones que va a incluir el deploy:

# ~/proyectos/shopify/brew-atlas/brew-atlas-app/shopify.app.toml
name = "Brew Atlas"
client_id = "TU_CLIENT_ID"
application_url = "https://tu-servidor.com"
embedded = true
 
[access_scopes]
scopes = "read_products,write_products,read_orders,read_customers,write_customers"
 
[auth]
redirect_urls = [
  "https://tu-servidor.com/auth/callback",
  "https://tu-servidor.com/auth/shopify/callback"
]
 
[[webhooks.subscriptions]]
topics = ["app/uninstalled"]
uri = "/webhooks"
 
[[webhooks.subscriptions]]
topics = ["orders/create"]
uri = "/webhooks"

La clave embedded = true le indica a Shopify que esta app debe renderizarse dentro del Admin. Sin esa declaración, Shopify la trata como una app standalone y no inyecta el runtime de App Bridge.

Session tokens: autenticación sin redirecciones

El mecanismo de autenticación de las apps embebidas merece explicación detallada porque es diferente al flujo OAuth tradicional.

Cuando el comerciante navega a tu app dentro del Admin, App Bridge inyecta automáticamente un session token — un JWT firmado por Shopify — en cada request que hace tu app al servidor. Ese JWT contiene el identificador del shop y expira en un minuto (por diseño, para forzar rotación frecuente).

Tu servidor recibe ese JWT, lo verifica contra el client secret de tu app, y lo intercambia por el access token de larga duración del comerciante (stored en tu DB) mediante el proceso llamado Token Exchange. El acceso token es el que realmente usas para llamar al Admin GraphQL API.

El template del CLI encapsula todo esto en el helper authenticate.admin(request).

Editá ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app._index.tsx:

// ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app._index.tsx
import { json } from '@remix-run/node';
import { authenticate } from '~/shopify.server';
 
export async function loader({ request }: { request: Request }) {
  const { admin, session } = await authenticate.admin(request);
 
  // 'admin' es un cliente de Admin API ya autenticado con el token del comerciante
  const response = await admin.graphql(`#graphql
    query {
      shop {
        name
        myshopifyDomain
        plan { displayName }
      }
    }
  `);
 
  const { data } = await response.json();
  return json({ shop: data.shop });
}

Si el JWT es inválido o expiró, authenticate.admin redirige automáticamente al flujo de re-autenticación. No tienes que manejar ese caso manualmente.

Polaris: el sistema de diseño obligatorio

Polaris 13.x es el sistema de diseño de Shopify. Para apps que quieran la certificación “Built for Shopify”, usar Polaris en todas las superficies del Admin no es opcional — es un requisito. Incluso sin la certificación, usarlo reduce la fricción para el comerciante: tu app tiene el mismo lenguaje visual que el resto del Admin.

Los componentes más usados en apps embebidas.

Ejemplo de pantalla principal en ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app._index.tsx:

// ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app._index.tsx
import {
  Page,
  Layout,
  Card,
  DataTable,
  Badge,
  Button,
  TextField,
  Select,
} from '@shopify/polaris';
 
export default function Index() {
  const { shop } = useLoaderData<typeof loader>();
 
  return (
    <Page
      title="Brew Atlas Admin"
      subtitle={`Tienda: ${shop.name}`}
      primaryAction={<Button variant="primary">Nueva suscripción</Button>}
    >
      <Layout>
        <Layout.Section>
          <Card>
            <DataTable
              columnContentTypes={['text', 'text', 'numeric', 'text']}
              headings={['Tier', 'Frecuencia', 'Precio', 'Estado']}
              rows={[
                ['Monthly 1 bag', 'Mensual', '$15.00', <Badge tone="success">Activo</Badge>],
                ['Monthly 2 bags', 'Mensual', '$28.00', <Badge tone="success">Activo</Badge>],
                ['Quarterly sampler', 'Trimestral', '$42.00', <Badge>Borrador</Badge>],
              ]}
            />
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}

La jerarquía de Polaris que usarás en el 95% de las pantallas: PageLayoutLayout.SectionCard. Page maneja el título, las acciones primarias y secundarias, y el breadcrumb. Card es el contenedor de contenido. DataTable para tablas. Form, TextField, Select para formularios.

App Bridge: acciones del Admin

App Bridge 3.x usa web components — custom elements del navegador cargados desde la CDN de Shopify. No necesitas instalar ningún paquete npm: el script de App Bridge lo inyecta el template de React Router v7 automáticamente cuando la app está embebida.

Los web components de App Bridge que más usarás.

Editá el layout raíz en ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app.tsx:

// ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/routes/app.tsx (layout raiz)
// App Bridge web components — se renderizan en el cliente, fuera del SSR
export default function AppLayout() {
  return (
    <>
      {/* Barra de título del Admin con acciones */}
      <ui-title-bar title="Brew Atlas">
        <button variant="primary" onClick={() => openModal()}>
          Nueva suscripción
        </button>
      </ui-title-bar>
 
      {/* Modal nativo del Admin */}
      <ui-modal id="new-subscription-modal">
        <ui-title-bar title="Crear tier de suscripción"></ui-title-bar>
        <p>Contenido del modal aquí</p>
        <button slot="primary-action" variant="primary">
          Guardar
        </button>
        <button slot="secondary-action">Cancelar</button>
      </ui-modal>
 
      <Outlet />
    </>
  );
}

Los custom elements como <ui-title-bar>, <ui-modal>, <ui-nav-menu> se comunican con el shell del Admin directamente. No son divs disfrazados — el Admin los procesa y renderiza en sus propias regiones (la barra superior, los modales del sistema, la navegación lateral). Esto es lo que hace que la app se sienta nativa.

Para mostrar un toast (notificación temporal):

// Desde cualquier componente cliente
import { useAppBridge } from '@shopify/app-bridge-react';
// O directamente con el web component:
const shopify = window.shopify; // inyectado por el script de App Bridge
shopify.toast.show('Suscripción guardada', { duration: 3000 });

Scopes y permisos de la app

Los scopes que tu app puede ejercer se declaran en shopify.app.toml bajo [access_scopes]. El comerciante los aprueba durante la instalación — ve una pantalla que lista exactamente qué puede hacer tu app. Si pides un scope y después intentas usar otro no declarado, el Admin API rechaza la operación.

Reglas prácticas sobre scopes:

  • Pide solo los scopes que realmente usas — cada scope adicional reduce la tasa de conversión en la instalación
  • write_X incluye read_X implícitamente
  • Algunos recursos tienen scopes más granulares desde 2024 — consulta la doc de cada recurso para el scope exacto
  • Si necesitas un scope adicional después del despliegue, el comerciante tiene que re-aceptar la instalación

Puentes mentales

Desde VS Code Extensions

Una app embebida de Shopify es a Shopify Admin lo que una extensión de VS Code es al editor: el shell (VS Code / Admin de Shopify) pertenece al host. Tu código se ejecuta dentro de esa shell mediante integration points predefinidos — comandos, vistas laterales, webviews / rutas del Admin, modales. No puedes cambiar la barra de menú de VS Code, y no puedes cambiar la barra lateral del Admin. Pero dentro de tu webview / iframe, tienes control total. La restricción de usar el sistema de diseño del host (Polaris / los componentes de VS Code) no es arbitraria: es lo que hace que tu extensión se sienta nativa en lugar de un cuerpo extraño pegado encima.

Estado 2026

Lo relevante al 2026:

  • React Router v7 es el framework recomendado para apps de Shopify. El template anterior era Remix (que se convirtió en React Router v7 en 2025). Si ves tutoriales con @remix-run/node o @remix-run/react, siguen siendo válidos — son los mismos paquetes bajo un nombre diferente a partir de cierta versión.
  • Token Exchange reemplazó al flujo de OAuth de redirección para apps embebidas. No necesitas redirigir al comerciante fuera del Admin para autenticar — el session token JWT lo hace en segundo plano.
  • Polaris 13.x: los componentes LegacyCard, LegacyStack, y otros marcados como “legacy” en v12 están eliminados en v13. Si migras una app antigua, revisa el migration guide de Polaris.
  • App Bridge 4.x (web components) es el modelo actual. El wrapper de React @shopify/app-bridge-react sigue disponible como capa de compatibilidad, pero la API canónica son los web components.

Proyecto · Brew Atlas

Brew Atlas · Paso 11

En lugar de reproducir todo el scaffold (que el CLI genera en segundos), el plan del Admin App de Brew Atlas está documentado en ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/PLAN.md. Define las rutas, las operaciones de Admin API, y los componentes de Polaris que construiría una implementación completa del Subscription Tier Manager.

Para generar el scaffold real en cualquier momento, desde ~/proyectos/shopify/brew-atlas/ (o cualquier directorio padre donde quieras crear el proyecto):

Referencia Scaffold desde cero - referencia

npm init @shopify/app@latest

Una vez generado, desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/:

Referencia Dev server - referencia

shopify app dev

El PLAN.md describe exactamente qué construir después de tener el scaffold, con pseudo-código de los loaders y actions principales.

Qué vas a leer en esta sección (archivos):

  • ~/proyectos/shopify/brew-atlas/brew-atlas-app/app/PLAN.md — lectura obligatoria; define rutas, loaders y actions.
  • No hay archivos de código que modificar en el repo del tutorial para este paso.

Comandos a ejecutar (orden):

  • Ninguno obligatorio. Todo lo que aparece en esta sección marcado como Referencia ya lo corriste en sección 02 (scaffold y dev server).

Errores comunes

Cuidado

App Bridge 3.x (web components) y la versión anterior basada en React son incompatibles en la misma superficie. No mezcles @shopify/app-bridge (v3 npm) con los web components del CDN — son dos modelos distintos. El template nuevo del CLI usa exclusivamente web components. Si estás en una app legacy que usa el wrapper de React, migra cuando puedas — el soporte del modelo antiguo tiene fecha de fin.

Cuidado

Polaris no es un sistema de diseño de propósito general. Está diseñado específicamente para las interfaces de administración de Shopify: sus componentes asumen la paleta de colores, los espaciados, y el lenguaje visual del Admin. No lo uses en el storefront del comerciante, en páginas públicas, ni en tu propio sitio de marketing. Para esos contextos, esa decisión solo añade peso a la bundle y produce interfaces visualmente fuera de lugar.

Cuidado

Las apps embebidas corren dentro de un iframe con la Content Security Policy de Shopify aplicada. Eso significa que no puedes cargar scripts externos sin declararlo, no puedes usar localStorage en el iframe directamente (usa la API de App Bridge para persistencia en sesión), y ciertos ataques de clickjacking que explotarían el iframe ya están mitigados por el host. No asumas que el DOM de tu app embebida tiene acceso al DOM del Admin que la rodea — el iframe es una frontera de seguridad real.

Nota

El session token JWT que inyecta App Bridge expira en 60 segundos por diseño. Esto es intencional — obliga a la rotación frecuente y limita la ventana de exposición si el token se compromromete. El helper authenticate.admin(request) del template maneja la renovación automáticamente. Si estás construyendo sin el template, tienes que implementar el refresh del token en cada request.

Tip

Durante el desarrollo, shopify app dev (desde ~/proyectos/shopify/brew-atlas/brew-atlas-app/) imprime en la consola la URL del tunnel de Cloudflare. Cada vez que reiniciás el servidor, el CLI reutiliza el mismo tunnel si lo configuraste con --tunnel-url. Sin esa flag, genera una URL nueva — lo que rompe la configuración del app en el Partner Dashboard. Para desarrollo consistente, fijá la URL del tunnel o usá ngrok con un dominio fijo y configurá --tunnel-url en el comando.

Checklist senior

Checklist senior

  • Puedes explicar qué es un session token JWT, por qué expira en 60 segundos, y cómo authenticate.admin(request) lo intercambia por un access token de larga duración
  • Sabes la diferencia entre App Bridge web components (modelo actual) y el wrapper de React (modelo legacy)
  • Entiendes qué scopes declarar en shopify.app.toml y por qué pedir solo los necesarios mejora la tasa de instalación
  • Conoces la jerarquía de Polaris: Page → Layout → Card, y puedes construir una pantalla básica con DataTable y formularios
  • Puedes usar <ui-title-bar> y <ui-modal> de App Bridge para acciones nativas del Admin
  • Sabes que la app corre en un iframe con CSP aplicada y entiendes las limitaciones que eso implica
  • Puedes generar el scaffold con el CLI, identificar los archivos clave, y modificar el loader de una ruta para hacer una query al Admin GraphQL API
  • Entiendes por qué Polaris no debe usarse fuera del contexto de apps embebidas en el Admin

Quiz

Quiz

Quiz · ¿Lo tenés claro?

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

  1. 1. ¿Por qué el session token JWT expira en 60 segundos?

  2. 2. ¿Cuál es la jerarquía canónica de Polaris para la mayoría de las pantallas de una app embebida?

  3. 3. ¿Cuál es el modelo actual (2026) de App Bridge?

  4. 4. ¿Dónde declarás los scopes que tu app va a pedir?

  5. 5. Una app embebida intenta usar `localStorage` para persistir estado. ¿Qué puede salir mal?

Siguiente

Con la capa de UI del Admin cubierta, el siguiente bloque entra en el territorio del backend distribuido de Shopify: las Shopify Functions. Son módulos de WebAssembly que tu app despliega en la infraestructura de Shopify para extender la lógica de negocio del checkout — descuentos, validaciones, transformaciones de carrito — sin gestionar ningún servidor.