# Keeping in sync [#keeping-in-sync] A generated client is a snapshot of your server's client contract. When the contract changes, the snapshot goes stale; regenerating it is one command, and the server tells you when you forgot. ## What makes a client stale [#what-makes-a-client-stale] The client is bound to the hash of the whole contract the deployment advertises, not only your schema. Regenerate after any of: * a schema change (a new model, field or enum value), * a change to exposure (at the root or under `client`), field validators, limits or operations in your configuration, * transactions switched on or off for the client layer, * a typed [JSON field](/framework/docs/json-fields) declared or changed (its shape is part of the contract), * moving to a new Aventara release that changes what a contract states: the release notes say so ([Upgrading](/framework/docs/upgrading)). Changes that stay on the server (a pipeline guard, the application layer's configuration, a bug fix) do not change the contract. ## Regenerate [#regenerate] With the server running: ```bash npm run avclient:generate # same as: npx avclient generate [--yes] ``` The generator fetches `/_contract`, verifies it, and writes `AvClient.ts` and `generated/` into `generateAt`. A failed run leaves the previous output exactly as it was. Commit the result, never edit it. ## Up to date [#up-to-date] A rerun asks the server conditionally (`If-None-Match` with the hash of the output it finds). When nothing changed, the server answers `304 Not Modified`, and nothing is written: ```text avclient: up to date: /path/to/src/api already holds this deployment's client; nothing was written. ``` You can see the mechanism with curl: the contract carries its hash as an `ETag`. ```bash curl -si http://localhost:3103/api/_contract | head -5 # ETag: "sha256:f9a5230f..." curl -si http://localhost:3103/api/_contract -H 'If-None-Match: "sha256:f9a5230f..."' | head -1 # HTTP/1.1 304 Not Modified ``` If the contract is unchanged but the generator or the entrypoint changed, the output is rewritten and the message says so. ## What a stale client does [#what-a-stale-client-does] Every request carries the contract hash and protocol version. If they do not match the server, the server answers `409 A2005` and the client throws `AvContractMismatchError`: ```ts try { await avClient.User.find.many({}); } catch (e) { // e instanceof AvContractMismatchError, e.code === "A2005" // e.cause.message: "Generated client contract does not match the server. Regenerate the client." } ``` It happens before anything runs, so nothing is half-applied. This includes calling an operation the server no longer advertises. A response that is not an Aventara envelope at all (a host's own 404, a proxy error) is an `AvTransportError` instead: nothing in it says the client is stale. Two deployments of one schema can advertise different contracts (different exemptions, different limits). A client generated against one is refused by the other, so generate against the deployment the client will call. In practice: generate against the environment you deploy to, or make sure every environment runs the same configuration. ## Generating in CI [#generating-in-ci] Generate against the deployment the build will call and fail the build if the committed output differs: ```bash npm run avclient:generate -- --yes git diff --exit-code src/api ``` `--yes` also answers the prompts, which matter because in CI nobody can: where there is no terminal and the generator would have to ask (for example, `AvClient.ts` or `generated/` contains something it did not produce), the run is refused and nothing is touched. To use one config for local and CI, read the entrypoint from the environment (`env("AVENTARA_API_URL")`, see [Client setup](/framework/docs/client-setup#environment-variable-or-literal)) and set the variable in the CI job. ## Content the generator did not produce [#content-the-generator-did-not-produce] If `AvClient.ts` or `generated/` holds something the generator did not write, it is listed and you are asked before it is overwritten. `--yes` answers yes. Anything else in `generateAt` is never touched. ## A workflow that works [#a-workflow-that-works] 1. Change the schema or the configuration on the server; `prisma db push` / `npm run aventara:prepare`; restart. 2. `npm run avclient:generate` in the frontend. 3. Fix the type errors the compiler now reports: they are exactly what changed in the contract. 4. Commit the server change and the regenerated client together. ## See also [#see-also] * [Client setup](/framework/docs/client-setup) * [Client errors](/framework/docs/client-errors#avcontractmismatcherror) * [Contract hash](/framework/docs/contract-hash) * [CLI reference](/framework/docs/cli-reference) * Guides: [Deploying](/framework/docs/deploying), [Upgrading](/framework/docs/upgrading)