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 = 200res.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
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},} = reqres.end(`Post: ${pid}`)}
Now, a request to
/api/post/abc
will respond with the text: Post: abc
.Index routes and Dynamic API routes
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.
Catch all API routes
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},} = reqres.end(`Post: ${slug.join(", ")}`)}
Now, a request to
/api/post/a/b/c
will respond with the text: Post: a, b, c
.Optional catch all API routes
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)
Caveats
- 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
Response Helpers
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
Default Middlewares
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
Custom config
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,},}
Connect/Express middleware support
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# oryarn add cors
Now, let's add
cors
to the API route:import Cors from "cors"// Initializing the cors middlewareconst 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 middlewarefunction 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 middlewareawait runMiddleware(req, res, cors)// Rest of the API logicres.json({message: "Hello Everyone!"})}export default handler