k-sync
Volver al blog

API de administración de Shopify para desarrolladores de WooCommerce (2026)

Cómo los desarrolladores de WooCommerce utilizan la API de administración de Shopify: GraphQL vs REST, mutaciones de productos, gestión de inventario, consultas de pedidos, webhooks y portabilidad de la lógica de la API REST de WooCommerce a Shopify.

·Por k-sync
7 min de lectura · 1,433 palabras

Los desarrolladores de WooCommerce que creaban integraciones personalizadas usaban la API REST de WooCommerce v3 — una interfaz REST para productos, pedidos, clientes y cupones. Shopify ofrece dos APIs de administración paralelas: GraphQL (recomendada) y REST (heredada). Esta guía conecta los patrones de la API REST de WooCommerce con sus equivalentes en la API de administración de Shopify, cubriendo las mutaciones y consultas que más utilizarás al crear migraciones, integraciones y herramientas de backend.

API REST de WooCommerce vs API de administración de Shopify

OperaciónREST de WooCommerce v3API de administración de Shopify (GraphQL)
Listar productosGET /wp-json/wc/v3/productsquery { products(first: 50) }
Crear productoPOST /wp-json/wc/v3/productsmutation productCreate
Actualizar productoPUT /wp-json/wc/v3/products/{id}mutation productUpdate
Eliminar productoDELETE /wp-json/wc/v3/products/{id}mutation productDelete
Listar pedidosGET /wp-json/wc/v3/ordersquery { orders(first: 50) }
Crear pedidoPOST /wp-json/wc/v3/ordersmutation orderCreate
Actualizar inventarioPUT /products/{id} stock_quantitymutation inventorySetQuantities
Listar clientesGET /wp-json/wc/v3/customersquery { customers(first: 50) }
Establecer metafieldsPOST /wp-json/wc/v3/products/{id} meta_datamutation metafieldsSet
WebhooksGestor de webhooks de WooCommercemutation webhookSubscriptionCreate

Autenticación

La API de administración requiere un token de acceso de app privada (no el token de acceso de la Storefront API):

const SHOP_DOMAIN = 'tutienda.myshopify.com';
const ACCESS_TOKEN = process.env.SHOPIFY_ADMIN_ACCESS_TOKEN;

const endpoint = 'https://' + SHOP_DOMAIN + '/admin/api/2024-01/graphql.json';

async function adminQuery(query, variables = {}) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Shopify-Access-Token': ACCESS_TOKEN,
    },
    body: JSON.stringify({ query, variables }),
  });
  const data = await response.json();
  if (data.errors) throw new Error(JSON.stringify(data.errors));
  return data;
}

Mutaciones de productos

Crear un producto (productCreate)

const CREATE_PRODUCT = `
  mutation productCreate($input: ProductInput!) {
    productCreate(input: $input) {
      product {
        id
        title
        handle
        variants(first: 10) {
          edges { node { id sku price } }
        }
      }
      userErrors { field message }
    }
  }
`;

const result = await adminQuery(CREATE_PRODUCT, {
  input: {
    title: "Cartera de cuero clásica",
    bodyHtml: "<p>Cuero de grano completo, 6 ranuras para tarjetas</p>",
    vendor: "Artisan Co",
    productType: "Accesorios",
    tags: ["cuero", "cartera", "accesorios"],
    status: "ACTIVE",
    variants: [{
      price: "49.99",
      sku: "CARTERA-NGR-001",
      inventoryManagement: "SHOPIFY",
      inventoryQuantities: [{
        availableQuantity: 50,
        locationId: "gid://shopify/Location/12345"
      }]
    }]
  }
});

Actualizar un producto

const UPDATE_PRODUCT = `
  mutation productUpdate($input: ProductInput!) {
    productUpdate(input: $input) {
      product { id title }
      userErrors { field message }
    }
  }
`;

await adminQuery(UPDATE_PRODUCT, {
  input: {
    id: "gid://shopify/Product/12345",
    title: "Título actualizado",
    tags: ["cuero", "cartera", "nueva-llegada"]
  }
});

Productos variables: crear con opciones y variantes

const CREATE_VARIABLE_PRODUCT = `
  mutation productCreate($input: ProductInput!) {
    productCreate(input: $input) {
      product { id title variants(first: 50) { edges { node { id title sku } } } }
      userErrors { field message }
    }
  }
`;

await adminQuery(CREATE_VARIABLE_PRODUCT, {
  input: {
    title: "Camiseta clásica",
    options: ["Color", "Talla"],
    variants: [
      { options: ["Negro", "S"], price: "29.99", sku: "CAMISETA-NGR-S", inventoryQuantities: [{ availableQuantity: 10, locationId: "gid://shopify/Location/12345" }] },
      { options: ["Negro", "M"], price: "29.99", sku: "CAMISETA-NGR-M", inventoryQuantities: [{ availableQuantity: 15, locationId: "gid://shopify/Location/12345" }] },
      { options: ["Blanco", "S"], price: "29.99", sku: "CAMISETA-BLC-S", inventoryQuantities: [{ availableQuantity: 8, locationId: "gid://shopify/Location/12345" }] },
      { options: ["Blanco", "M"], price: "29.99", sku: "CAMISETA-BLC-M", inventoryQuantities: [{ availableQuantity: 12, locationId: "gid://shopify/Location/12345" }] }
    ]
  }
});

Metafields (reemplaza meta_data de WooCommerce)

WooCommerce almacenaba datos personalizados como arrays de meta_data en los productos. Shopify usa metafields estructurados con espacio de nombres + clave + tipo:

const SET_METAFIELDS = `
  mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
    metafieldsSet(metafields: $metafields) {
      metafields { namespace key value type }
      userErrors { field message }
    }
  }
`;

await adminQuery(SET_METAFIELDS, {
  metafields: [
    {
      ownerId: "gid://shopify/Product/12345",
      namespace: "product_info",
      key: "material",
      value: "Cuero de grano completo",
      type: "single_line_text_field"
    },
    {
      ownerId: "gid://shopify/Product/12345",
      namespace: "product_info",
      key: "weight_g",
      value: "85",
      type: "number_integer"
    }
  ]
});

Gestión de inventario

WooCommerce almacena stock_quantity en el producto. Shopify separa el inventario en InventoryItem (el artículo) e InventoryLevel (cantidad por ubicación):

// Obtener primero el ID de ubicación
const LOCATIONS_QUERY = `
  query { locations(first: 10) { edges { node { id name } } } }
`;

// Establecer la cantidad de inventario
const SET_INVENTORY = `
  mutation inventorySetQuantities($input: InventorySetQuantitiesInput!) {
    inventorySetQuantities(input: $input) {
      inventoryAdjustmentGroup { id }
      userErrors { field message }
    }
  }
`;

await adminQuery(SET_INVENTORY, {
  input: {
    name: "available",
    reason: "correction",
    quantities: [{
      inventoryItemId: "gid://shopify/InventoryItem/67890",
      locationId: "gid://shopify/Location/12345",
      quantity: 25
    }]
  }
});

Consultas de pedidos

const ORDERS_QUERY = `
  query getOrders($first: Int!, $after: String) {
    orders(first: $first, after: $after, sortKey: CREATED_AT, reverse: true) {
      pageInfo { hasNextPage endCursor }
      edges {
        node {
          id
          name
          createdAt
          totalPriceSet { shopMoney { amount currencyCode } }
          displayFinancialStatus
          displayFulfillmentStatus
          customer { id email firstName lastName }
          lineItems(first: 20) {
            edges {
              node {
                id title quantity
                variant { sku price }
              }
            }
          }
        }
      }
    }
  }
`;

let allOrders = [];
let cursor = null;
let hasMore = true;

while (hasMore) {
  const result = await adminQuery(ORDERS_QUERY, { first: 50, after: cursor });
  const { orders } = result.data;
  allOrders = allOrders.concat(orders.edges.map(e => e.node));
  hasMore = orders.pageInfo.hasNextPage;
  cursor = orders.pageInfo.endCursor;
}

Webhooks (reemplaza los hooks de WooCommerce)

WooCommerce usaba hooks de acción (woocommerce_order_status_changed, woocommerce_new_order). Shopify tiene webhooks HTTP:

const CREATE_WEBHOOK = `
  mutation webhookSubscriptionCreate($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
    webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) {
      webhookSubscription { id callbackUrl topic }
      userErrors { field message }
    }
  }
`;

// Suscribirse a nuevos pedidos
await adminQuery(CREATE_WEBHOOK, {
  topic: "ORDERS_CREATE",
  webhookSubscription: {
    callbackUrl: "https://tuapp.com/webhooks/shopify/orders",
    format: "JSON"
  }
});

// Temas disponibles: PRODUCTS_CREATE, PRODUCTS_UPDATE, ORDERS_PAID,
// ORDERS_FULFILLED, CUSTOMERS_CREATE, INVENTORY_LEVELS_UPDATE, etc.

Límites de velocidad

APITipo de límiteLímiteTasa de restauración
API de administración GraphQLThrottling basado en coste1000 puntos/cubo50 puntos/segundo
API de administración RESTCubo con fugas40 solicitudes2 solicitudes/segundo
Storefront APIThrottling basado en coste1000 puntos/cubo50 puntos/segundo

La respuesta de GraphQL incluye información de coste en las extensiones:

// Comprobar el coste en la respuesta
const result = await adminQuery(PRODUCTS_QUERY, { first: 50 });
const { actualQueryCost, throttleStatus } = result.extensions.cost;
// throttleStatus: { maximumAvailable, currentlyAvailable, restoreRate }
// Si currentlyAvailable es bajo, añadir un retardo: await new Promise(r => setTimeout(r, 1000))

Operaciones masivas (migraciones de catálogos grandes)

Para migrar catálogos con más de 1.000 productos, usar las Bulk Operations de Shopify — operaciones asíncronas que devuelven un archivo JSONL:

// 1. Iniciar la operación masiva
const BULK_QUERY = `
  mutation {
    bulkOperationRunQuery(
      query: """
      {
        products {
          edges {
            node {
              id title handle
              variants(first: 100) { edges { node { id sku price inventoryQuantity } } }
            }
          }
        }
      }
      """
    ) {
      bulkOperation { id status }
      userErrors { field message }
    }
  }
`;

// 2. Sondear hasta completar
const POLL_BULK = `
  query {
    currentBulkOperation {
      id status errorCode
      createdAt completedAt
      objectCount url
    }
  }
`;

// 3. Cuando status === "COMPLETED", descargar el JSONL desde url
// Cada línea es un nodo de producto como JSON

Lista de verificación para la migración del desarrollador

El error más habitual que cometen los desarrolladores de WooCommerce con la API de administración de Shopify es ignorar userErrors en las respuestas de mutación. A diferencia de la API REST de WooCommerce, que devuelve códigos de estado HTTP 4xx para los fallos de validación, las mutaciones GraphQL de Shopify siempre devuelven HTTP 200 — los errores de validación están en el array userErrors del resultado de la mutación. Un producto que no se pudo crear sigue devolviendo 200 con un producto vacío y userErrors rellenos. Comprobar siempre userErrors en cada mutación o perderás productos silenciosamente durante la migración.

Migra tu tienda con k-sync

Conecta tu tienda WooCommerce, valida tus productos y publícalos en Shopify en minutos. Gratis hasta 50 productos.

Empezar gratis

Lectura relacionada

Ver todas las guías de migración