Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type conflicts between NextAuth and Next.js Route Handlers #8243

Closed
Lada496 opened this issue Aug 6, 2023 · 9 comments
Closed

Type conflicts between NextAuth and Next.js Route Handlers #8243

Lada496 opened this issue Aug 6, 2023 · 9 comments
Labels
question Ask how to do something or how something works

Comments

@Lada496
Copy link

Lada496 commented Aug 6, 2023

Question 💬

I'm having type errors while trying to implement Advanced initialization with Route Handlers[https://nextjs.org/docs/app/building-your-application/routing/route-handlers] and wondering if there is any solution to fix this issue.

Error Messages:

  • Type '{ __tag__: "GET"; __param_position__: "first"; __param_type__: NextApiRequest; }' does not satisfy the constraint 'ParamCheck<Request | NextRequest>'.
  • Type '{ __tag__: "POST"; __param_position__: "first"; __param_type__: NextApiRequest; }' does not satisfy the constraint 'ParamCheck<Request | NextRequest>'.
  • Types of property '__param_type__' are incompatible.
  • Type 'NextApiRequest' is not assignable to type 'Request | NextRequest'.
  • Type 'NextApiRequest' is missing the following properties from type 'NextRequest': geo, ip, nextUrl, page, and 19 more.

Code

import type { NextApiRequest, NextApiResponse } from "next";
import { cookies } from "next/headers";
import NextAuth from "next-auth";
import { authOptions } from "~/server/auth";

async function auth(req: NextApiRequest, res: NextApiResponse) {
 ...some logic
  return await NextAuth(req, res, authOptions);
}

export { auth as GET, auth as POST };

In contrary when I use Request instead of NextApiRequest, then I get another type error: Type 'Request' is missing the following properties from type 'NextApiRequest': query, cookies, env, aborted, and 53 more.

How to reproduce ☕️

  • Next.js v13.4.1
  • NextAuth.js v4.21.0

While code is available here

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

@Lada496 Lada496 added the question Ask how to do something or how something works label Aug 6, 2023
@balazsorban44
Copy link
Member

App Router's Route Handlers are typed differently than the Pages Router. Check out https://nextjs.org/docs/app/api-reference/file-conventions/route

@Lada496
Copy link
Author

Lada496 commented Aug 9, 2023

I put a solution here just in case someone also faces the same problem as me.

import type { NextApiRequest, NextApiResponse } from "next";
import { cookies } from "next/headers";
import NextAuth from "next-auth";
import { authOptions } from "~/server/auth";

type CombineRequest = Request & NextApiRequest;
type CombineResponse = Response & NextApiResponse;

async function auth(req: CombineRequest, res: CombineResponse) {
 ...some logic
  return await NextAuth(req, res, authOptions);
}

export { auth as GET, auth as POST };

@balazsorban44
Copy link
Member

balazsorban44 commented Aug 12, 2023

The correct solution is:

import type { NextRequest } from "next/server"
import NextAuth from "next-auth"
import { authOptions } from "~/server/auth"

interface RouteHandlerContext {
  params: { nextauth: string[] }
}

async function auth(req: NextRequest, context: RouteHandlerContext) {
  // ...some logic
  return await NextAuth(req, context, authOptions)
}

export { auth as GET, auth as POST }

See the link I sent above: #8243 (comment)

@Lada496
Copy link
Author

Lada496 commented Aug 12, 2023

@balazsorban44 Thank you for your response. However TypeScript still complains with your solution asking me to use NextApiRequest instead of NextRequest.

Please correct me if I'm doing wrong, however, the followings are what I've found.
As long as I read the source code, NextAuth receives two different parameters: ...args: [AuthOptions] | [NextApiRequest, NextApiResponse, AuthOptions]. Therefore, NextRequest as a req type is not assignable from TypeScript perspective and I think we need to add some code at https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/next/index.ts to accept that types like this.

// Before
function NextAuth(options: AuthOptions): any
function NextAuth(
  req: NextApiRequest,
  res: NextApiResponse,
  options: AuthOptions
): any

/** The main entry point to next-auth */
function NextAuth(
  ...args: [AuthOptions] | [NextApiRequest, NextApiResponse, AuthOptions]
) {
  if (args.length === 1) {
    return async (
      req: NextAuthRequest | NextRequest,
      res: NextAuthResponse | RouteHandlerContext
    ) => {
      if ((res as unknown as any)?.params) {
        return await NextAuthRouteHandler(
          req as unknown as NextRequest,
          res as RouteHandlerContext,
          args[0]
        )
      }
      return await NextAuthApiHandler(
        req as NextApiRequest,
        res as NextApiResponse,
        args[0]
      )
    }
  }

  if ((args[1] as any)?.params) {
    return NextAuthRouteHandler(
      ...(args as unknown as Parameters<typeof NextAuthRouteHandler>)
    )
  }

  return NextAuthApiHandler(...args)
}
// After
function NextAuth(options: AuthOptions): any
function NextAuth(
  req: NextApiRequest,
  res: NextApiResponse,
  options: AuthOptions
): any
function NextAuth(
  req: NextRequest,
  res: RouteHandlerContext,
  options: AuthOptions
): any


/** The main entry point to next-auth */
function NextAuth(
  ...args: [AuthOptions] | [NextApiRequest, NextApiResponse , AuthOptions] | [NextRequest,RouteHandlerContext,AuthOptions]
) {
  if (args.length === 1) {
    return async (
      req: NextAuthRequest | NextRequest,
      res: NextAuthResponse | RouteHandlerContext
    ) => {
      if ((res as unknown as any)?.params) {
        return await NextAuthRouteHandler(
          req as unknown as NextRequest,
          res as RouteHandlerContext,
          args[0]
        )
      }
      return await NextAuthApiHandler(
        req as NextApiRequest,
        res as NextApiResponse,
        args[0]
      )
    }
  }

  if ((args[1] as any)?.params) {
    return NextAuthRouteHandler(
      ...(args as unknown as Parameters<typeof NextAuthRouteHandler>)
    )
  }

  return NextAuthApiHandler(...args as Parameters<typeof NextAuthApiHandler>)
}

export default NextAuth

Please take a look. Thanks.

@balazsorban44
Copy link
Member

It's on the v4 branch

function NextAuth(
req: NextRequest,
res: RouteHandlerContext,
options: AuthOptions
): any

@Lada496
Copy link
Author

Lada496 commented Aug 13, 2023

@balazsorban44 Thank you for your help.
I needed to update the next-auth version and now everything works fine.
I really appreciate it!

@goerlitz
Copy link
Contributor

The correct solution is:

import type { NextRequest } from "next/server"
import NextAuth from "next-auth"
import { authOptions } from "~/server/auth"

interface RouteHandlerContext {
  params: { nextauth: string[] }
}

async function auth(req: NextRequest, context: RouteHandlerContext) {
  // ...some logic
  return await NextAuth(req, context, authOptions)
}

export { auth as GET, auth as POST }

See the link I sent above: #8243 (comment)

Although next-auth is a great library, It took me 3 days to figure out how to do the advanced initialization with Route Handler correctly. After a lot of trial and error and digging deep into the source code of next-auth I finally came up with something that looks closely like @balazsorban44's solution. Unfortunately, only afterwards I found this discussion. :(

👉 I would strongly suggest to update the documentation with this example for Route Handler.

What I found most confusing:

  1. The signature of Next's RouteHandler is different from what the NextAuth RouteHandler expects
export type RouteHandler = (
  req: BaseNextRequest,
  res: BaseNextResponse,
  parsedUrl: NextUrlWithParsedQuery
) => PromiseLike<boolean> | boolean
async function auth(req: NextRequest, context: RouteHandlerContext) {
  // ...some logic
  return await NextAuth(req, context, authOptions)
}
  1. RouteHandlerContext is not exported from the library
  2. https://nextjs.org/docs/app/api-reference/file-conventions/route is really helpful in understanding what is going on, but it is not referenced from https://next-auth.js.org/configuration/initialization#advanced-initialization

@Magimart
Copy link

Magimart commented Feb 8, 2024

I put a solution here just in case someone also faces the same problem as me.

import type { NextApiRequest, NextApiResponse } from "next";
import { cookies } from "next/headers";
import NextAuth from "next-auth";
import { authOptions } from "~/server/auth";

type CombineRequest = Request & NextApiRequest;
type CombineResponse = Response & NextApiResponse;

async function auth(req: CombineRequest, res: CombineResponse) {
 ...some logic
  return await NextAuth(req, res, authOptions);
}

export { auth as GET, auth as POST };

type CombineRequest = Request & NextApiRequest;
type CombineResponse = Response & NextApiResponse;
async function auth(req: CombineRequest, res: CombineResponse){ }
solved the problem with nextjs 13.5.4v and deployed on vercel Node 18x

this is as of now best solution for this issue. Most solutions that recommended like turning strict to false only works in development but not in build or production.

@lifeike
Copy link

lifeike commented Jun 30, 2024

one inconvenience I met, not urgent, however, hope someone could look at in the future.

when i use next-auth v5 in the middleware file, i have some code like this:

const {auth} = nextAuth(authConfig) export default auth((req)=>{//middleware logic})

in this case, the req's type passed into auth function is NextAuthRequest, basically something like this

export interface NextAuthRequest extends NextRequest { auth: Session | null }
added a auth property into NextRequest, however, this NextAuthRequest is not exported, which is not convenient when i want to pass this param to break down pieces of middleware.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

5 participants