
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Next tAPI (Next.js typesafe API) is an intuitive and powerful abstraction of Next.js API Routes. Taking inspiration from tRPC, Next tAPI is DX focussed, making the development of your Next.js API easier, safer, and more enjoyable.
Features:
npm install next-tapi # npm
yarn add next-tapi # yarn
pnpm add next-tapi # pnpm
Define API method handlers on the router object and export.
// pages/api/some-route
import { createRouter } from "next-tapi";
const router = createRouter();
router.get(({ res, res, ...}) => { ... });
router.post(({ res, res, ...}) => { ... });
router.delete(({ res, res, ...}) => { ... });
router.put(({ res, res, ...}) => { ... });
router.patch(({ res, res, ...}) => { ... });
export default router.export();
The object returned by each method handler will be sent. The status code can be changed as usual on the res object.
router.get(({ res }) => {
// create some resource
res.status(201);
return { ... }
})
It may be useful to define a return type which can be done by defining the generic function.
router.get<{ id: number }>(() => {
return {
id: "123", // error, must be a number
};
});
Errors thrown in an API method handler or middleware will be caught and processed. A custom error handler can be easily added to handle custom errors. Next tAPI provides its own TapiError which is handled with the default error handler.
import { TapiError } from "next-tapi";
router.get((req) => {
throw new TapiError({ status: 400, message: "Oh no!" });
return { ... };
});
Middleware in Next tAPI is similar to Express but typed variables are accessible in subsequent middleware and method handlers in the fields object.
Note that Next tAPI middleware does not a replacement for Next.js middleware. Next tAPI middleware is especially useful when typed variables want to be passed to a method handler.
router
.middleware(async ({ req, res, next }) => {
const session = await getSession(req, res);
return next({
session,
});
})
.middleware(({ fields, next }) => {
fields.session; // fully typed
return next();
})
.get(({ fields }) => {
fields.session; // fully typed
return { ... };
});
Errors thrown in middleware will be handled as normal by the error handler.
router.middleware(async ({ req, res, next }) => {
throw new TapiError({ status: 400, message: "Oh no!" });
return next();
});
Reusable middleware can be created with the createMiddleware function.
export const authMiddleware = createMiddleware(asyncc ({ req, res, next }) => {
const session = await getSession(req, res);
if (!session) throw new TapiError({ status: 403, "Not allowed!" });
return next({
session
});
});
The middleware can be easily consumed.
import { authMiddleware } from "path/to/middleware"
router.middleware(authMiddleware).get(({ fields }) => {
fields.session // typed
return { ... }
})
Before defining the method handler, the body and query methods can be defined which validate the request body and query respectively, which are then available in the method handler. Next tAPI is agnostic to choice of validation; however, this can be done easily with zod, yup, or other validation libraries.
router
.body(zodBodySchema.parse)
.query(zodQuerySchema.parse)
.get(({ body }) => {
// body and query typesafe
return { ... };
})
router
.body((body) => yupBodySchema.cast(body))
.query((query) => yupQuerySchema.cast(query))
.post(({ body }) => {
// body and query typesafe
return { ... };
});
Reusable query and body resolvers can be created with the createQueryResolver and createResolver functions.
Responses returned by method handlers can be forced to extend a certain shape. Let's say we wanted all successful responses to extend the following interface:
interface StandardSuccessResponse<T extends {}> {
success: true;
data: T;
}
We could do so using the generic:
const router = createRouter<StandardSuccessResponse<{}, StandardErrorResponse>>();
router.get(() => {
return {
user: { ... } // error, does not extend StandardSuccessResponse<{}>
}
}
Response types defined must also extend the specified type.
router.get<StandardResponse<{ name: string }>>(() => {
return {
success: true,
data: { name: "Next tAPI" }
}
}
A custom error handler can easily be added to handle custom errors or to change the error response.
interface StandardErrorResponse {
success: false;
error: { message: string };
}
const myCustomErrorHandler: ErrorHandler<StandardErrorResponse> = (
req,
res,
err
) => {
// handle error
return res.status(400).json({
success: false,
error: {
message: "Oh no!",
},
});
};
const router = createRouter<StandardSuccessResponse<{}>, StandardErrorResponse>(
{
errorHandler: myCustomErrorHandler,
}
);
It may be convenient to define a few types of routers with different middleware.
import { authMiddleware, logRequest } from "path/to/middleware";
import { myCustomErrorHandler } from "path/to/myCustomErrorHandler";
import { createRouter } from "next-tapi";
export const mainRouter = () => {
return createRouter<StandardSuccessResponse<{}>>({
errorHandler: myCustomErrorHandler,
}).middleware(logRequest);
};
export const authRouter = () => {
return createRouter<StandardSuccessResponse<{}>>({
errorHandler: myCustomErrorHandler,
})
.middleware(logRequest)
.middleware(authMiddleware);
};
Regular Next.js
Without an abstraction like Next tAPI, API routes get extremely repetitive.
const handler: NextApiHandler = (req, res) => {
switch (req.method) {
case "GET":
return handleGET(req, res);
case "POST":
return handlePOST(req, res);
default:
return res.status(405).json({
error: `${req.method} method not supported.`,
});
}
};
export default handler;
Next tAPI
Forget the switch-case, or other unideal solutions. Define the methods you want to support and the error handler will take care of the rest.
import { createRouter } from "next-tapi";
const router = createRouter();
router.get(() => {
return { ... }
})
router.post(() => {
return { ... }
})
export default router.export();
Regular Next.js
Sending errors (especially in a consistent way) is especially annoying in regular API routes.
const handler: NextApiHandler = async (req, res) => {
if (req.method !== "GET")
return res.status(405).json({
error: `${req.method} method not supported.`,
});
const session = await getSession(req, res);
if (!session)
return res.status(401).json({
error: `You must be authenticated.`,
});
if (!session.role === "ADMIN")
return res.status(403).json({
error: `You are not authorised to access this resource.`,
});
try {
const data = await db.query("some_table").where({
user_id: session.user.id,
});
if (!data)
return res.status(404).json({
error: "Resource not found",
});
return res.status(200).json({ data });
} catch (err) {
return res.status(500).json({
error: "Some DB error.",
});
}
};
Next tAPI
Simply throw errors in the handler body and consistently handle errors in the error handler. This is similar to Express.
router.middleware(authMiddleware).get(async ({ fields }) => {
const data = await db.query("some_table").where({
user_id: session.user.id,
});
if (!data) throw new TapiError({ status: 404, message: "Oh no!" });
return { data };
});
FAQs
Next.js type-safe API wrapper.
We found that next-tapi demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.