Fastify Adapter
The @zenstackhq/server/fastify
module provides a quick way to install API routes onto a Fastify project for database CRUD operations. Combined with ZenStack's power of enhancing Prisma with access policies, it's surprisingly simple to achieve a secure data backend without manually coding it.
Installation
npm install @zenstackhq/server
Mounting the API
You can integrate ZenStack into your project with the ZenStackFastifyPlugin
fastify plugin:
import { enhance } from '@zenstackhq/runtime';
import { ZenStackFastifyPlugin } from '@zenstackhq/server/fastify';
import { prisma } from './db.ts';
import { getSessionUser } from './auth.ts';
const server = fastify();
// serve OpenAPI at /api/model
server.register(ZenStackFastifyPlugin, {
prefix: '/api/model',
// getSessionUser extracts the current session user from the request, its
// implementation depends on your auth solution
getPrisma: (request) => enhance(prisma, { user: getSessionUser(request) }),
});
The Fastify adapter takes the following options to initialize:
-
prefix
string
Prefix for the mounted API endpoints. E.g.: /api/model.
-
getPrisma (required)
(request: FastifyRequest, reply: FastifyReply) => unknown | Promise<unknown>
A callback for getting a PrismaClient instance for talking to the database. Usually you'll use an enhanced instance created with ZenStack's
enhance
API to ensure access policies are enforced. -
logger (optional)
LoggerConfig
Configuration for customizing logging behavior.
-
modelMeta (optional)
ModelMeta
Model metadata. By default loaded from the
node_module/.zenstack/model-meta
module. You can pass it in explicitly if you configured ZenStack to output to a different location. E.g.:require('output/model-meta').default
. -
zodSchemas (optional)
ModelZodSchema | boolean | undefined
Provides the Zod schemas used for validating CRUD request input. The Zod schemas can be generated with the
@core/zod
plugin. Passtrue
for this option to load the schemas from the default location. If you configured@core/zod
plugin to output to a custom location, you can load the schemas explicitly and pass the loaded module to this option. E.g.:factory({
...
zodSchemas: require('./zod'),
});Not passing this option or passing in
undefined
disables input validation. -
handler (optional)
(req: RequestContext) => Promise<Response>
The request handler function. This option determines the API endpoints and its input and output formats. Currently ZenStack supports two styles of APIs: RPC (the default) and RESTful.
-
RPC
The goal of the RPC-style API handler is to fully mirror PrismaClient's API across the network, so that developers can continue enjoying the convenience and flexibility of Prisma's query syntax. This is the default choice for the
handler
option.The RPC-style handler can be created like:
import { RPCApiHandler } from '@zenstackhq/server/api';
const handler = RPCApiHandler();For more details, please check out RPC API Handler.
-
RESTful
The goal of RESTful-style API handler is to provide a resource-centric RESTful API using JSON:API as transportation format.
The RESTful-style handler can be created like:
import { RestApiHandler } from '@zenstackhq/server/api';
const handler = RestApiHandler({ endpoint: 'http://myhost/api' });For more details, please check out RESTful API Handler.
-
Using the API
The APIs can be used in the following three ways:
-
With generated client hooks
ZenStack provides plugins to generate client hooks from the ZModel targeting the most popular frontend data fetching libraries: TanStack Query and SWR. The generated hooks can be used to make API calls to the server adapters. Refer to the follow docs for detailed usage:
infoThe generated client hooks assumes the server adapter uses RPC-style API handler (which is the default setting).
-
With direct HTTP calls
You can make direct HTTP calls to the server adapter using your favorite client libraries like
fetch
oraxios
. Refer to the documentation of the API Handlers for the API endpoints and data formats.Here's an example using
fetch
:- RPC Handler
- RESTful Handler
// create a user with two posts
const r = await fetch(`/api/user/create`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
include: { posts: true },
data: {
email: 'user1@abc.com',
posts: {
create: [{ title: 'Post 1' }, { title: 'Post 2' }],
},
},
}),
});
console.log(await r.json());Output:
{
"id": 1,
"email": "user1@abc.com",
"posts": [
{
"id": 1,
"createdAt": "2023-03-14T07:45:04.036Z",
"updatedAt": "2023-03-14T07:45:04.036Z",
"title": "Post 1",
"authorId": 1
},
{
"id": 2,
"createdAt": "2023-03-14T07:45:04.036Z",
"updatedAt": "2023-03-14T07:45:04.036Z",
"title": "Post 2",
"authorId": 1
}
]
}// create a user and attach two posts
const r = await fetch(`/api/user`, {
method: 'POST',
headers: { 'Content-Type': 'application/vnd.api+json' },
body: JSON.stringify({
data: {
type: 'user',
attributes: {
email: 'user1@abc.com'
},
relationships: {
posts: {
data: [
{ type: 'post', id: 1 },
{ type: 'post', id: 2 }
]
}
}
}
})
});
console.log(await r.json());Output:
{
"jsonapi": { "version": "1.1" },
"data": {
"type": "user",
"id": 1,
"attributes": {
"email": "user1@abc.com",
},
"links": {
"self": "http://localhost/api/user/1",
},
"relationships": {
"posts": {
"links": {
"self": "http://localhost/api/user/1/relationships/posts",
"related": "http://localhost/api/user/1/posts",
},
"data": [
{ "type": "post", "id": 1 },
{ "type": "post", "id": 2 },
],
},
},
},
} -
With third-party client generators
ZenStack provides an OpenAPI plugin for generating Open API 3.x specification from the ZModel. The generated OpenAPI spec can be used to generate client libraries for various languages and frameworks. For example, you can use openapi-typescript to generate a typescript client.
Error Handling
Refer to the specific sections for RPC Handler and RESTful Handler.