hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2
https://www.npmjs.com/package/next-connect
MIT License
1.63k stars 65 forks source link

Preloading data automatically #124

Closed vvo closed 2 years ago

vvo commented 3 years ago

Hi there, I am currently not yet using next-connect. I plan on using it still because its interface seems great.

Before I use it, I need to know how I could solve one pattern: data preloading.

Right now all my Next.js API handlers are wrapped with something like that:

/pages/api/teams/[teamId]/users

function teamUsersApi(req, res, preloadedData) {
  console.log(preloadedData.user);
  console.log(preloadedData.team);
}

export default withPreloadData(teamApi);

When I call /api/teams/200/users then I preload from my database the current user (from reading cookie data) and the team with id 200 (from url params). This is all done by withPreloadData which then calls the API handler with (req, res, preloadedData).

What would be the right pattern to do the same with next-connect? Attaching some property to req maybe? If so what would be the right TypeScript typings to use for that?

Thanks!

hoangvvo commented 3 years ago

You would have to attach some properties to req (even though I am not a big fan tbh).

When creating the nc instance just define the type there.

export default nc<
  NextApiRequest & { team: Team | null; user: User | null },
  NextApiResponse
>()
  .use(async (req, res, next) => {
    const [user, team] = await Promise.all(getUser(req.cookies), getTeam(req.query.teamId));
    req.user = user;
    req.team = team;
  })
  .get((req, res) => {
    console.log(req.user);
    console.log(req.team);
  })
  .post((req, res) => {
    console.log(req.user);
    console.log(req.team);
  });

If user and team properties only exist on a specific handler. You can define the generics per handler.

export default nc<NextApiRequest, NextApiResponse>()
  .use<{ team?: Team | null; user?: User | null }>(async (req, res, next) => {
    if (req.method !== "POST") return next();
    const [user, team] = await Promise.all(getUser(req.cookies), getTeam(req.query.teamId));
    req.user = user;
    req.team = team;
    next();
  })
  .get((req, res) => {
    console.log(req.user); // No
    console.log(req.team); // No
  })
  .post<{ team: Team | null; user: User | null }>((req, res) => {
    console.log(req.user);
    console.log(req.team);
  });

Basically whatever generics provided per handler get merged with the ones provided when creating the instance.

You should probably split that middleware into two seperate ones getUserMiddleware and getTeamMiddleware.