I have this patch request where I update certain information depending on object of a document. When I send request using Postman(form-data) I get validation error saying "_id" is required.
Here's how it looks in Postman,
The route looks similar to this,
router.patch("/update", async (req, res) => {
try{
await updateValidation(req.body);
// whatever stuff processed with the data
} catch (err) {
res.status(400).send({ message: err.details[0].message });
}
}
The validation function looks like this,
const updateValidation = (data) => {
const schema = Joi.object({
_id: Joi.string().required(),
// other whatever validation possible
});
return schema.validateAsync(data);
};
Am I missing something here? I think I do, please point it out.
Besides using var bodyParser = require('body-parser'); in server side, it will still return empty req.body that will lead to errors because you have validation in place. The reason it is returning empty req.body when you sending PATCH request using form-data in Postman is because body-parser can't handle multipart/form-data. You need a package that can handle multipart/form-data like multer.
at the first to install the body-parser and multer, go to your terminal and use −
npm install --save body-parser multer
so add this code to in server.js
var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
var app = express();
at the next time use this middelware:
// for parsing application/json
app.use(bodyParser.json());
// for parsing application/xwww-
app.use(bodyParser.urlencoded({ extended: true }));
//form-urlencoded
// for parsing multipart/form-data
app.use(upload.array());
app.use(express.static('public'));
After importing the body parser and multer, we will use the body-parser for parsing json and x-www-form-urlencoded header requests, while we will use multer for parsing multipart/form-data.
Related
Something that I wanted clarification on is when console.logging req.body in my express app. I end up with a buffer/string or hexadecimal of some sort when I'm expecting an JSON object. I'm using postman to send a raw json body.
Here are some visuals of the source code and terminal/postman results.
const express = require('express');
const bodyParser = require('body-parser');
const { randomBytes } = require('crypto');
const app = express();
app.use(express.raw({type: "application/json"}));
app.use(express.json({strict: false}));
app.use(express.urlencoded({extended: false}));
const posts = {};
app.get('/posts', (req, res) => {
res.send(posts);
});
app.post('/posts', (req, res) => {
const id = randomBytes(4).toString('hex');
const { title } = req.body;
console.log(req.body)
posts[id] = {
id,
title: title
};
res.status(201).send(posts[id]);
});
app.listen(4000, () => {
console.log('Listening on 4000');
});
Console.log terminal of buffer/hex/string
Postman body
Postman Raw String
app.use(express.raw({type: "application/json"})); is going to give you a Buffer object and since that's your first middleware that might handle that mime type, that's what you're going to get for any application/json request.
Right from the Express doc for express.raw():
This is a built-in middleware function in Express. It parses incoming
request payloads into a Buffer
It's unclear why you're using express.raw() as that is not typical for JSON payloads, but when doing so, you are going to get a Buffer - that's how it works. One would more typically use express.json() for JSON payloads and let it parse your JSON so that req.body contains an actual Javascript object.
If you remove the app.use(express.raw({type: "application/json"})); line of code and let the app.use(express.json()); line of code right after it handle the application/json payloads, then you will get your parsed data in req.body.
Keep in mind that when using middleware they are processed in the order declared and for this specific type of middleware, the first one that matches and reads the body from the incoming stream takes precedence and none of the others after it will be able to do their job (since the incoming stream has already been read).
I can test my models fine with AVA, but I would like to also test the routes.
I feel it should be possible to access the Express app object, and pass it a URL, then see what comes back, but I do not know how to get the Express object to use it.
After some playing around and referencing the supertest repo, I was able to get the following working:
const test = require("ava");
const request = require("supertest");
const express = require("express");
test("test express handler with supertest", async t => {
// in async tests, it's useful to declare the number of
// assertions this test contains
t.plan(3);
// define (or import) your express app here
const app = express();
app.get("/", (req, res) => {
res.json({
message: "Hello, World!",
});
});
// make a request with supertest
const res = await request(app).get("/").send();
// make assertions on the response
t.is(res.ok, true);
t.is(res.type, "application/json");
t.like(res.body, {
message: "Hello, World!",
});
});
I tend to run AVA with the following shell command:
yarn ava --verbose --watch
I have a very simple user backend up and running (node, express, mongoose, mongo, etc) and with postman can verify when I add a user it works, when I request a login it works and get a token, and if I put in the wrong details it rejects it,
Now I used this git hub repo https://github.com/christiannwamba/vue-auth-vuex to spin up a simple frontend for this. Which I thought was all working fine as it appeared to be logging in until I found it was accepting whatever details I put in for the email and password as correct!
The backend server kept responding ok when I hit it with the vue app, but on closer inspection when I console logged what it was getting, which was null and returning user not found. So again I don't think there is anything wrong here.
Something I have noticed in chrome dev tools network, it is sending two versions of authenticate, first is empty and then the next one has responses.
I'm at a bit of a loss why it's sending empty requests first time and why it allows the login when it's getting a bad return.
Server.js file:
const express = require('express');
const logger = require('morgan');
const movies = require('./routes/movies') ;
const users = require('./routes/users');
const bodyParser = require('body-parser');
const mongoose = require('./config/database'); //database configuration
var jwt = require('jsonwebtoken');
var cors = require('cors')
const app = express();
// Add cors
app.use(cors());
app.options('*', cors()); // enable pre-flight
app.set('secretKey', 'nodeRestApi'); // jwt secret token
// connection to mongodb
mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:'));
app.use(logger('dev'));
app.use(bodyParser.urlencoded({extended: false}));
app.get('/', function(req, res){
res.json({"api" : "User API"});
});
// public route
app.use('/users', users);
// private route
app.use('/movies', validateUser, movies);
app.get('/favicon.ico', function(req, res) {
res.sendStatus(204);
});
function validateUser(req, res, next) {
jwt.verify(req.headers['x-access-token'], req.app.get('secretKey'), function(err, decoded) {
if (err) {
res.json({status:"error", message: err.message, data:null});
}else{
// add user id to request
req.body.userId = decoded.id;
next();
}
});
}
// express doesn't consider not found 404 as an error so we need to handle 404 it explicitly
// handle 404 error
app.use(function(req, res, next) {
let err = new Error('Not Found');
err.status = 404;
next(err);
});
// handle errors
app.use(function(err, req, res, next) {
console.log(err);
if(err.status === 404)
res.status(404).json({message: "Not found"});
else
res.status(500).json({message: "Something looks wrong :( !!!"});
});
app.listen(3000, function(){
console.log('Node server listening on port 3000');
});
Update:
I have added in under my CORS bit in server.js:
app.options('/users/authenticate', function(req, res){
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST');
res.end();
});
In network I now only get the one request. Under form data it appears to be there but it's saying in the response that data is null, and even more odd the vuejs is still logging in and allowing access to the restricted pages.
Temporarily comment out the line where you set the headers to application/x-www-form-urlencoded. Then add app.use(bodyParser.json()) to your server.js and see if it works. What's happening is your request object is malformed, which is why the server cannot parse the request correctly.
Looks like CORS issue. If you run UI using a different server and your backend is running by itself, then your browser will send pre-flight request first which is an options request. That is the reason you see 2 authenticate requests in the developer tools. You can read more about this here
Why is an OPTIONS request sent and can I disable it?
I am using an Angular front-end with a Nodejs backend. Im currently proxying all my front-end requests through my express server. However when I make my http request to the Here API I am rejected due to an invalid combination of app_id and app_code.
angular service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { HttpParams } from '#angular/common/http'
#Injectable({
providedIn: 'root'
})
export class GetReqPlaces {
constructor(private http: HttpClient) { }
getPlaces(wLong,sLat,eLong,nLat){
// let obj = {params: {westLong: wLong, southLat: sLat, eastLong:eLong, northLat:nLat }};
let params = new HttpParams().set("westLong" , '-97.783').set("southLat", '30.231').set( "eastLong" , '-97.740').set("northLat", '30.329');
return this.http.get( 'api/find/places', { params : params}).subscribe(res=>console.log(res))
}
}
server.js
const express = require("express")
const bodyParser = require("body-parser")
const cors = require("cors")
const path = require("path")
const app = express();
const request = require("request")
const environment= require('./keys')
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
let reqPath = __dirname.substring(0,__dirname.length-7)
app.use(express.static(path.join(reqPath, '/dist/angular-places-search')));
app.get('/api/find/places', (req, res) => {
let appId = environment.environment.appId;
let appCode = environment.environment.appCode;
let URL= `https://places.cit.api.here.com/places/v1/discover/search?app_id={${appId}}&app_code={${appCode}}&in=${req.query.westLong},${req.query.southLat},${req.query.eastLong},${req.query.northLat}&pretty`;
console.log(URL)
request(URL, function (error, response, body) {
let data={
body:body,
};
console.log(error,response)
res.send(data);
});
});
app.get('/test', (req, res) => res.send('Well this route was a hit! Bada....tsss'));
// CATCH ALL
app.get('*', (req, res) => {
res.sendFile(path.join(reqPath, 'dist/angular-places-search/index.html'));
});
app.listen(4000, () => console.log(`Express server running on port 4000`));
Before this I was running into CORS and request issues but I think I sorted those out. Based on my research on this same error code (In the context of the framework that Im working in), people overwhelmingly suggest to wait for tokens to register with Here API. Waiting two days is enough I think, still doesnt work. Then there is the very popular solution of just scratching the Here freemium and starting a new project, which I did, and which did not solve my issue. Very few things I have 100% certainty on but I did copy my keys correctly and the URL path built is according to the required Here syntax.
If anyone has any insight you will be my Hero, and also the catalyst for my continued learning :D. Happy Sunday!
In addition the incoming message I get through express is :
method: 'GET',
path: '/places/v1/discover/search?app_id=%notmyid%7D&app_code=%normycode%7D&in=-97.783,30.231,-97.740,30.329&pretty'
However i dont know why it is setting the app_id=% instead of using {}, when i console log the URL it is correct, with my app_id and app_code
The %7D is the url encoded value of the symbol } (urlencoding) which is done by most libraries. For using the HERE API you should not enclose the app_id/app_code between {}. They should be provided directly as strings, check the examples
Since the last Postman update, I'm unable to get the response data in the console, from the request body that I'm sending to the endpoint.
Here's the code:
let express = require('express');
let app = express();
let bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/', (req, res) => {
console.log(req.body);
res.send('OK');
});
app.listen(8080, () => console.log('App is listening! :)'));
Then I make a POST request using Postman putting data in the Body part, On the log I get "{}".
I have tested with cURL, and it works, that's why I suspect it is a problem with Postman.
Thank you for your time!
Using the code from the question, I am able to see the request body logged out to the console. This is using either the raw > application/json option or the x-www-form-urlencoded option the send the request.
In order to see data from the form-data option in Postman, I needed to add the multer module to the code.
let express = require('express');
let multer = require('multer');
let upload = multer();
let app = express();
let bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/', upload.array(), (req, res) => {
console.log(req.body);
res.send('OK');
});
app.listen(8080, () => console.log('App is listening! :)'));
As you can see from the image below, this is writing the request body to the console.