How to send FormData in MERN Stack project with TS - node.js

I'm trying to send FormData to my backend but when I console.log the req.body it's empty object and I don't know why.
Here is my frontend request:
const createProduct = (e: any) => {
e.preventDefault();
const data = new FormData()
data.append("name", name)
data.append("description", description)
data.append("price", price)
for (const colorAndImage of colorsAndImages) {
data.append('images', colorAndImage.images[0]);
data.append('colors', colorAndImage.colors);
}
data.append("type", type)
fetch('http://localhost:4000/products/create', {
method: 'POST',
body: data
})
Here is how the image file looks like in the console:
File {name: 'iphone_13_pro_max_gold_pdp_image_position-1a__wwen_5.jpg', lastModified: 1642862621798, lastModifiedDate: Sat Jan 22 2022 16:43:41 GMT+0200 (Eastern European Standard Time), webkitRelativePath: '', size: 22194, …}
Here is what I'm sending in the request in Network tab:
name: sdf
description: sdf
price: 234
images: (binary)
colors: red
type: sdf
Here is the controller in backend side:
productController.post('/create', async (req: Request, res: Response) => {
console.log(req)
try {
const data = req.body;
let product = await create(data)
res.status(201).json(product)
} catch (error) {
console.log(error);
//res.status(500).json({error: error})
}
})
And what I see when I try to console.log the request:
{
name: undefined,
description: undefined,
price: undefined,
colors: undefined,
images: undefined,
type: undefined,
likes: undefined
}
Error: Product validation failed: name: Path `name` is required., description: Path `description` is required., price: Path `price` is required., type: Path `type` is required.
My express config:
const corsConfig: cors.CorsOptions = {
credentials: true,
origin: ['http://localhost:3000', 'http://localhost:2000']
}
export default function (app: Application) {
app.use(cors(corsConfig))
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));
app.use(express.json())
app.use(auth())
}

Not sure why are you using FormData in sending raw data from frontend to backend.
FormData is generally used when you have to send (upload) files. For simple data you can just send JSON object.
Express by default can't parse multipart/form-data, you will have to install a middleware like multer in order to get the data or you can update your data structure in frontend.
let dataToSend = {
name: name,
description: description,
price: price
// Rest of the properties
}

Related

How to upload images from remix to sanity.io?

I am trying to upload images from a remix app to sanity.io from a server action where I am sending data through form submit using encode type multipart/formData.
I cannot understand how to parse the file correctly from formData to send to sanity.
I have looked at unstable_parseMultipartFormData and am using the following code for the main upload:
const imageAsset = await sanityClient.assets.upload(
'image',
// <not sure what to put here>,
{
contentType: profilePic.type,
filename: `${name}-profile-pic`,
}
);
const sanityDoc = {
_type: 'profilePic',
image: {
_type: 'image',
asset: {
_type: 'reference',
_ref: imageAsset._id,
},
},
name,
email,
isOrganisation: false,
};
await sanityClient.create(sanityDoc);
Thanks for the help!

Logging axios request and response headers

We are needing log the HTTP request that axios will send to the remote host, along with the response. This data will be stored in a database for a number of days before being automatically pruned.
The lifecycle we are looking at for the logging:
// creates the session that joins both the request and response
function createSession (url: string): string;
// log the request message as sent
function logRequest (sessionId: string, headers: Record<string, any>, body: Record<string, any> | string);
// log the request message as received
function logResponse (sessionId: string, headers: Record<string, any>, body: Record<string, any> | string);
// close the session, recording the final status and any error message
function closeSession(sessionId: string, status: number, error?: string);
We have looked at the request and response interceptors, but the issue we are having is that the request interceptor is before axios adds all the headers is going to send and the response interceptor doesn't seem to have access to the request, to be able to correlate the request and response.
Without needing to channel all this through a proxy to do the logging, how would you suggest doing this with axios?
Here's what I usually do:
In the request interceptor, I use some UUID library (or maybe the crypto core module of node) to generate a UUID, then attach it to the config object as a request ID, say config.reqId. Same config object should be accessible in response.config, or if an error occurs, in error.response.config and I can get the reqId from there. Then, if you have some script to parse the logs, you can correlate the request and response using this ID.
The disadvantage here is that, yes, the accurate req headers may not be logged.
Also, if you're just looking for the request object in response, then they should be accessible in response.request, going from what I checked in the axios docs. You can try that out.
const axios = require("axios");
const getNewUuid = require("./someUuidGeneratorUtilFunction.js");
const logger = require('./yourChoiceOfLogger.js');
const config = {
BASE_URL: "https://your.baseurl.here"
}
const myAxiosInstance = axios.create({
baseURL: config.BASE_URL,
timeout: 30000,
})
myAxiosInstance.interceptors.request.use((config) => {
const customUuid = getNewUuid();
config.reqId = customUuid;
const message = {
reqId: customUuid,
time: Date.now(),
config: config
}
logger.info(message)
return config;
})
myAxiosInstance.interceptors.response.use((response) => {
const customUuid = response.config && response.config.reqId ? response.config.reqId : "";
const message = {
reqId: customUuid,
time: Date.now(),
status: response.status,
data: response.data,
headers: response.headers,
logMessage: "RESPONSE RECEIVED"
}
logger.info(message)
return response;
},(error) => {
const customUuid = error.response && error.response.config && error.response.config.reqId ? error.response.config.reqId : "";
const errorResponse = error.response ? error.response : {
status: null,
data: null,
headers: null
}
const message = {
reqId: customUuid,
time: Date.now(),
status: errorResponse.status,
data: errorResponse.data,
headers: errorResponse.headers,
logMessage: error.message || "ERROR"
}
logger.error(message)
return Promise.reject(error)
})
module.exports = myAxiosInstance;

Set params on request when testing hapi routes

I am currently writing some tests for our hapi routes. The route I want to test looks like that:
server.route(
{
method: 'POST',
path: '/',
options: {
tags: ['api'],
cors: true,
handler: async (req: Hapi.Request | any, h: Hapi.ResponseObject) => {
if (!req.params.userId) {
throw Boom.badRequest();
}
return 200;
}
}});
So my test looks like this:
it('should return 200', async () => {
const request : ServerInjectOptions = {
url: '/user',
method: 'POST',
payload: {
email: 'e#email.de',
password: 'secred',
firstname: 'John',
lastname: 'Doe'
},
app: {}
};
const response = await server.inject(request);
expect(response.statusCode).toEqual(200);
});
As you can see the route expects a param in the params array with the name userId but i am not able to set the parameter on the ServerInjectOptions object. The error I get is that the property does not exist on the ServerInjectOptions type.
Is there any other way i can set the params array? I didn`t find something in the docs maybe i missed it and someone can tell me where to find it.
Thanks in advance
For the route I believe you add the name of the parameter to the path like so:
server.route(
{
method: 'POST',
path: '/:userId',
//
}});
And for the test you should be able to add your parameter to the url option:
const request : ServerInjectOptions = {
url: '/user/parameterYouNeedToAdd',
//
};
Or if the parameter is a variable:
const request : ServerInjectOptions = {
url: '/user/' + parameterYouNeedToAdd,
//
};

Node breaks JSON string in URL on 'equal' sign =

I am receiving user object from FB API on a React client (localhost:3000).
Then i'm sending it to my node server (localhost:4000)
When the object is received on the node route and i console log it, it SPLITS any value that has '=' sign to ': ' (colon and space). That's breaking my JSON object.
original object being sent from client: (problem is in imageUrl key equal signs)
userInfo {
about: "abcde"
age: 26
email: "test#test.com"
fbProfileLink: "www.some-profile-link-here.com"
gender: "male"
id: null
imageUrl: "https://url-url.url.url/platform/profilepic/?asid=3422352323&height=50&width=50&ext=3422352323&hash=hash"
language: {mainLang: "", speaksEnglish: true}
name: "abcde"
residence: "abcde"
}
This is how i get it in the node server: (turns to 'asid': ...)
"imageUrl":"https://url-url.url.url/url/profilepic/?asid': '3422352323',
height: '50',
width: '50',
ext: '3422352323',
hash: 'hash'
router function:
router.post("/post-test", (req, res, next) => {
console.log("hi amit POST")
console.dir(req.body)
res.end()
})
request sending from client:
axios.post("http://localhost:4000/post-test", user, {
headers: {"Content-Type": "application/x-www-form-urlencoded"}
})
.then(res => {})
.catch(err => {
console.log(err)
})
its like its breaking the string to query params but i just want it as a string
axios.post("http://localhost:4000/post-test", user, {
headers: {"Content-Type": "application/x-www-form-urlencoded"}
})
Here, you are telling the server that you are sending form-urlencoded data. URL-encoded data follows the key=value&key2=value2 format. The server sees the Content-Type header and tries to parse your JSON as URL-encoded data.
Change the Content-Type to the JSON one:
axios.post("http://localhost:4000/post-test", user, {
headers: {"Content-Type": "application/json"}
})
You are sending an incorrect header which is causing the server to parse the values incorrectly.
Change
{"Content-Type": "application/x-www-form-urlencoded"}
to
{"Content-Type": "application/json"}
I'm Assuming you're using express with body parser.
In order to make it work with json, consider bootstrapping your app in the following way:
Server
const express = require("express")
const bodyParser = require('body-parser')
const app = express();
app.use(bodyParser.json());
Client
axios.post("http://localhost:4000/post-test", user, {
headers: {"Content-Type": "application/json"}
});
Payload
{
"about": "abcde",
"age": 26,
"email": "test#test.com",
"fbProfileLink": "www.some-profile-link-here.com",
"gender": "male",
"id": null,
"imageUrl": "https://url-url.url.url/platform/profilepic/?asid=3422352323&height=50&width=50&ext=3422352323&hash=hash",
"language": {
"mainLang": "",
"speaksEnglish": true
},
"name": "abcde",
"residence": "abcde"
}
Result
hi amit POST
{ about: 'abcde',
age: 26,
email: 'test#test.com',
fbProfileLink: 'www.some-profile-link-here.com',
gender: 'male',
id: null,
imageUrl:
'https://url-url.url.url/platform/profilepic/?asid=3422352323&height=50&width=50&ext=3422352323&hash=hash',
language: { mainLang: '', speaksEnglish: true },
name: 'abcde',
residence: 'abcde' }

(Angular4 / MEAN) Sending Request to Local API Results in Empty Body

I'm trying to post data to an Items API, for instance:
"data": {
"title": "stack",
"notes": "asdsad",
"time": "19:02",
"meridian": "PM",
"type": "Education",
"_id": "5a2f02d3bba3640337bc92c9",
"days": {
"Monday": true,
"Tuesday": false,
"Wednesday": false,
"Thursday": false,
"Friday": false,
"Saturday": false,
"Sunday": false
}
}
However, when using HttpClient to post the data
this.http.post("http://localhost:3000/api/items",
JSON.stringify(item))
.subscribe(
(val) => {
console.log("POST call successful value returned in body",
val);
},
response => {
console.log("POST call in error", response);
},
() => {
console.log("The POST observable is now completed.");
});
I always get the Bad Request 400 response from the Items Controller in the API.
exports.createItem = async function(req, res, next){
// Req.Body contains the form submit values.
console.log(req);
console.log(req.body);
var item = {
id: req.body.id,
title: req.body.title,
days: req.body.days,
notes: req.body.notes,
time: req.body.time,
meridian: req.body.meridian,
type: req.body.type,
completed: req.body.completed,
}
try{
// Calling the Service function with the new object from the Request Body
var createdItem = await ItemService.createItem(item)
return res.status(201).json({status: 201, data: createdItem, message: "Succesfully Created Item"})
} catch(e){
console.log(e);
//Return an Error Response Message with Code and the Error Message.
return res.status(400).json({status: 400, message: "Item Creation was Unsuccesfull"})
}
}
I have already set up BodyParser in my app.js as so
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
In the express console, the output is as follows
_startTime: 2017-12-11T22:35:18.204Z,
_remoteAddress: '::1',
body: {},
secret: undefined,
cookies: {},
signedCookies: {},
route:
Route {
path: '/',
stack: [ [Object], [Object] ],
methods: { post: true } } }
{}
As you can see the body is empty and this is preventing an item from being created. I have gone ahead and tested this using Postman and when sending url encoded form data and raw JSON data the posts are successful and return 200. However, I can never get the application's request to work.
Any help is appreciated as I have been struggling with this for several hours now.
this.http.post("http://localhost:3000/api/items",
JSON.stringify(item) -----> convert JSON object to string
).subscribe(...);
Based on your server side code, I believe that it expects JSON object other than a JSON string, therefore remove JSON.stringify(item)) from your client side, the body of your POST should be JSON object.

Resources