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.64k stars 65 forks source link

Handle error in getServerSideProps #184

Closed nemanjam closed 2 years ago

nemanjam commented 2 years ago

I want to reuse same error handler from APIs in getServerSideProps. I looked carefully in Readme doc and I see that neither handler.use(base, ...fn) can receive error middleware, nor handler.run(req, res) triggers main onError handler. How can I reuse same API error handler for getServerSideProps?

I think it would be more elegant to either can have use(onErrorMiddleware) or an option to trigger the default API onError handler.

Here is what I have:

// main API handler
export const ncOptions = {
  onError(error: Error, req: NextApiRequest, res: NextApiResponse) {
    handleError(error, req, res);
  },
  onNoMatch(req: NextApiRequest, res: NextApiResponse) {
    const error = new ApiError(`Method '${req.method}' not allowed`, 405);
    //not in api, handle it manually
    handleError(error, req, res);
  },
};

export type NextApiRequestWithResult = NextApiRequest & { result: any };

type NextReq = IncomingMessage & {
  cookies: NextApiRequestCookies;
};

// SSR handler that receives callback and returns result
export const ssrNcHandler = async (
  req: NextReq,
  res: ServerResponse,
  callback: () => Promise<any>
) => {
  const base = () => {
    const handler = nc<NextApiRequestWithResult, NextApiResponse>(ncOptions).use(
      async (req, res, next) => {
        req.result = await callback();
        next();
      } // cant I pass here onErrorMiddleware?
    );

    return handler;
  };

  const _req = req as NextApiRequestWithResult;
  const _res = res as NextApiResponse;

  try {
    await base().run(_req, _res);

    return _req.result;
  } catch (error) {
    handleError(error, _req, _res); // handle error here, log etc...
    return null;
  }
};

Usage:

export const getServerSideProps: GetServerSideProps = async ({ req, res, params }) => {
  const id = Number(params?.id);

  const callback = async () => await getPostWithAuthorById(id);
  const post = (await ssrNcHandler(req, res, callback)) as PostWithUser;

  if (!post) {
    return {
      notFound: true,
    };
  }
...
hoangvvo commented 2 years ago

This is solvable in the current version using the .run function. The upcoming version will make this clearer https://github.com/hoangvvo/next-connect/tree/v1-rewrite#nextjs-getserversideprops