next-better-api ⚡️🔵 ![npm version](https://badge.fury.io/js/next-better-api.svg)
Opinionated TypeScript-first helpers for building better NextJS APIs, powered by Zod.
At a glance:
- 🙅♀️ Hands-off Typescript type inference based on your Zod validation schemas for
req.query
, req.body
and your API response - ✨ Type inference helpers to use with
react-query
, fetch
, and other client-side utilities - 🔌 Minimal and composable — bring your own request context, add middleware, etc
import { z } from 'zod';
import { endpoint, asHandler } from 'next-better-api';
const getUser = endpoint(
{
method: 'get',
querySchema: z.object({
id: z.string(),
}),
responseSchema: z.object({
user: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
active: z.boolean(),
}),
}),
},
async ({ req }) => {
const user = await getUser(req.query.id);
return {
status: 200,
body: {
user,
},
};
}
);
export default asHandler([getUser]);
Installation:
next-better-api
requires zod
for schema validation. You can install both libraries with yarn or npm on your existing NextJS project:
$ yarn add zod next-better-api
// OR
$ npm i -S zod next-better-api
And import it from your API definitions:
import { endpoint, asHandler } from 'next-better-api';
Remember you always need to use asHandler
to convert your next-better-api
endpoints to NextJS-compatible handlers:
export default asHandler([getUsers, createUser, updateUser]);
Features:
Method handler support
const getUsers = endpoint({
method: 'get',
});
const deleteUser = endpoint({
method: 'delete',
});
Schema validation & automatic type inference support
See Zod for all available schema validation options.
const createUser = endpoint(
{
method: 'post',
bodySchema: z.object({
name: z.string().min(3),
}),
responseSchema: z.object({
userId: z.string(),
}),
},
async ({ req }) => {
const { name } = req.body;
const newUser = await createUser(req.body);
return {
status: 201,
body: {
userId: newUser.id,
},
};
}
);
Endpoint type inference helpers
Different utility types are available for handling endpoint type information.
import type { InferEndpointType } from 'next-better-api';
import type { getUsers } from '../api/users.ts';
type GetUsersResponseBody = InferEndpointType<typeof getUsers>['Response'];
const response = await fetch('/api/users' );
const newUser = (await response.json()) as GetUsersResponseBody;
newUser.userId;
Additional helpers are available:
import type {
InferEndpointType,
InferQueryType,
InferResponseBodyType,
InferRequestBodyType,
} from 'next-better-api';
Composable endpoint context builder:
Use the context
option to provide a context builder with access to { req, res }
,
to generate your own request context object.
const getUser = endpoint(
{
method: 'get',
context: async ({ req }) => ({
user: await getUserFromRequest(req),
}),
},
({ user }) => ({
user: {
id: user.id,
email: user.email,
},
})
);
Stuff
See license information under LICENSE.md
.
Contributions are super welcome - in the form of bug reports, suggestions, or better yet, pull requests!