prisma-generator-express
v1.16.6
Published
Prisma generator of Express CRUD API
Downloads
69
Maintainers
Readme
Prisma Generator Express
This tool helps you quickly create API endpoints in your Express app using your Prisma models.
When you run npx prisma generate
, it automatically creates two things:
- Exposes Prisma API to clients - all operations with database is available, including all relationships at any depth.
- Service functions that you can import into your Express routes. By default, these functions handle CRUD operations and output validation. This behavior can be controlled.
- Router generator function that lets you select which routes to add to the application and which middlewares to apply.
Table of Contents
Installation
Using npm:
npm install prisma-generator-express
Using yarn:
yarn add prisma-generator-express
Basic usage
- Include this generator in your schema.prisma file:
generator express {
provider = "prisma-generator-express"
}
- Generate your middleware functions by running:
npx prisma generate
- Attach Prisma instance
import { PrismaClient } from '@prisma/client'
import express from 'express'
const prisma = new PrismaClient()
const app = express()
app.use(express.json()) // for parsing application/json
// Attach Prisma to every request
app.use((req, res, next) => {
req.prisma = prisma
next()
})
- Here’s how you can use a generated function in your Express app:
import { UserFindUnique } from './generated/api/UserFindUnique' // Adjust the path as necessary
import { FindUniqueUserSchema } from './prisma-zod-generator/schemas/FindUniqueUser.schema' // Adjust the path as necessary
import { FindUniqueUserSchemaOutput } from './prisma-zod-generator/schemas/FindUniqueUserOutput.schema' // Adjust the path as necessary
// move this to /helpers
export function validateQuery(schema: ZodSchema) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
req.query = schema.parse(req.query)
next()
} catch (error) {
res.status(400).json({
error: 'Input Validation failed',
details: error.errors,
})
}
}
}
app.get(
'/user/:id',
validateQuery(FindUniqueUserSchema),
async (req, res, next) => {
// Attach generated Zod schema for output validation
req.outputValidation = FindUniqueUserOutput
// Use the generated middleware to handle the request
await UserFindUnique(req, res, next)
},
)
The UserFindUnique
function will fetch the user details from the database, validate the output with Zod, and handle the API response.
Router generator usage
The library will create functions to generate routers per each model in schema. Each route can accept middleware that will be injected right before generated handler is invoked. You can use it to modify/remove/validate request payload. The output validation can be also attached to req
object on this step.
import express, { json } from 'express'
import type { Response, Request, NextFunction, RequestHandler } from 'express'
import { PrismaClient } from '../prisma/generated/client'
import { UserAccountRouter } from '../prisma/generated/express/UserAccount'
import { RouteConfig } from '~prisma/generated/express/routeConfig'
import { UserAccountFindFirstSchema } from '../prisma/generated/prisma-zod-generator/schemas'
const app = express()
const prisma = new PrismaClient()
/**
* Middleware to attach Prisma client instance to the request object.
* This ensures that Prisma client is available in all subsequent middleware and route handlers.
*/
const addPrisma: RequestHandler = (
req: Request,
res: Response,
next: NextFunction,
) => {
req.prisma = prisma
next()
}
/**
* Run context-related operations or modify `req` properties to control the behavior of the route
*/
const beforeFindFirst: RequestHandler = (
req: Request,
res: Response,
next: NextFunction,
) => {
req.passToNext = true
next()
}
/**
* if `req.passToNext` is true, then the result of generated middleware
* will be available in req.locals?.data for modifications
*/
const afterFindFirst: RequestHandler = (
req: Request,
res: Response,
next: NextFunction,
) => {
console.log('req.locals?.data :>> ', req.locals?.data)
next()
}
/**
* For generated route the middleware order will be as follows:
* 1. Query parser (kicks in for GET requests)
* 2. Custom middlewares: config.{method}.before[]
* 3. Input validator middleware (Optional): config.{method}.input. For GET request validates `req.query`, for others - `req.body`
* 4. Generated middleware
* 5. Output validator middleware: config.{method}.input
* 6. Custom middlewares: config.{method}.after[] (not available if req.passToNext is falsy)
*/
const userAccounRouterConfig: RouteConfig<RequestHandler> = {
FindFirst: {
before: [beforeFindFirst],
after: [afterFindFirst],
input: {
schema: UserAccountFindFirstSchema, // make sure you set `isGenerateSelect = true` in prisma-zod-generator
allow: [
'select.id',
'select.full_name',
'select.emailAddress',
'select.orders[].ProductName',
'select.orders[].quantity',
'where.id',
'where.createdAt',
],
},
},
addModelPrefix: true,
enableAll: true,
customUrlPrefix: '/v1',
}
app.use(addPrisma)
app.use(UserAccountRouter(userAccounRouterConfig))
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000')
})
Request Object Properties
The following properties can be attached to the req
object to control the behavior of generated middleware:
| Property | Type | Description |
| ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| prisma
| PrismaClient | An instance of PrismaClient that allows the middleware to interact with your database. |
| passToNext
| boolean | Optional, if true
- the result of a Prisma request will be passed to the next middleware as if (req.locals) req.locals.data
|
| outputValidation
| ZodTypeAny | (Optional) A Zod schema used to validate the data returned from the Prisma query before sending it to the client. |
Router Schema
| Function | Method | URL |
| ------------ | -------- | ------------ |
| findUnique
| GET
| /:id
|
| findFirst
| GET
| /first
|
| findMany
| GET
| /
|
| aggregate
| GET
| /aggregate
|
| count
| GET
| /count
|
| groupBy
| GET
| /groupby
|
| create
| POST
| /
|
| createMany
| POST
| /many
|
| update
| PUT
| /
|
| updateMany
| PUT
| /many
|
| upsert
| PATCH
| /
|
| delete
| DELETE
| /
|
| deleteMany
| DELETE
| /many
|
Skip generation
/// generator off
model UserAccount {
ID Int @id @default(autoincrement())
full_name String
emailAddress String @unique
createdAt DateTime @default(now())
orders orderItem[]
}
Helper functions
createValidatorMiddleware(validatorOptions: ValidatorOptions)
Simple wrapper that internally uses allow
or forbid
logic for filtering incoming queries and data payloads. Helps to make sure that schemas from prisma-zod-generator
is not too permissive.
interface ValidatorOptions {
schema: ZodSchema<any>
allowedPaths?: string[] // Fobids all except allowed. For example [`where.user.id`, `select.id`], all other provided inputs will throw an error
forbiddenPaths?: string[] // Similar, but allows all, except forbidden
target?: 'body' | 'query'
}
encodeQueryParams(params: Params)
It can be used on the frontend to encode Prisma-compatible queries. Alternatively qs
can be used, but it probably won't work with OR: [{ blah: false }, { blah: null }]
or some other edge cases.
type RecursiveUrlParams = {
[key: string]: RecursiveUrlParams | string | boolean | unknown
}
type Params = Record<string, RecursiveUrlParams | string>
parseQueryParams(params: QueryParams)
type QueryParams = string | ParsedQs | string[] | ParsedQs[] | undefined
Recursively converts strings "true", "false", "null", and "number" into correct formats.
allow( schema: T, allowedPaths: string[] ): ZodEffects<T, any, any>
Accepts schema and ['array.of.allowed.paths']
. Throws an error if provided something that doesn't fit allowed schema.
forbid( schema: T, forbiddenPaths: string[] ): ZodEffects<T, any, any>
Same as allow
but works in opposite way.
Credits:
Super Kick Gym - Brazilian Jiu Jitsu in Bangkok