Skip to main content
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.

Mutation methods work with form()

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:
StatusError class
400BadRequestError
401UnauthorizedError
403ForbiddenError
404NotFoundError
409ConflictError
422UnprocessableEntityError
429RateLimitError
500InternalServerError
503ServiceUnavailableError

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: