next.jsnext.js13

Next.js treating pages/API route as a client component?


I have a simple API route, src/pages/api/me.ts that simply echoes if the user is logged in or not.

import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from '../../lib/session'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
    let user = getSession();
    res.status(200).end((user) ? 'yes' : 'no')
}

The import, ../../lib/session (`src/lib/session.ts):

import { cookies } from 'next/headers';
import jwt from 'jsonwebtoken'

export const getSession = () => {
    const nxtCookies = cookies();

    if (nxtCookies.has('wp_session')) {
        const cookie = nxtCookies.get('wp_session');

        // prints on the server
        console.log('this is happening on the server')

        let sessionData = jwt.verify(cookie.value, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
            if (err) return false;
            return user;
        });

        if (sessionData) return sessionData;
    }

    return false;
}

When I try to call getSession() from pages/api/me.ts, I get an error:

./src/lib/session.ts

You're importing a component that needs next/headers. That only works in a Server Component but one of its parents is marked with "use client", so it's a Client Component.

import { cookies } from 'next/headers'; :

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

One of these is marked as a client entry with "use client":
src\lib\session.ts src\pages\api\me.ts

How is this possible? Both are server-sided code.

I even have a server component that uses getSession() to display user information on the website, and this error is not thrown. I even verified via console.log that within both that component and getSession(), that the console prints to the server console. So I am not sure how this is possible.

Specifically, the issue here seems to be the cookie import from next/headers.


Solution

  • I believe cookies from next/headers is only for use in server components. src/pages/api/me.ts is on the server but is not a server component.

    You can access the cookies from a request through req.cookies, and pass it to your getSession function.

    A possible implementation of this would be:

    // src/pages/api/me.ts
    import type { NextApiRequest, NextApiResponse } from 'next';
    import { getSession } from '../../lib/session'
    
    export default function handler(req: NextApiRequest, res: NextApiResponse) {
      const user = getSession(req.cookies);
      res.status(200).end((user) ? 'yes' : 'no');
    }
    
    // src/lib/session.ts
    import jwt from 'jsonwebtoken';
    
    export const getSession = (cookies: Partial<Record<string, string>>) => {
      const session = cookies.wp_session;
    
      if (session) {
        // prints on the server
        console.log('this is happening on the server');
    
        const sessionData = jwt.verify(session, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
          if (err) return false;
          return user;
        });
    
        return sessionData;
      }
    }