Skip to main content
Version: 1.x

Express.js Adapter

The @zenstackhq/server/express module provides a quick way to install API routes onto a Express.js 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 ZenStackMiddleware express middleware:

import { PrismaClient } from '@prisma/client';
import { enhance } from '@zenstackhq/runtime';
import { ZenStackMiddleware } from '@zenstackhq/server/express';
import express from 'express';

const prisma = new PrismaClient();
const app = express();

app.use(express.json());

app.use(
'/api/model',
ZenStackMiddleware({
// getSessionUser extracts the current session user from the request, its
// implementation depends on your auth solution
getPrisma: (request) => enhance(prisma, { user: getSessionUser(request) }),
})
);

The Express.js adapter takes the following options to initialize:

  • getPrisma (required)

    (request: Request, response: Response) => 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. Pass true 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.

  • useSuperJson (optional)

    boolean

    Whether to use superjson for data serialization. Defaults to false.

    NOTE: This options is decprecated. The server adapters now always use superjson for data serialization.

  • 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/rpc';
      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/rest';
      const handler = RestApiHandler({ endpoint: 'http://myhost/api' });

      For more details, please check out RESTful API Handler.

  • sendResponse (optional)

    boolean

    Controls if the middleware directly sends a response. If set to false, the response is stored in the res.locals object and then the middleware calls the next() function to pass the control to the next middleware. Subsequent middleware or request handlers need to make sure to send a response.

    Defaults to true.

Using the API​

The APIs can be used in the following three ways:

  1. 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:



    info

    The generated client hooks assumes the server adapter uses RPC-style API handler (which is the default setting).

  2. With direct HTTP calls

    You can make direct HTTP calls to the server adapter using your favorite client libraries like fetch or axios. Refer to the documentation of the API Handlers for the API endpoints and data formats.

    Here's an example using fetch:

    // 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
    }
    ]
    }
  3. 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.

Fully working example​

You can find a fully working example here.

Comments
Feel free to ask questions, give feedback, or report issues.

Don't Spam


You can edit/delete your comments by going directly to the discussion, clicking on the 'comments' link below