Snippets

Next.js API Wrappers

Middleware-like wrappers around Next.js API Routes and Route Handlers (See also: TS Wrappers)

TS

import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";

/**
 * Wrapper around a Next.js API handler that attaches an extra
 * `wrapped` property to the request, and logs to the console.
 *
 * @param handler
 * @returns
 */
export const withWrapper = <
  Req extends NextApiRequest & { wrapped?: boolean },
  Res extends NextApiResponse
>(
  handler: (req: Req, res: Res) => ReturnType<NextApiHandler>
) => {
  // Define the new handler to replace the original one.
  function newHandler(this: any, req: Req, res: Res) {
    req.wrapped = true;

    // Invoke the original handler
    const result = handler.call(this, req, res);

    // Return the original function result
    return result;
  }

  // Return the new handler to be used in place of the old one
  return newHandler;
};
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";

/**
 * Wrapper around a Next.js API handler that attaches an extra
 * `wrapped` property to the request, and logs to the console.
 *
 * @param handler
 * @returns
 */
export const withWrapper = <
  Req extends NextApiRequest & { wrapped?: boolean },
  Res extends NextApiResponse
>(
  handler: (req: Req, res: Res) => ReturnType<NextApiHandler>
) => {
  // Define the new handler to replace the original one.
  function newHandler(this: any, req: Req, res: Res) {
    req.wrapped = true;

    // Invoke the original handler
    const result = handler.call(this, req, res);

    // Return the original function result
    return result;
  }

  // Return the new handler to be used in place of the old one
  return newHandler;
};

Examples

withLogging

Example showing how to log requests to an API handler

TS

import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";

/**
 * A wrapper that logs
 * each stage of the request.
 */
export function withLogging<
  Req extends NextApiRequest & { loggingEnabled?: boolean },
  Res extends NextApiResponse
>(handler: (req: Req, res: Res) => ReturnType<NextApiHandler>) {
  // Define the new function to replace the original one.
  async function newHandler(this: any, req: Req, res: Res) {
    req.loggingEnabled = true;

    const start = Date.now();

    console.log(`[${req.method}] ${req.url} started.`);

    try {
      const result = await handler.call(this, req, res);
      console.log(`[${req.method}] ${req.url} succeeded.`);
      return result;
    } catch (e) {
      res.status(500).send({ message: "Your request failed" });
      console.log(`[${req.method}] ${req.url} failed.`);
    }

    const stop = Date.now();
    console.log(`[${req.method}] ${req.url} took ${stop - start}ms.`);
  }

  // Return the new function to be used in place of the wrapped function
  return newHandler;
}
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";

/**
 * A wrapper that logs
 * each stage of the request.
 */
export function withLogging<
  Req extends NextApiRequest & { loggingEnabled?: boolean },
  Res extends NextApiResponse
>(handler: (req: Req, res: Res) => ReturnType<NextApiHandler>) {
  // Define the new function to replace the original one.
  async function newHandler(this: any, req: Req, res: Res) {
    req.loggingEnabled = true;

    const start = Date.now();

    console.log(`[${req.method}] ${req.url} started.`);

    try {
      const result = await handler.call(this, req, res);
      console.log(`[${req.method}] ${req.url} succeeded.`);
      return result;
    } catch (e) {
      res.status(500).send({ message: "Your request failed" });
      console.log(`[${req.method}] ${req.url} failed.`);
    }

    const stop = Date.now();
    console.log(`[${req.method}] ${req.url} took ${stop - start}ms.`);
  }

  // Return the new function to be used in place of the wrapped function
  return newHandler;
}

pages/api/hello.ts

Using a wrapper inside a Next.js API handler file

TS

// pages/api/hello.ts
import { NextApiResponse } from "next";
import { withLogging } from "./withLogging";

export default withLogging(function handler(
  req,
  res: NextApiResponse<{ message: string }>
) {
  if (req.loggingEnabled) {
    res.setHeader("X-Logging-Enabled", req.loggingEnabled.toString());
  }

  res.json({
    message: "Hello world!",
  });
});
// pages/api/hello.ts
import { NextApiResponse } from "next";
import { withLogging } from "./withLogging";

export default withLogging(function handler(
  req,
  res: NextApiResponse<{ message: string }>
) {
  if (req.loggingEnabled) {
    res.setHeader("X-Logging-Enabled", req.loggingEnabled.toString());
  }

  res.json({
    message: "Hello world!",
  });
});

© 2021-2024 Rexford Essilfie. All rights reserved.