Update the Gezy API v4 client
This guide explains how to update the typed API v4 client when the Gezy backend API changes. The client is generated from the OpenAPI specification using Orval, ensuring type safety between Front-Commerce and Gezy.
Overview
Front-Commerce uses Orval to generate a fully typed
TypeScript client from the Gezy API v4 OpenAPI specification. Orval produces one
factory per OpenAPI tag (e.g. getMarketplace, getOauth), each accepting an
AxiosInstance and returning typed functions for that domain.
Gezy maintains two backend lines that evolve independently — v11 (e-commerce) and v12 (marketplace) — so the client is generated once per line, from that line's own committed specification.
This ensures that:
- TypeScript types always reflect the real API contract
- Breaking changes are caught at compile time
- The client is request-scoped (no shared singleton)
The OpenAPI specifications are committed in the repository so that CI builds do not depend on a running Gezy backend.
Prerequisites
- A running Gezy backend instance with API v4 enabled
- Node.js and pnpm installed
Updating the client
Step 1: Fetch the OpenAPI specification of your line
A deployment talks to one backend line, so it only ever fetches that line's specification:
# e-commerce line (the default)
FRONT_COMMERCE_GEZY_ENDPOINT=https://your-v11-instance.lundimatin.biz \
pnpm --filter @front-commerce/gezy fetch-openapi-v4 v11
# marketplace line
FRONT_COMMERCE_GEZY_ENDPOINT=https://your-v12-instance.lundimatin.biz \
pnpm --filter @front-commerce/gezy fetch-openapi-v4 v12
Pick the first or the second block — one run is enough. Each run saves the spec
to packages/gezy/generated/openapi-v4.<flavor>.json. The backend line is the
argument of the command and is required: fetching without naming the line is
what used to mix two backends into a single specification. The endpoint defaults
to http://localhost:8080.
The other line's spec is already committed and does not need refreshing: it belongs to the installations that run that line. Only a maintainer of the connector itself fetches both, to keep the mirror of both backends current.
Step 2: Regenerate the typed clients
pnpm --filter @front-commerce/gezy generate-api-v4
pnpm prettier --write "packages/gezy/generated/**"
Orval writes with its own quote style and JSDoc indentation, while the committed
outputs are prettier-formatted — the Lint job runs
prettier --check ./packages and .prettierignore excludes none of
packages/gezy/generated/. Format right after generating: without it the commit
fails Lint, and the line-isolation check below reports a diff on the untouched
line even though the isolation held.
This runs Orval with the configuration in packages/gezy/orval.config.ts — one
target per line — and outputs one module per tag in
packages/gezy/generated/api-v4/v11/ and packages/gezy/generated/api-v4/v12/.
Only the tags listed in each target's input.filters.tags are generated; those
filters describe the storefront surface, so a line's client stops at what
the storefront calls even when the backend exposes more.
Both targets run and both outputs must exist at compile time — di.ts wires
both lines — but the line you did not fetch is regenerated byte-identical: each
target reads its own committed spec and only clears its own output directory.
Verify it with git diff: the untouched line's directory must show no change.
Step 3: Review the changes
git diff packages/gezy/generated/
Review the diff to understand what changed in the API. Look for:
- New or removed endpoints
- Changed request parameters
- Changed response shapes
- Fields that became required or optional
Step 4: Fix compilation errors
If the API changed in a breaking way, TypeScript will flag errors in the code that uses the generated types. Fix these before committing.
Step 5: Commit both files
Always commit the OpenAPI spec and the generated client together, so the repository stays consistent:
git add packages/gezy/generated/
git commit -m "chore(gezy): update API v4 OpenAPI spec and generated client"
Using the API v4 client
The client is accessible through the Gezy DI as publicRestAPIv4 and
privateRestAPIv4, organized by domain and built from the line the deployment
targets. The clients only one line serves are reached through the
requireCatalogAPI() / requireMarketplaceAPI() accessors, which fail with an
actionable message on the other line:
// In a runtime.ts contextEnhancer
const marketplace = services.DI.get("gezy").requireMarketplaceAPI();
// Typed call: params and response are inferred from the OpenAPI spec
const response = await marketplace.getMerchants({ query: "tech", limit: 10 });
For tests, create a client bound to the mock server:
import { getMarketplace } from "@front-commerce/gezy/api-v4/v12/marketplace";
const api = getMarketplace(axios.create({ baseURL: mockServer.url }));
new MerchantLoader(api, makeDataLoaderStub);
Imports per backend line
Generated clients and types are exposed per line — there is no shared entry
point. The column below tells which backend line serves each client; a consumer
still imports the line it is built for — @front-commerce/gezy-marketplace
stays on v12 and @front-commerce/gezy internals on v11, enforced by the
flavor ESLint guard:
| Import path | Served by |
|---|---|
@front-commerce/gezy/api-v4/v11/types | Both lines (schema types) |
@front-commerce/gezy/api-v4/v11/cms | Both lines |
@front-commerce/gezy/api-v4/v12/types | Both lines (schema types) |
@front-commerce/gezy/api-v4/v12/cms | Both lines |
@front-commerce/gezy/api-v4/v12/marketplace | marketplace line only |
Only these entry points are exposed. The other generated modules (the catalogue,
OAuth and System factories of both lines) are consumed by @front-commerce/gezy
itself and are not part of the package's public surface: a project that needs
one of them should ask for it to be exposed rather than deep-importing the
generated files, which is not a stable path.
Imports of @front-commerce/gezy/api-v4/types, …/cms and …/marketplace —
the entry points of the former single client — no longer resolve: point them at
api-v4/v11/* or api-v4/v12/* (table in the migration guide).
Adding a new API tag
When the storefront gains a new domain — add a tag only if the storefront
calls it. A backend domain the storefront never calls (the merchant back
office's Marketplace - * family, for instance) stays out of the filter and out
of the client, even though the line serves it:
- Add the tag name to
input.filters.tagsof its line inorval.config.ts - Run
pnpm --filter @front-commerce/gezy generate-api-v4 - Import the new factory in
di.tsand add it topublicRestAPIv4orprivateRestAPIv4, wired to that line - Add the corresponding export in
package.json
Manual grafts in a specification
When shipped code consumes an endpoint its line has not merged yet, the missing
block is copied from the other specification (never written by hand) and marked
with x-front-commerce-manual-graft, stating why it is provisional. A test
suite lists every graft it tolerates and fails as soon as the backend serves the
real thing: drop the grafted block and regenerate at that point.
Architecture
Gezy backend v11 (e-commerce) Gezy backend v12 (marketplace)
└─ GET /api/v4/openapi.json └─ GET /api/v4/openapi.json
└─ generated/openapi-v4.v11.json └─ generated/openapi-v4.v12.json
└─ Orval target gezyApiV4V11 └─ Orval target gezyApiV4V12
└─ generated/api-v4/v11/ └─ generated/api-v4/v12/
├─ index.schemas.ts ├─ index.schemas.ts
├─ catalog/catalog.ts ├─ marketplace/marketplace.ts
├─ cms/cms.ts ├─ cms/cms.ts
├─ oauth/oauth.ts ├─ oauth/oauth.ts
└─ system/system.ts └─ system/system.ts
generated/api-v4/v11/catalog/catalog.ts
└─ getCatalog(axiosInstance) → { getBrands }
generated/api-v4/v12/marketplace/marketplace.ts
└─ getMarketplace(axiosInstance) → { getMerchants }
generated/api-v4/v11/oauth/oauth.ts
└─ getOauth(axiosInstance) → { postToken, getMe }
DI (di.ts) — built from the `flavor` option of `gezy({ storesConfig, flavor })`
└─ publicRestAPIv4 / privateRestAPIv4
├─ marketplace (v12 only): getMarketplace(axiosInstance)
├─ catalog (v11 only): getCatalog(axiosInstance)
├─ oauth, cms, system: factories of the configured line
└─ axiosInstance: baseURL + X-LMB-Ecommerce-UUID header
Troubleshooting
fetch-openapi-v4 fails immediately with a flavor error
The command takes the backend line as its argument: fetch-openapi-v4 v11 or
fetch-openapi-v4 v12. A fetch without it is what used to mix two backends into
one file.
fetch-openapi-v4 fails with a connection error
The Gezy backend is not running or not reachable. Verify that
FRONT_COMMERCE_GEZY_ENDPOINT (or the default http://localhost:8080) is
correct and the backend is started.
Generated types do not match the API response
The committed OpenAPI spec may be outdated. Re-run fetch-openapi-v4 (for the
right line) and generate-api-v4 to synchronize.
Orval generates hash-based function names
The Orval configuration includes a custom operationName function that strips
the tag prefix from names (e.g. getMerchants instead of
getMarketplaceMerchants). If a new endpoint produces an odd name, adjust the
function in packages/gezy/orval.config.ts.