Next.js Discord

Discord Forum

Passing user data from the middleware.ts to an api route handler

Unanswered
Dwarf Crocodile posted this in #help-forum
Open in Discord
Dwarf CrocodileOP
Started here and moved to the forum -> https://discord.com/channels/752553802359505017/752647196419031042/1144181819919581255

I was guided to use the cookie map
const makeAccountCookie = (accountCookie: IAccountCookie): ResponseCookie => {
  return {
    name: "ts-account",
    value: btoa(JSON.stringify(accountCookie)),
    httpOnly: true,
    path: "/",
    maxAge: 86_400,
  };
};

/** Get the current trade state from cookie storage
 *
 * @function getAccountCookie
 * @returns {IAccountCookie} - TradeCookie
 */
export const getAccountCookie = (): IAccountCookie => {
  const accountCookie = cookies().get("ts-account");
  if (accountCookie) {
    // TODO::Add value validation
    const cookie = JSON.parse(atob(accountCookie.value));
    if (process.env.IMPERSONATE === "true" && getAddressSafe(process.env.IMPERSONATE_ADDRESS) && cookie.loggedIn) {
      cookie.address = process.env.IMPERSONATE_ADDRESS;
    }
    return cookie;
  }

  return DefaultAccountCookie;
};

I assumed that the example was referencing the cookies that is imported from next/headers.
Unfortunately when that's called in the middleware.ts I am getting the following error:
- error Invariant: cookies() expects to have requestAsyncStorage, none available.


I tried to use the available req.cookies instead, but found that it was empty when attempting to retrieve it in the api route.

Any insights would be appreciated!

6 Replies

Show me your middleware file
Dwarf CrocodileOP
right now it's in a state where I attempted to pass it as a string
import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import { plainToInstance } from 'class-transformer'
import { ValidationError, validate } from 'class-validator'
import { SendCodeDto } from './app/api/verifyPhone/sendCode/dto'
import { HttpStatus } from './constants/http-status.constant'
import { IJwtPayload, verifyJwtToken } from './utils/auth'
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies'

async function checkToken(cookies: RequestCookies): Promise<IJwtPayload | undefined> {
    // const { cookies } = req
    const token = cookies.get('token')?.value
    if (!token) return undefined
    const payload = await verifyJwtToken(token)
    if (!payload) return undefined
    return payload
}

export async function middleware(req: NextRequest) {
    const { method, nextUrl, headers } = req
    const { pathname } = nextUrl

    // const response = NextResponse.next()

    const authed = await checkToken(req.cookies)
    if (authed) {
        cookies().set('custName', authed.custName)
        cookies().set('custDesc', authed.custDesc)
        cookies().set('phoneNumber', authed.phoneNumber)
    }

    // if (authed) {
    //     response.headers.set('X-CUSTNAME', Buffer.from(authed.custName).toString('base64'))
    //     if (authed.custDesc) {
    //         response.headers.set('X-CUSTDESC', Buffer.from(authed.custDesc).toString('base64'))
    //     }
    // }

    if (headers.get('Accept')?.includes('text/html') && method === 'GET') {
        if (!authed && pathname !== '/auth') {
            const url = nextUrl.clone()
            url.pathname = '/auth'
            return NextResponse.redirect(url)
        } else if (authed && (pathname === '/auth' || pathname === '/')) {
            const url = nextUrl.clone()
            url.pathname = '/in'
            return NextResponse.redirect(url)
        }

        // return response
    }

    // if (headers.get('Accept')?.includes('application/javascript') && method === 'GET') {
    //     return response
    // }

    if (method === 'POST') {
        let errors: ValidationError[] | undefined = undefined

        if (!pathname.startsWith('/api/verifyPhone') && !authed) {
            return NextResponse.json({ error: 'Unauthorized' }, { status: HttpStatus.Unauthorized })
        }

        if (method === 'POST') {
            if (pathname === '/api/logout') return

            try {
                const body = await req.json()

                switch (pathname) {
                    case '/api/verifyPhone/sendCode':
                        errors = await checkError(SendCodeDto, body)
                        break
                    case '/api/verifyPhone/verifyCode':
                        errors = await checkError(SendCodeDto, body)
                        break
                }

                if (errors && errors.length > 0) {
                    return NextResponse.json({ errors }, { status: HttpStatus.BadRequest })
                }

                return NextResponse.next({
                    ...req,
                })
            } catch {
                // empty body
            }
        }
    }
}

export function checkError(type: any, data: any): Promise<ValidationError[]> {
    return validate(plainToInstance(type, data), { skipMissingProperties: true })
}

(same result as described above)
Dwarf CrocodileOP
😅
@Dwarf Crocodile 😅
i dont see you setting cookies on anything
i see you cloning responses and sending it with zero cookie modification
const response = NextResponse.next();
response.cookies.set(asdfasdfasdf)