https://github.com/seamapi/nextlove
An NPM module that has best practices and essential modules for building APIs with NextJS
https://github.com/seamapi/nextlove
maintained
Last synced: 3 months ago
JSON representation
An NPM module that has best practices and essential modules for building APIs with NextJS
- Host: GitHub
- URL: https://github.com/seamapi/nextlove
- Owner: seamapi
- Created: 2022-06-27T05:05:40.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2025-02-20T19:11:31.000Z (4 months ago)
- Last Synced: 2025-04-05T20:15:44.230Z (3 months ago)
- Topics: maintained
- Language: TypeScript
- Homepage:
- Size: 1.62 MB
- Stars: 21
- Watchers: 4
- Forks: 2
- Open Issues: 40
-
Metadata Files:
- Readme: README.md
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# nextlove - Enhanced NextJS API Types, OpenAPI and Utilities
Make type-safe routes that automatically generate OpenAPI in NextJS easy!
- Define endpoints with middleware and have your request objects and responses automatically be typed
- The same [zod](https://github.com/colinhacks/zod) schemas used for your types will be in the generated
`openapi.json` file!
- Throw [http exceptions and they'll magically be handled](https://github.com/seamapi/nextjs-exception-middleware#exception-types)
- Have well-typed middleware## Installation
`yarn add nextlove`
## Create well-typed routes + middleware with nextlove!
`nextlove` allows you to create well-typed middleware and routes using utility types and functions. The
two main functions to know are `createWithRouteSpec`, which allows you to create a `withRouteSpec` function
that can be used with all your endpoints, and the `Middleware` utility function which makes middleware type-safe.Let's take a look at an example project with three files:
- **lib/with-route-spec.ts** - This file is used to create the `withRouteSpec` middleware. This middleware should
be used for all your routes.
- **lib/middlewares/with-auth-token.ts** - This is an authentication middleware we'll be using to make sure requests are authenticating
- **lib/middlewares/with-db.ts** - A common global middleware that attaches a database client to the request object
- **pages/api/health.ts** - Just a health endpoint to see if the server is running! It won't have any auth
- **pages/api/todos/add.ts** - An endpoint to add a TODO, this will help show how we can use auth!```ts
// pages/api/health.ts
import { withRouteSpec } from "lib/with-route-spec"
import { z } from "zod"const routeSpec = {
methods: ["GET"],
auth: "none",
jsonResponse: z.object({
healthy: z.boolean(),
}),
} as constexport default withRouteSpec(routeSpec)(async (req, res) => {
/* ... */
return res.status(200).json({ healthy: true })
})
``````ts
// lib/with-route-spec.ts
export const withRouteSpec = createWithRouteSpec({
authMiddlewareMap: { auth_token: withAuthToken },
globalMiddlewares: [globalMiddleware],// For OpenAPI Generation
apiName: "My API",
productionServerUrl: "https://example.com",
globalSchemas: {
user: z.object({
user_id: z.string().uuid(),
}),
},
} as const)
``````ts
// lib/middlewares/with-auth-token.ts
import { UnauthorizedException, Middleware } from "nextlove"export const withAuthToken: Middleware<{
auth: {
authorized_by: "auth_token"
}
}> = (next) => async (req, res) => {
req.auth = {
authorized_by: "auth_token",
}return next(req, res)
}export default withAuthToken
``````ts
// pages/api/todos/add.ts
import { withRouteSpec, UnauthorizedException } from "lib/with-route-spec"
import { z } from "zod"const routeSpec = {
methods: ["POST"],
auth: "auth_token",
jsonBody: z.object({
content: z.string(),
}),
jsonResponse: z.object({
ok: z.boolean(),
}),
} as constexport default withRouteSpec(routeSpec)(async (req, res) => {
// req.auth is correctly typed here!
if (req.auth.authorized_by !== "auth_token") {
throw new UnauthorizedException({
type: "unauthorized",
message: "Authenticate yourself to get the requested response",
})
}
// TODO add todo
return res.status(200).json({ ok: true })
})
```## createWithRouteSpec Parameters
| Parameter | Description |
| --------------------- | ------------------------------------------------------------ |
| `authMiddlewareMap` | Object that maps different types of auth to their middleware |
| `globalMiddlewares` | Middlewares that should be applied on every route |
| `apiName` | Used as the name of the api in openapi.json |
| `productionServerUrl` | Used as the default server url in openapi.json |## withRouteSpec Parameters
| Parameter | Description |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `methods` | HTTP Methods accepted by this route |
| `auth` | `none` or a key from your `authMiddlewareMap`, this authentication middleware will be applied |
| `queryParams` | Any GET query parameters on the request as a zod object |
| `jsonBody` | The JSON body this endpoint accepts as a zod object |
| `formData` | The `multipart/form-data` [(todo)](https://github.com/seamapi/nextlove/issues/127) or `application/x-www-form-urlencoded` encoded body |
| `commonParams` | Parameters common to both the query and json body as a zod object, this is sometimes used if a GET route also accepts POST |
| `jsonResponse` | A zod object representing the json resposne |### Generating OpenAPI Types (Command Line)
Just run `nextlove generate-openapi` in your project root!
Examples:
```bash
# Print OpenAPI JSON directly to the command line for the package in the current directory
nextlove generate-openapi --packageDir .# Write OpenAPI JSON to "openapi.json" file
nextlove generate-openapi . --outputFile openapi.json# Only generate OpenAPI JSON for public api routes
nextlove generate-openapi . --pathGlob '/pages/api/public/**/*.ts'
```| Parameter | Description |
| ------------ | ------------------------------------------------------------------------------------------ |
| `packageDir` | Path to directory containing package.json and NextJS project |
| `outputFile` | Path to output openapi.json file |
| `pathGlob` | Paths to consider as valid routes for OpenAPI generation, defaults to `/pages/api/**/*.ts` |### Generating OpenAPI Types (Script)
```ts
import { generateOpenAPI } from "nextlove"generateOpenAPI({
packageDir: ".",
outputFile: "openapi.json",
pathGlob: "/src/pages/api/**/*.ts",// Tags improve the organization of an OpenAPI spec by making "expandable"
// sections including routes
tags: ["users", "teams", "workspaces"].map((t) => ({
name: `/${t}`,
description: t,
doesRouteHaveTag: (route) => route.includes(`/${t}`),
})),
mapFilePathToHTTPRoute(fp) {
return fp
.replace("src/pages/api/public", "")
.replace(/\.ts$/, "")
.replace(/\/index$/, "")
},
})
```### Extracting route specs (Command Line)
Just run `nextlove extract-route-specs` in your project root! It will output a ESM file bundled by esbuild.
Caveats:
- All dependencies and dev dependencies in your `package.json` are automatically marked as external when bundling. This means that you may want to re-bundle the output file if you plan on publishing it as part of a library.
- By default, API route files aren't allowed to import anything besides dependencies declared in `package.json`. This is to avoid accidentally polluting the bundle. To allow specific imports, use the `--allowed-import-patterns` flag: `--allowed-import-patterns '**/lib/**' --allowed-import-patterns '**/models/**'`### Wrap middlewares together using `wrappers`!
```ts
import { wrappers } from "nextlove"wrappers(withDatabase, logger.withContext("somecontext"), async (req, res) => {
res.status(200).end("...")
})
```### nextjs-exception-middleware
```ts
import { BadRequestException } from "nextlove"// Inside a route handler
if (bad_soups.includes(soup_param)) {
throw new BadRequestException({
type: "cant_make_soup",
message: "Soup was too difficult, please specify a different soup",
data: { soup_param },
})
}
```### All Modules
This repo bundles NextJS utility modules including...
- [nextjs-exception-middleware](https://github.com/seamapi/nextjs-exception-middleware)
- [nextjs-server-modules](https://github.com/seamapi/nextjs-server-modules)
- [nextjs-middleware-wrappers](https://github.com/seamapi/wrappers)
- openAPI generation utilities