hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2

Home Page:https://www.npmjs.com/package/next-connect

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Nextjs routes

majeed4u opened this issue · comments

not working with nextjs 13 app directory api

i have trieded many times

What problems are you facing?

So... Next-connect, The example provided looks like it is used in "one file"
I will refer as nextjs pages as "pages", and nextjs-13 as "app".

Example with nextjs-13: https://github.com/hoangvvo/next-connect/blob/main/examples/nextjs-13/src/app/api/users/route.ts

But the beauty of next-connect seems to be it can be defined as a "centralized hub" for api routes.

In "Pages" app shown here you can create a wrapper with next-connect

/api/shows/index.ts

const handler = createHandler();
handler.get(async (req: NextApiRequest, res: NextApiResponse) => {
  let shows = await getShows();

  // generate shows if there aren't any
  if (shows.length === 0) {
    await generateData();
    shows = await getShows();
  }

  return res.status(200).json({ shows });
});

createHandler is stored in a api/handler file.

import type { NextApiRequest, NextApiResponse } from "next";
import nextConnect from "next-connect";

import { validateToken } from "@/lib/auth/utils";

import { processApiError } from "./utils";

// base handler for centralized error and method handling
export const createHandler = ({
  authRequired,
}: { authRequired?: boolean } = {}) => {
  const handler = nextConnect({
    onError(error, req: NextApiRequest, res: NextApiResponse) {
      const { status, message } = processApiError(error);
      res.status(status).json({ message });
    },
    onNoMatch(req: NextApiRequest, res: NextApiResponse) {
      res.status(405).end(`Method ${req.method} Not Allowed`);
    },
  });
  if (authRequired) {
    handler.use(async (req, res, next) => {
      const tokenIsValid = await validateToken(req);
      if (!tokenIsValid) return res.status(401).end();
      return next();
    });
  }
  return handler;
};

With this setup. We don't need to add useSession in every page.tsx file. It can be handled during api call as well.

// As example an reservation page - requires authenticated calls
const handler = createHandler({ authRequired: true });
handler.post(async (req: NextApiRequest, res: NextApiResponse) => {

My own attempt of doing in "app":

import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { createEdgeRouter } from "next-connect";

import { validateToken } from "@/lib/auth/utils";

import { processApiError } from "./utils";

// base handler for centralized error and method handling
export const createHandler = ({
  authRequired,
}: { authRequired?: boolean } = {}) => {
  const handler = createEdgeRouter<NextRequest, NextFetchEvent>();
  handler.handler({
    onError(error, req, res) {
      const { status, message } = processApiError(error);
      NextResponse.json( { message },{ status });
    },
    onNoMatch(req, res) {
      NextResponse.json(`Method ${req.method} Not Allowed`);
    },
  });
  if (authRequired) {
    handler.use(async (req, res, next) => {
      const tokenIsValid = await validateToken(req, req.nextUrl.searchParams.get("userId"));
      if (!tokenIsValid) return NextResponse.json({status: 401});
      return next();
    });
  }
  return handler;
};

Used in route.ts /api/shows/ as:

export async function GET(req: Request) {
  return handler.get(async (req: Request) => {
    let shows = await getShows();

    // generate shows if there aren't any
    if (shows.length === 0) {
      await generateData();
      shows = await getShows();
    }

    return NextResponse.json({ shows });
  });
}

But I don't get it working yet because I think nextConnect wrapper is used in handler.ts

EDIT:
Continuing on this. Looking at the types for "Old" nextConnect is

export default function <Req = IncomingMessage, Res = ServerResponse>(
    options?: Options<Req, Res>
  ): NextConnect<Req, Res>;

In new one it alittle different.

export { createEdgeRouter } from "./edge.js";
export { expressWrapper } from "./express.js";
export { createRouter } from "./node.js";
export type { HandlerOptions, NextHandler } from "./types.js";

One thing that gets me , most of times, is people built perfect libraries but then when it refactored many use-cases in the old ones are lost for "faster" way of writing. Having a page with "old code" - for this scenario. Here's the exact same scenario but with using new version.
This is such a bummer for developers who are really used using older libraries and to their capabilities only to be thrown down into the bottom, to re-learn everything yet again.