Aventara has four ideas: contracts and layers, Resources and operations, results and typing, and the contract hash.
schema.prisma + aventara.config.ts
│ compile
▼
core ─► application client ─► GET /api/_contract ─► avclient generate ─► typed client
(server only) (the only layer served)Contracts and layers
A contract is passive data: which Resources exist, which fields can be read, filtered, ordered or written, and which operations are available. It holds no code, so it can be serialized, hashed and versioned. Aventara compiles four from the same schema:
| Layer | Used for | Served over HTTP |
|---|---|---|
| Core | The baseline everything else starts from. | No |
| Application | Your trusted server-side code (framework.application.*). | No |
| Client | Remote callers: the HTTP protocol and the generated frontend client. | Yes, at GET <api>/_contract |
| Agent | AI agents, through an MCP server (the agent layer). | No: the MCP server builds its tools from it |
The root of your configuration is the core layer; application and client are overlays on it that can hide more for their own callers, never less. What each layer exposes is set with exposed and exemptions (Exposure). A field hidden from the client layer is absent from the generated client's types. Details: Contract layers.
Resources and operations
A Resource is one Prisma model, keyed by its name (User, Post). Every Resource offers operations from five families, each with variants:
| Family | Variants |
|---|---|
find | first, unique, many, count |
create | one, many, count |
update | first, unique, many, count |
delete | first, unique, count (no many with the Prisma 7 adapter) |
upsert | unique |
Their meaning is fixed by the protocol; configuration can only switch them off, or run your own code around them (Pipelines). Each variant, with an example, is in Resources and operations.
Results and typing
Every operation answers one envelope, { data, code, cause }, on the wire. The generated client and your server-side calls (framework.application, framework.client, framework.transaction) resolve to data and throw a typed error for every failure; a first miss is null, a count is a number, and results are readonly. The type of a result follows your select exactly. Details: Results and typing.
{ "data": { "id": 1, "email": "ada@example.com" }, "code": "A1002", "cause": null }Branch on code, never on cause.message. The codes are listed in the HTTP protocol.
The contract hash
The client contract has a hash over everything it advertises, served as the ETag of /_contract and sent by the generated client on every request. If the server's contract has changed, the request is refused with A2005 (CONTRACT_MISMATCH) and the client throws AvContractMismatchError:
Generated client contract does not match the server. Regenerate the client.Details and the regenerate workflow: Contract hash.
Where to go next
| To learn | Read |
|---|---|
| What each layer sees | Contract layers |
| Hiding fields and operations per layer | Exposure |
| Every operation, with an example | Resources and operations |
| How results are typed | Results and typing |
| Identity, ETag and stale clients | Contract hash |
| Changing what each layer exposes | Configuration |
| The query language | Querying |
See also: Exposure, Exposing and hiding fields, Pipelines, Limits and safety.