Next.js Discord

Discord Forum

Serverless API route rejects with 401 only when called from Vercel edge function

Answered
Jersey Wooly posted this in #help-forum
Open in Discord
Jersey WoolyOP
On Next v13.4.12 and I'm hitting an issue where an edge function deployed to Vercel calls into a serverless API function and the serverless API call gets rejected with a 401 before it ever calls into the function body. Any ideas?

Tap button
-> POST to /api/bug/edge
-> within edge, POST to /api/bug/serverless -> rejected with 401

If I do:
Tap button
-> POST to /api/bug/serverless it works fine

There is zero auth / middleware involved, and I've tried CORS with no restrictions.

## Repro:

Deploy the following to Vercel (since it all works fine locally) and see how it'll throw the 500 error when the serverless function is called indirectly via the edge fn.

Create an /api/bug/edge.ts file with:

import { env } from '@/utils/misc'
import cors from '../stream/cors'

export const runtime = 'edge'

export default async function POST(req: Request): Promise<Response> {
  const isFnCall = req.method === 'OPTIONS'
  console.log('req method', req.method)
  if (!isFnCall && req.method != 'POST') {
    console.log('Not allowed ze method.')
    return cors(
      req,
      new Response('Method not allowed', {
        status: 400,
        headers: {
          Allow: 'POST',
        },
      }),
    )
  }

  console.log('calling serverless route')
  const response = await fetch(env.getApiUrl('/api/bug/serverless'), {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ why_do: 'you hate me' }),
  })

  if (!response.ok) {
    console.log('response was not okay, call help')
    return cors(
      req,
      new Response('Oh noz', {
        status: 500,
        statusText: 'Oh noz',
      }),
    )
  }

  const data = await response.json()
  console.log('data is', data)

  return cors(
    req,
    new Response(data, {
      status: 200,
    }),
  )
}


Create an /api/bug/serverless.ts file with:

import { NextApiRequest, NextApiResponse } from 'next'

// Note: I've tried wrapping this in a CORS handler as well
export default async function POST(req: NextApiRequest, res: NextApiResponse) {
  console.log('Serverless function called.')
  const { why_do } = req.body
  console.log('why_do', why_do)
  res.status(200).json({ message: 'Success' })
}


Add this to next.config.js

async headers() {
    return [
      {
        // matching all API routes
        source: '/api/:path*',
        headers: [
          { key: 'Access-Control-Allow-Credentials', value: 'true' },
          { key: 'Access-Control-Allow-Origin', value: '*' },
          {
            key: 'Access-Control-Allow-Methods',
            value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT',
          },
          {
            key: 'Access-Control-Allow-Headers',
            value:
              'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version',
          },
        ],
      },
    ]
  },


Hook up a button to call them like:

<Button
label="Call the routes"
onClick={async () => {
  await callDirectly() // this works locally & remotely on Vercel
  await callViaEdge() // this doesn't work remotely, but works locally
}}
/>

// calling code
const callDirectly = async () => {
  const response = await fetch(env.getApiUrl('/api/bug/serverless'), {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ why_do: 'you hate me too' }),
  })
  if (!response.ok) {
    console.error('Error calling /api/bug/serverless', response)
  } else {
    console.log('Called directly')
  }
}

const callViaEdge = async () => {
  const response = await fetch(env.getApiUrl('/api/bug/edge'), {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({}),
  })
  if (!response.ok) {
    console.error('Error calling /api/bug/edge', response)
  } else {
    console.log('Called via edge')
  }
}
Answered by Jersey Wooly
🤔 Found the issue... had Vercel Deploy Protection enabled on the preview env and it was preventing the edge function from calling into the serverless function even though they were deployed within the same app.

🐛 Feels like a bug on Vercel infra side
View full answer

1 Reply

Jersey WoolyOP
🤔 Found the issue... had Vercel Deploy Protection enabled on the preview env and it was preventing the edge function from calling into the serverless function even though they were deployed within the same app.

🐛 Feels like a bug on Vercel infra side
Answer