Skip to content

Integration Guides

Working client code for the Bagisto API in the language you are building in. Both transports are covered — pick one, wire up the client below, then follow the per-language guide for the rest.

Which transport

Both surfaces expose the same data and the same capabilities. The choice is about how you fetch it, not what you can reach.

RESTGraphQL
Endpoint/api/shop/* — one path per resourcePOST /api/graphql — one endpoint
Fetching a screenOne call per resource; a product page may take severalOne call selects exactly the fields the screen needs
Filtering & sortingQuery string — ?category_id=&price=&sort=A JSON filter: string plus sortKey/reverse
Pagingpage + per_page, totals in response headersCursor — first + after, with pageInfo
Failure signalThe HTTP status codeHTTP 200 with a top-level errors array
Good fit forSimple screens, server scripts, quick integrationsRich screens that would otherwise need several round trips

Nothing stops you mixing them in one app — the credentials are identical.

What every request needs

SurfaceRequired headers
Shop (public)X-STOREFRONT-KEY: <key>
Shop (customer or guest cart)X-STOREFRONT-KEY plus Authorization: Bearer <token>
AdminAuthorization: Bearer <id>|<token> only — no storefront key

Two things to get right on the shop side: the Bearer is the token from login, not the apiToken that comes back alongside it, and a guest can act without an account by sending a cart token as the Bearer. Full model on the Authentication page.

The shape of a client

Every example on the two per-language pages is built the same way: one function that attaches the headers and unwraps the response, then thin calls on top of it. In JavaScript, that is:

javascript
const BASE_URL = 'https://your-domain.com/api/shop';
const STOREFRONT_KEY = 'pk_storefront_xxxxxxxxxxxxx';

async function api(path, { method = 'GET', body, token } = {}) {
  const headers = { 'X-STOREFRONT-KEY': STOREFRONT_KEY };
  if (body) headers['Content-Type'] = 'application/json';
  if (token) headers['Authorization'] = `Bearer ${token}`;

  const res = await fetch(`${BASE_URL}${path}`, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });

  // REST signals failure with the status code.
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const products = await api('/products?per_page=20&sort=name-asc');
javascript
const API_URL = 'https://your-domain.com/api/graphql';
const STOREFRONT_KEY = 'pk_storefront_xxxxxxxxxxxxx';

async function gql(query, variables = {}, token = null) {
  const headers = {
    'Content-Type': 'application/json',
    'X-STOREFRONT-KEY': STOREFRONT_KEY,
  };
  if (token) headers['Authorization'] = `Bearer ${token}`;

  const res = await fetch(API_URL, {
    method: 'POST',
    headers,
    body: JSON.stringify({ query, variables }),
  });

  // GraphQL returns 200 even on failure — the reason is in errors[].
  const json = await res.json();
  if (json.errors) throw new Error(json.errors[0].message);
  return json.data;
}

const { products } = await gql(`
  query GetProducts($first: Int!) {
    products(first: $first) {
      edges { node { id _id name sku } }
      pageInfo { hasNextPage endCursor }
    }
  }
`, { first: 20 });

The difference that catches people out is the error check. A failed GraphQL request still returns HTTP 200, so a client that only inspects the status code will treat an error as success and hand null fields to the UI.

Your language

Each guide carries a client, a login call, and an authenticated call, ready to paste.

LanguageRESTGraphQL
JavaScript / Node.jsFetch, Axios, Next.jsFetch, Apollo, graphql-request, Next.js
Pythonrequests, Djangorequests, gql
PHPcURL, Laravel HTTP clientcURL, Laravel HTTP client
RubyNet::HTTPNet::HTTP
Gonet/httpnet/http
JavaOkHttpOkHttp
cURLShell examplesShell examples

All examples target the Shop API. For a back-office integration the calls are the same shape with a different credential — see Admin Authentication.

Before you ship

  • Handle 401 by getting a new credential. There is no refresh token anywhere in the API: re-login for a customer token, regenerate an admin token, rotate a storefront key.
  • Treat the storefront key as public. It ships in browser and mobile bundles, and it permits the storefront's open writes (contact form, newsletter, registration, cart creation), so protect those forms on your side.
  • Page properly. REST caps per_page at 50 and reports totals in headers; GraphQL pages with cursors until pageInfo.hasNextPage is false. See Pagination.
  • Back off on 429. The storefront key's limit is applied per hour, so a retry loop can wait a long time — see Rate Limiting.
  • Store _id, pass id back. GraphQL nodes carry both; only the numeric one is stable for your database and for REST URLs. See Identifiers.

Released under the MIT License.