The Vertz codegen produces a fully typed SDK from your entity and service definitions. Every method returns a Result — no thrown exceptions for HTTP errors. SDK methods plug directly into query() and form().
Creating the client
The codegen generates createClient() based on your entity definitions. Each entity becomes a property on the client with typed CRUD methods.
Entity methods
Read methods return QueryDescriptor
Calling .list() or .get() returns a QueryDescriptor — not a raw Promise. This descriptor carries the fetch function, a deterministic cache key, and entity metadata. Pass it directly to query():
Two components calling api.tasks.list({ status: 'todo' }) share the same cache entry — the cache key is derived from the HTTP method, path, and sorted query parameters.
Pass a mutation method (.create, .update) to form(). The codegen attaches the entity’s validation schema to the method metadata, so form() picks it up automatically:
Result type
Every SDK method returns Result<T, FetchError> — a discriminated union, never a thrown exception.
Checking results
Error status codes
When result.ok is false, result.error has a status property matching the HTTP response:
| Status | Error class |
|---|
| 400 | BadRequestError |
| 401 | UnauthorizedError |
| 403 | ForbiddenError |
| 404 | NotFoundError |
| 409 | ConflictError |
| 422 | UnprocessableEntityError |
| 429 | RateLimitError |
| 500 | InternalServerError |
| 503 | ServiceUnavailableError |
Handling errors in SDK calls
SDK methods never throw for HTTP errors. Always check result.ok before accessing result.data.
If you await a QueryDescriptor directly (outside of query()), you get a Result back — not
the data directly.
QueryDescriptor vs await
A QueryDescriptor is PromiseLike — you can await it directly for one-off calls. But for reactive UI, always use query():
List responses
List endpoints return ListResponse<T>:
Invalidation
After a mutation, invalidate related queries to trigger a refetch: