I'm working on API Routes in Next.js with every path is structured like this:
import { NextApiRequest, NextApiResponse } from "next";
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { query } = req;
try {
switch (req.method) {
case "GET":
// Do some GET stuff
res.status(200).send(result);
break;
case "POST":
// Do some POST stuff
res.status(200).send(result);
break;
default:
// No Matched Method
res.status(405).send("Method Not Allowed");
break;
}
} catch (e) {
// Unprocessable Entity
res.status(422).send(e);
}
};
As you can see the default case and catch part will be duplicated all over the files. So, I wanted to know:
Here's my idea of a solution if there's no built-in support:
export default. I've tried to implement this but stuck at accessing req and res objects as in the code block below.const wrapAPICall = async (
fn: (req: NextApiRequest, res: NextApiResponse) => void
) => {
try {
await fn(req, res); // Can't access req, res here
} catch (e) {
res.status(422).send(e); // Also can't access req, res here
} finally {
if (!res.headersSent) res.status(405).send("Method Not Allowed");
}
};
const apiCall = async (req: NextApiRequest, res: NextApiResponse) => {
// Does some API stuff
};
export default wrapAPICall(apiCall);
I have found another solution rather than writing a custom wrapper is to use next-connect which is a routing middleware for Next.js
The code below produces the same results as a wrapper solution.
import { NextApiRequest, NextApiResponse } from "next";
import nc from "next-connect";
// Handle Error Here
const handler = nc({
onError: (err, req: NextApiRequest, res: NextApiResponse) => {
res.status(500).end(err.toString());
},
onNoMatch: (req: NextApiRequest, res: NextApiResponse) => {
res.status(405).send("Method Not Allowed");
},
});
handler
.get(async (req: NextApiRequest, res: NextApiResponse) => {
// Do some GET stuff
res.status(200).json(...);
})
.post(async (req: NextApiRequest, res: NextApiResponse) => {
// Do some POST stuff
res.status(200).json(...);
});
export default handler;
Next.js separate API routes into many files if you want the handler to be reusable do it like this.
/modules/Utils.ts
export const apiHandler = {
onError: (err, req: NextApiRequest, res: NextApiResponse) => {
res.status(500).end(err.toString());
},
onNoMatch: (req: NextApiRequest, res: NextApiResponse) => {
res.status(405).send("Method Not Allowed");
},
};
/pages/api/index.ts
import nc from "next-connect";
import { apiHandler } from "../modules/Utils";
const handler = nc(apiHandler); // Use handler exported from Utils
handler
.get((req, res) => {...})
.post((req, res) => {...});
export default handler;
I think your wrapper solution is sound. To make it work you just need to return a function from the wrapper.
const wrapAPICall = (
fn: (req: NextApiRequest, res: NextApiResponse) => void
) => {
return async (req: NextApiRequest, res: NextApiResponse) => {
try {
await fn(req, res);
} catch (e) {
res.status(422).send(e);
} finally {
if (!res.headersSent) res.status(405).send("Method Not Allowed");
}
}
};