node.js modify response and store request at the same time - node.js

So what I'm trying to do is to manualy overwrite response when there is a request to my json-server. I'm fine with that, but I dont know how to add a bit to also store the original request in database.
Here is my code (i'm sending as response the name from request and static uuid)
The next(); in if step is failing complaining it cannot setup headers.
module.exports = (req, res, next) => {
if (req.path == "/business") {
res.status(201);
res.jsonp({
id: "a23e1b13-cf69-461c-aa8a-a0eb99e41350",
name: req.body['name'],
revision: "1"
});
next();
}
else {
next();
}
}

If you want to pass data from middleware to any routes you can assign value with req object rather than res object.
app.use(function(req.res)) {
req.custom_data = {
id: "a23e1b13-cf69-461c-aa8a-a0eb99e41350",
name: req.body['name'],
revision: "1"
};
next();
}
From other location you can do like this.
module.exports = (req, res, next) => {
res.jsonp(req.custom_data)//something like this
}
It might help you.

You are right.
For example, we cannot:
server.js
const jsonServer = require("json-server")
const server = jsonServer.create()
const router = jsonServer.router("db.json")
server.use(jsonServer.defaults())
server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
if (req.method === "POST" && req.url === "/business") {
res.status(201)
res.jsonp({
id: "a23e1b13-cf69-461c-aa8a-a0eb99e41350",
name: req.body["name"],
revision: "1"
})
}
next()
})
server.use(router)
server.listen(3000, () => {
console.log("JSON Server is running")
})
Or we get: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client when we run node server.js, because we have already set and sent the headers when we call res.jsonp, so we cannot do this again by calling server.use(router).
We have a couple options.
Option 1.
server.js
const jsonServer = require("json-server")
const server = jsonServer.create()
const router = jsonServer.router("db.json")
server.use(jsonServer.defaults())
server.use(router)
server.listen(3000, () => {
console.log("JSON Server is running")
})
db.json
{
"business": []
}
Now we can POST to our http://localhost:3000/business endpoint and save our request body to our database. However, we cannot amend the 201 status, nor the request body to something else.
Option 2.
server.js
const jsonServer = require("json-server")
const server = jsonServer.create()
server.use(jsonServer.defaults())
server.use(jsonServer.bodyParser)
server.post("/business", (req, res, next) => {
res.status(201)
res.jsonp({
id: "a23e1b13-cf69-461c-aa8a-a0eb99e41350",
name: req.body["name"],
revision: "1"
})
next()
})
server.listen(3000, () => {
console.log("JSON Server is running")
})
Now we get the correct response, but we can no longer save to our database business key, because we are calling server.post("/business"...
You could however use this in your own code and save it somewhere else (or write to your own database), as this response will be returned from a Promise.

Related

Caching in an app which consumes and serves an API

I don't know if this is the best place to ask but.
I am building a weather app which consumes an api using axios and then serves it using express. I wanted to know where should I add caching to improve the speed of the api? Would it be at the axios layer when I am consuming or at the express layer when I am serving.
Below is my code for a little context
import { weatherApiKey } from 'config';
import axios from 'axios';
const forecast = (location, service) => {
console.log('inside api calling location: ', location);
axios.get(`http://api.openweathermap.org/data/2.5/weather?q=${location}&appid=${weatherApiKey}`)
.then(res => {
service(undefined, res.data)
})
.catch(err => {
service('Error calling weather API');
})
}
module.exports = forecast;
I am then serving the consumed api via the following.
app.get('/weather', (req, res) => {
const locale = req.query.locale;
if(!locale) {
return res.send({
error: 'Please provide valid locale'
})
}
foreCast(locale, (err, weatherData) => {
if(err) {
console.log('error in calling weather API')
res.send({err});
}
console.log('returning weather data', weatherData)
res.send({weatherData})
});
})
Yes, generally there are lots of forms and layers to cache at. Given the fact that you're creating an API, I would expect some caching to be applied as close to the consumer as possible. This could be at the CDN level. However, a quick easy answer would be to add something as a cacheable middleware for your express app.
There are many approaches to populating and invalidating caches, and you take care to plan these specifically for your use case. Try not to prematurely optimise using caching where you can. It introduces complexity, dependencies and can contribute towards hard-to-debug problems when there are lots of caching layers being applied.
But a simple example would be something like:
'use strict'
var express = require('express');
var app = express();
var mcache = require('memory-cache');
app.set('view engine', 'jade');
var cache = (duration) => {
return (req, res, next) => {
let key = '__express__' + req.originalUrl || req.url
let cachedBody = mcache.get(key)
if (cachedBody) {
res.send(cachedBody)
return
} else {
res.sendResponse = res.send
res.send = (body) => {
mcache.put(key, body, duration * 1000);
res.sendResponse(body)
}
next()
}
}
}
app.get('/', cache(10), (req, res) => {
setTimeout(() => {
res.render('index', { title: 'Hey', message: 'Hello there', date: new Date()})
}, 5000) //setTimeout was used to simulate a slow processing request
})
app.get('/user/:id', cache(10), (req, res) => {
setTimeout(() => {
if (req.params.id == 1) {
res.json({ id: 1, name: "John"})
} else if (req.params.id == 2) {
res.json({ id: 2, name: "Bob"})
} else if (req.params.id == 3) {
res.json({ id: 3, name: "Stuart"})
}
}, 3000) //setTimeout was used to simulate a slow processing request
})
app.use((req, res) => {
res.status(404).send('') //not found
})
app.listen(process.env.PORT, function () {
console.log(`Example app listening on port ${process.env.PORT}!`)
})
Note: This is taken from https://medium.com/the-node-js-collection/simple-server-side-cache-for-express-js-with-node-js-45ff296ca0f0 using the memory-cache npm package.

How to mock req.session in express while testing API with jest and supertest?

What I'm trying to do is to test a REST API endpoint that validates requests with req.session (set by express-session).
Here are snippets of my code:
middleware.ts
export const validateSecurityCode = (req, res, next) => {
// How do I mock/spy on this "req.session.securityCode"?
if (req.session.securityCode !== req.body.securityCode) {
throw new BadRequestError('Wrong security code was provided');
}
return next();
};
api/item.ts
router.post('/item', [validateSecurityCode], async (req, res) => {
await createItem(req.body);
res.send({ message: 'DONE' });
});
As you can see, if req.session.securityCode !== req.body.securityCode, this endpoint has to throw an error.
Is it possible to mock/spy on req.session while testing the endpoint with jest and supurtest?
Below is what I'd like to achieve:
it('should pass security code check', async () => {
// " MOCK_REQ_SESSION('valid-code')" will ensure that "req.session.securityCode" will get 'valid-code'
validateSecurityCode = MOCK_REQ_SESSION('valid-code');
const response = await request(app).post('/api/item').send({
securityCode: 'valid-code',
// other request body values
});
expect(response.statusCode).toEqual(200);
});

Location API not returning location id to server

I'm following an tutorial where i am supposed to consume an mongoose API using express and display location information to the view. However I am getting an 404 error on the front end everytime i click the button that calls the API request. The source code for this tutorial can be found here. https://github.com/cliveharber/gettingMean-2/tree/chapter-07
Here is the API request handler on the server side:
const getLocationInfo = (req, res, callback) => {
const path = `/api/locations/${req.params.locationid}`;
const requestOptions = {
url: `${apiOptions.server}${path}`,
method: 'GET',
json: {}
};
request(
requestOptions,
(err, {statusCode}, body) => {
const data = body;
if (statusCode === 200) {
data.coords = {
lng: body.coords[0],
lat: body.coords[1]
}
callback(req, res, data);
} else {
showError(req, res, statusCode);
}
}
);
};
const locationInfo = (req, res) => {
getLocationInfo(req, res,
(req, res, responseData) => renderDetailPage(req, res, responseData)
);
};
The Render handler of the page i am trying to reach:
const renderDetailPage = (req, res, location) => {
res.render('location-info',
{
title: location.name,
pageHeader: {
title: location.name,
},
sidebar: {
context: 'is on Loc8r because it has accessible wifi and space to sit down with your laptop and get some work done.',
callToAction: 'If you\'ve been and you like it - or if you don\'t - please leave a review to help other people just like you.'
},
location
}
);
};
This is the Mongoose API controller that retrieves the location data from the database:
const locationsReadOne = (req, res) => {
Loc
.findById(req.params.locationid)
.exec((err, location) => {
if (!location) {
return res
.status(404)
.json({
"message": "location not found"
});
} else if (err) {
return res
.status(404)
.json(err);
}
res
.status(200)
.json(location);
});
};
This is the express routing code:
const express = require('express');
const router = express.Router();
const ctrlLocations = require('../controllers/locations');
const ctrlOthers = require('../controllers/others');
router.get('/', ctrlLocations.homelist);
router.get('/locations/:location._id', ctrlLocations.locationInfo);
And finally this is a snippet of the Pug code that contains the button which calls the get request to retrieve the location id:
each location in locations
.card
.card-block
h4
a(href=`/locations/${location_id}`)= location.name
+outputRating(location.rating)
span.badge.badge-pill.badge-default.float-right= location.distance
p.address= location.address
.facilities
each facility in location.facilities
span.badge.badge-warning= facility
Let me know if i need to provide additional information. Appreciate any help.

How to send constant value along with api get request to node server

I have a api service in which there is a method to fetch data from the mongo db through node server. But i want to send the value of const userplant = localStorage.getItem("userplant"); along with the get request to the GET router in my node server so i can filter the data with a WHERE condition.
API.SERVICE.TS
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
return this.http.get(this.localURL + 'storagelocation/view', {headers:this.appHeader});
}
ROUTER.JS
router.get('/storagelocation/view', auth, function(req, res, next) {
StorageLocation.find({plantId : "5dd262a61120910d94326cc1"}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
I want the value of userplant next to the {plantId : "here"}
P.S.: My get request is perfectly working , i just want to send the const value along with it...
You can use queryParams to do that:
API.SERVICE.TS
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
return this.http.get(this.localURL + 'storagelocation/view' + '?userplant=' + userplant , {headers:this.appHeader});
}
ROUTER.JS
router.get('/storagelocation/view', auth, function(req, res, next) {
let userplant = req.query.userplant;
// you can use it now
StorageLocation.find({plantId : "5dd262a61120910d94326cc1"}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
Make the following changes in Angular, you can pass headers and parameters like this:
const httpOptions = {
headers: { 'Content-Type': 'application/json' },
params: {userplant:userplant}
};
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant");
return this.http.get(this.localURL + 'storagelocation/view',httpOptions);
}
Make the following change in Node Js,you have to use body parser.
router.get('/storagelocation/view', auth, function(req, res, next) {
var plantId=req.body.plantId;
StorageLocation.find({plantId : plantId}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
In query params you can send.
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
const params = new HttpParams().set('userplant ',userplant);
return this.http.get(this.localURL + 'storagelocation/view', {headers:this.appHeader, params: params});
}

Express: Show value from external request as response

I have the following function where I am using the cryptocompare npm package:
getPrice: function(coin){
cc.price(coin, 'USD')
.then(prices => {
console.log(prices);
return prices;
}).catch(console.error)
}
// https://github.com/markusdanek/crypto-api/blob/master/server/helper/cryptocompare.js
Now I want to set up an Express server to open http://localhost:9000/current and to display the current "price".
So I have my controller which looks like this:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price);
}
};
// https://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.jshttps://github.com/markusdanek/crypto-api/blob/master/server/controllers/CryptoController.js
My route:
var controllers = require('../controllers'),
app = require('express').Router();
module.exports = function(app) {
app.get('/current', controllers.crypto.getCurrentPrice);
};
When I open now http://localhost:9000/current I only get the current price in my console, but not in my browser.
How can I also set the response to the value?
I tried this but failed:
module.exports = {
getCurrentPrice: function(req, res, next) {
getPrice('ETH', function(price){
res.status(200).json(price);
});
}
};
I guess thats the wrong way to call a callback.. do I have to modify my helper function or anything else?
My project is also on Github for further references: https://github.com/markusdanek/crypto-api
below may help you
module.exports = {
getCurrentPrice: function(req, res, next) {
cc.price('ETH', 'USD')
.then(prices => {
console.log(prices);
res.json(prices)
})
.catch(err=>{
console.error(err)
return next(err);
})
}
};

Resources