SOLVED / SEE MY OWN ANSWER FOR DETAILS
Hey I am getting this very strange error and I can’t seem to be able to figure it out. So basically ‘m trying to send an audio recording that the user records in my Nextjs Frontend, to my Nextjs API. Which seems to be going well. But, when I try to forward the entire recording from my Nextjs api to my actual backend endpoint which is running Node/Express.js on Vercel, I end up getting a 405 bad request error. Any ideas?
IMPORTANT NOTE: Everything worked fine on localhost. But not when I took it to production on Vercel. (Both client and server are Node apps running on Vercel. One is Nextjs, a slightly older version, and the server is Express)
I am using Next.js as an API in the middle for security reasons
My only theory so far is that "formData.append" is not working properly as whenever i try to log it or its entires out, i always get a blank object: {}
Here:
const formDataToSend = new FormData()
formDataToSend.append(fileName, new Blob([readResult], { type: 'audio/wav' }), `${fileName}.wav`)
(full code given down below)
Nextjs version: 13.5.4
Expressjs version: 4.18.2
EXPRESS.JS CODE:
app.use(cors())
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/')
},
filename: function (req, file, cb) {
cb(null, `${file.originalname}`)
}
})
const upload = multer({ storage: storage })
...
app.post('/upload', [requireSession, upload.any()], async (req, res) =>
{
// the request never even gets here in the first place
// requireSession works fine
})
NEXT.JS API:
import { csrf } from '../../../../lib/csrf'
import axios from 'axios'
import crypto from 'crypto'
import multiparty from 'multiparty'
import fs from 'fs'
// valid url
const ENDPOINT_URL = 'https://my-api.com/upload'
// works fine
async function readAudioFile(filePath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(filePath)
const chunks = []
readStream.on('data', (chunk) => {
chunks.push(chunk)
})
readStream.on('end', () => {
const fileBuffer = Buffer.concat(chunks)
resolve(fileBuffer)
})
readStream.on('error', (err) => {
reject(err)
})
})
}
async function post(req, res)
{
try {
// works fine
const form = new multiparty.Form()
// works fine
const formData = await new Promise((resolve, reject) => {
form.parse(req, function (err, fields, files) {
if (err) reject({ err })
resolve({ fields, files })
})
})
// works fine
const readResult = await readAudioFile(formData.files.audio[0].path)
// works fine
const randomizer = `${req?.headers?._uuid}-${req?.query?.taskId}-${(new Date().getTime()).toString(16)}`
// works fine
const fileName = crypto.createHash('sha256').update(randomizer).digest('hex')
// works fine
const formDataToSend = new FormData()
// !! Not sure. when i log em out, "formDataToSend" and "formDataToSend.entries()" are both empty. (Just a "{}")
formDataToSend.append(fileName, new Blob([readResult], { type: 'audio/wav' }), `${fileName}.wav`)
// Nothing undefined here, works fine
const headers = {
'client': process.env.API_ACCESS_KEY,
'_session': req.headers._session,
'_uuid': req.headers._uuid,
...req.headers,
'Content-Type': 'multipart/form-data',
'Request-Index': '0' ,
}
// this request fails with code 405
await axios.post(ENDPOINT_URL, formDataToSend, { headers, params: { ...req.query } })
.then(response => (res.status(200).json(response.data)))
.catch(error => (console.log(error), res.status(500).json({
...getErrorMessage(),
errorDetails: error,
dataToSend
})))
}
catch (error) {
console.log(error)
res.status(501).json({
...getErrorMessage(),
errorDetails: error,
dataToSend
})
}
}
async function handler(req, res)
{
switch (req.method.toLowerCase())
{
case 'post':
await post(req, res)
break
default:
res.status(200).send(`Method ${req.method} is not allowed.`)
break
}
}
const getErrorMessage = () => { return { time: Date.now(), result: false, status: 500, message: 'Something went wrong' } }
export default csrf(handler)
export const config = {
api: {
bodyParser: false
}
}
I’ve already tried pretty much everything i can think of. Including using fetch instead of axios, messed with the CORS settings of my express.js endpoint, etc. I think the problem is in FormData somehow. Worked fine in localhost though
3
Answers
Thanks for the help guys. Here's how I solved it:
changed this:
to this:
Apparently I forgot to remove a single line of code that was interfering with the request headers. Just realized what was going on by coincidence after wasting a couple nights tryna get this thing to work. Silly me!
Try After Changing This
To This
Since, Vercel is Serverless computing provider. I think you should try to deploy your web application to other platforms. e.g Railway
You can also read this: https://vercel.com/guides/using-express-with-vercel
Let me know if this works.