Next.js Server
Installation
- pnpm
- npm
- yarn
pnpm add @ts-rest/next
npm install @ts-rest/next
yarn add @ts-rest/next
Usage
For maximum type safety, by default @ts-rest/next
uses a single endpoint for all routes.
It is possible to split out to multiple endpoints by having multiple [...ts-rest].tsx files in different folders, so long as the routers you use all begin with the same path e.g. all /posts
endpoints in a posts folder.
// pages/api/[...ts-rest].tsx
import { createNextRoute, createNextRouter } from '@ts-rest/next';
// `contract` is the AppRouter returned by `c.router`
const postsRouter = createNextRoute(contract.posts, {
createPost: async (args) => {
const newPost = await posts.createPost(args.body);
return {
status: 201,
body: newPost,
};
},
});
const router = createNextRoute(contract, {
posts: postsRouter,
});
// Actually initiate the collective endpoints
export default createNextRouter(contract, router);
createNextRouter
is a function that takes a contract and a complete router, and creates endpoints, with the correct methods, paths and callbacks.
It's also possible to create a handler for a single contract route.
// pages/api/posts/index.tsx
import { createSingleRouteHandler } from '@ts-rest/next';
export default createSingleRouteHandler(api.posts.createPost, async (args) => {
const newPost = await posts.createPost(args.body);
return {
status: 201,
body: newPost,
};
});
createSingleRouteHandler
is a function that takes a single contract route and a handler, and creates a nextApiHandler
JSON Query Parameters
To handle JSON query parameters, you can use the jsonQuery
option.
export default createNextRouter(contract, router, { jsonQuery: true });
Response Validation
To enable response parsing and validation, you can use the validateResponses
option.
If there is a corresponding response Zod schema defined in the contract for the returned status code, the response will be parsed and validated.
If validation fails a ResponseValidationError
will be thrown causing a 500 response to be returned.
export default createNextRouter(contract, router, { validateResponses: true });
Error Handling
You can create a global error handler to handle any thrown errors by using the errorHandler
option.
This includes response validation errors.
export default createNextRouter(contract, router, {
responseValidation: true,
errorHandler: (error: unknown, req: NextApiRequest, res: NextApiResponse) => {
if (error instanceof ResponseValidationError) {
console.log(error.cause);
return res.status(500).json({ message: 'Internal Server Error' });
}
},
});
Request Validation Error Handling
The default behavior when validating request schema is to return a 400 status with the corresponding ZodError.
To customize handling of the request validation errors enable the throwRequestValidation
option.
This option allows you to handle the request validation error in the global errorHandler
function.
export default createNextRouter(contract, router, {
throwRequestValidation: true,
errorHandler: (error: unknown, req: NextApiRequest, res: NextApiResponse) => {
if (error instanceof RequestValidationError) {
if (error.body !== null) {
return res
.status(400)
.json({ message: 'Malformed Body', errors: error.body.flatten() });
}
return res.status(400).json({ message: 'Bad Request' });
}
},
});
export class RequestValidationError extends Error {
constructor(
public pathParams: z.ZodError | null,
public headers: z.ZodError | null,
public query: z.ZodError | null,
public body: z.ZodError | null,
) {
super('[ts-rest] request validation failed');
}
}