request.body is appearing as a string
Answered
Giant panda posted this in #help-forum
Giant pandaOP
I am sending form data via the fetch api (post request) to a page API route.
When I read the value of the body i get the following string
how can i convert this to an JSON object so i can validate the content with ZOD
const formData = new FormData(event.currentTarget);
const response = await fetch('/api/jobUpload', {
method: 'POST',
body: formData,
});export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method, body } = req;
await dbConnect();
}When I read the value of the body i get the following string
'------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="title"\r\n\r\ntester\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="datetime"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="occupation"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="description"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox--\r\n'how can i convert this to an JSON object so i can validate the content with ZOD
Answered by joulev
try this code
import { type Fields, type Files, formidable } from "formidable";
import type { NextApiHandler } from "next";
import type { IncomingMessage } from "node:http";
const form = formidable({ multiples: true });
function parseForm(req: IncomingMessage) {
return new Promise<{ fields: Fields; files: Files }>((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
resolve({ fields, files });
});
});
}
const handler: NextApiHandler = async (req, res) => {
const { fields } = await parseForm(req);
console.log(fields);
res.status(200).send("Ok");
};
export const config = {
api: {
bodyParser: false,
},
};
export default handler;20 Replies
Silver Marten
Use something similar to this:
const jobSchema = z.object({
title: z.string(),
datetime: z.date(),
occupation: z.string(),
description: z.string(),
});
const MyForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = async (data) => {
// Validate the form data against jobSchema
try {
const validatedData = jobSchema.parse(data);
// If validation is successful, you can send the data to your API
// Replace this with your API call
console.log('Valid data:', validatedData);
} catch (error) {
console.error('Validation error:', error);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>Title:</label>
<input {...register('title')} />
{errors.title && <span>{errors.title.message}</span>}
<label>Date and Time:</label>
<input type="datetime-local" {...register('datetime')} />
{errors.datetime && <span>{errors.datetime.message} (Please enter a valid date and time)</span>}
<label>Occupation:</label>
<input {...register('occupation')} />
{errors.occupation && <span>{errors.occupation.message}</span>}
<label>Description:</label>
<textarea {...register('description')} />
{errors.description && <span>{errors.description.message}</span>}
<button type="submit">Submit</button>
</form>
)and in your api you wanna make sure to pass the schema as well:
import {jobSchema} from "@/schemas/path/to/jobschema"
export async function POST(req: NextRequest, res) {
try {
const formData = jobSchema.parse(req.body);
// handle the data using formData
} catch (error) {
return NextResponse.json({message: "Invalid schema"}, {status: 500}}
}
}@Giant panda I am sending form data via the fetch api (post request) to a page API route.
const formData = new FormData(event.currentTarget);
const response = await fetch('/api/jobUpload', {
method: 'POST',
body: formData,
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method, body } = req;
await dbConnect();
}
When I read the value of the body i get the following string
'------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="title"\r\n\r\ntester\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="datetime"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="occupation"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox\r\nContent-Disposition: form-data; name="description"\r\n\r\n\r\n------WebKitFormBoundaryUB3bmQCSu0puH8ox--\r\n'
how can i convert this to an JSON object so i can validate the content with ZOD
use something like [multer](https://www.npmjs.com/package/multer) or [formidable](https://www.npmjs.com/package/formidable) to parse formData in pages router api routes
Giant pandaOP
nice thanks
got to convert to use the api router for the NextRequest
@Giant panda got to convert to use the api router for the NextRequest
if you use the app router then you can use [
req.formData](https://nextjs.org/docs/app/building-your-application/routing/route-handlers#request-body-formdata) directly, but of course migrating to the app router is not required to parse form dataGiant pandaOP
your amazing, ill keep you updated if i run into any troubles
thanks
@joulev use something like [multer](<https://www.npmjs.com/package/multer>) or [formidable](<https://www.npmjs.com/package/formidable>) to parse formData in pages router api routes
these libraries require you to turn off
bodyParser, check [here](https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config)Giant pandaOP
hey ive turned off bodyparser and left the pages router still up (until a new pr)
but its freezing when formidable tries to parse the request object
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
await dbConnect();
switch (method) {
case 'POST':
try {
const form = formidable();
const [fields] = await form.parse(req)this is the request object:
Just FYI, i tried changing the typescript type to NextResponse and NextRequest, but it still froze in the same place
@Giant panda but its freezing when formidable tries to parse the request object
try this code
import { type Fields, type Files, formidable } from "formidable";
import type { NextApiHandler } from "next";
import type { IncomingMessage } from "node:http";
const form = formidable({ multiples: true });
function parseForm(req: IncomingMessage) {
return new Promise<{ fields: Fields; files: Files }>((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
resolve({ fields, files });
});
});
}
const handler: NextApiHandler = async (req, res) => {
const { fields } = await parseForm(req);
console.log(fields);
res.status(200).send("Ok");
};
export const config = {
api: {
bodyParser: false,
},
};
export default handler;Answer
Giant pandaOP
Nice, I didn’t realise you had to export the config inside the api route
I was putting it in next config
Works great
that
parseForm is among the functions i made once and never tried to understand it again, i just copy paste whenever i need it