title | sidebar_label |
---|---|
API Routes |
API Routes |
:::info
Unlike Next.js, your api/
folder should be a sibling of pages/
instead of being nested inside. But pages/api
is still supported for compatibility with Next.js.
:::
Any file inside an api/
folder are accessible at a URL corresponding to its path inside api/
. So app/projects/api/webhook.ts
will be at localhost:3000/api/webhook
.
For example, the following API route app/api/hello.js
handles a json
response:
export default (req, res) => {
res.statusCode = 200
res.setHeader("Content-Type", "application/json")
res.end(JSON.stringify({name: "John Doe"}))
}
For an API route to work, you need to export as default a function (a.k.a request handler), which then receives the following parameters:
req
: An instance of http.IncomingMessage, plus some pre-built middlewares you can see hereres
: An instance of http.ServerResponse, plus some helper functions you can see here
To handle different HTTP methods in an API route, you can use req.method
in your request handler, like so:
export default (req, res) => {
if (req.method === "POST") {
// Process a POST request
} else {
// Handle any other HTTP method
}
}
To fetch API endpoints, take a look into any of the examples at the start of this section.
API Routes do not specify CORS headers, meaning they are same-origin only by default. You can customize such behavior by wrapping the request handler with the cors middleware.
API Routes do not increase your client-side bundle size. They are server-side only bundles.
Dynamic API routes follow the same file naming rules used for pages
.
For example, the API route app/api/post/[pid].js
has the following code:
export default (req, res) => {
const {
query: {pid},
} = req
res.end(`Post: ${pid}`)
}
Now, a request to /api/post/abc
will respond with the text: Post: abc
.
A very common RESTful pattern is to set up routes like this:
GET api/posts/
- gets a list of posts, probably paginatedGET api/posts/12345
- gets post id 12345
We can model this in two ways:
-
Option 1:
/api/posts.js
/api/posts/[postId].js
-
Option 2:
/api/posts/index.js
/api/posts/[postId].js
Both are equivalent.
API Routes can be extended to catch all paths by adding three dots (...
) inside the brackets. For example:
app/api/post/[...slug].js
matches/api/post/a
, but also/api/post/a/b
,/api/post/a/b/c
and so on.
Note: You can use names other than
slug
, such as:[...param]
Matched parameters will be passed as a query parameter (slug
in the example) to the api handler, and it will always be an array, so, the path /api/post/a
will have the following query
object:
{"slug": ["a"]}
And in the case of /api/post/a/b
, and any other matching path, new parameters will be added to the array, like so:
{"slug": ["a", "b"]}
An API route for app/api/post/[...slug].js
could look like this:
export default (req, res) => {
const {
query: {slug},
} = req
res.end(`Post: ${slug.join(", ")}`)
}
Now, a request to /api/post/a/b/c
will respond with the text: Post: a, b, c
.
Catch all routes can be made optional by including the parameter in double brackets ([[...slug]]
).
For example, app/api/post/[[...slug]].js
will match /api/post
, /api/post/a
, /api/post/a/b
, and so on.
The query
objects are as follows:
{ } // GET `/api/post` (empty object)
{ "slug": ["a"] } // `GET /api/post/a` (single-element array)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (multi-element array)
- Predefined API routes take precedence over dynamic API routes, and dynamic API routes over catch all API routes. Take a look at the following examples:
app/api/post/create.js
- Will match/api/post/create
app/api/post/[pid].js
- Will match/api/post/1
,/api/post/abc
, etc. But not/api/post/create
app/api/post/[...slug].js
- Will match/api/post/1/2
,/api/post/a/b/c
, etc. But not/api/post/create
,/api/post/abc
The response (res
) includes a set of Express.js-like methods to improve the developer experience and increase the speed of creating new API endpoints, take a look at the following example:
export default (req, res) => {
res.status(200).json({name: "Blitz.js"})
}
The included helpers are:
res.status(code)
- A function to set the status code.code
must be a valid HTTP status coderes.json(json)
- Sends a JSON response.json
must be a valid JSON objectres.send(body)
- Sends the HTTP response.body
can be astring
, anobject
or aBuffer
API routes provide built in middlewares which parse the incoming request (req
). Those middlewares are:
req.cookies
- An object containing the cookies sent by the request. Defaults to{}
req.query
- An object containing the query string. Defaults to{}
req.body
- An object containing the body parsed bycontent-type
, ornull
if no body was sent
Every API route can export a config
object to change the default configs, which are the following:
export const config = {
api: {
bodyParser: {
sizeLimit: "1mb",
},
},
}
The api
object includes all configs available for API routes.
bodyParser
enables body parsing, you can disable it if you want to consume it as a Stream
:
export const config = {
api: {
bodyParser: false,
},
}
bodyParser.sizeLimit
is the maximum size allowed for the parsed body, in any format supported by bytes, like so:
export const config = {
api: {
bodyParser: {
sizeLimit: "500kb",
},
},
}
externalResolver
is an explicit flag that tells the server that this route is being handled by an external resolver like express or connect. Enabling this option disables warnings for unresolved requests.
export const config = {
api: {
externalResolver: true,
},
}
You can also use Connect compatible middleware.
For example, configuring CORS for your API endpoint can be done leveraging the cors package.
First, install cors
:
npm i cors
# or
yarn add cors
Now, let's add cors
to the API route:
import Cors from "cors"
// Initializing the cors middleware
const cors = Cors({
methods: ["GET", "HEAD"],
})
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
async function handler(req, res) {
// Run the middleware
await runMiddleware(req, res, cors)
// Rest of the API logic
res.json({message: "Hello Everyone!"})
}
export default handler