I want to slightly change default expressjs behaviour of res.json(obj) method. I am trying to override it in my own middleware, the thing is I need to call its original inside.
But now it just calls itself causing a stack overflow.
app.use(function(req, res, next) {
res.json = function(obj) {
function delete_null_properties(obj) {
// ...
}
delete_null_properties(obj);
res.json(obj);
};
next();
});
I don't know the inner workings of express very well, but it seems something like this should work
app.use(function(req, res, next) {
var json = res.json;
res.json = function(obj) {
function delete_null_properties(obj) {
// ...
}
delete_null_properties(obj);
json.call(this, obj);
};
next();
});
edit: changed json(obj) to json.call(this, obj) as per comment by user3537411 and this previous answer to a similar question
P.S. I started the answer with I don't know the inner workings of express very well to avoid the sort of comments that just put crap on an answer without really going into WHY an answer is bad ... instead I get the sort of comment that's equally pointless. You can't win with SO trolls
You can do like below, after const app = express(); dont use this.json and dont use this.send inside the function otherwise you will get a maximum call size error :
app.response.json = function(body: any) {
this.contentType('json').end(JSON.stringify(
{
code: ApiCode.OPERATION_SUCCES,
message: CommonMessages.OperationSuccess.message,
data: body,
description: CommonMessages.OperationSuccess.description
}
));
return this;
}
It also might be useful
https://github.com/muratcorlu/connect-api-mocker/pull/30
Mounting twice will apply only last one.
const express = require('../../node_modules/express');
const app = express();
// default response
app.use('/', (req, res, next) => {
next();
try {
res.send({
profile: {
first_name: 'Aaron',
last_name: 'Pol'
}
});
} catch (e) {
//
}
});
// definite state, where default response can be changed
app.use('/', (req, res) => {
res.send({
profile: {
first_name: 'John',
last_name: 'Pol'
}
});
});
app.listen(9090);
Related
This is straight out of the express-validator documentation. I noticed that when these functions are passed as middleware, they include arguments and parenthesis, in which case they should be called at runtime right?
// ...rest of the initial code omitted for simplicity.
const { body, validationResult } = require('express-validator');
app.post(
'/user',
// username must be an email
body('username').isEmail(),
// password must be at least 5 chars long
body('password').isLength({ min: 5 }),
(req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
User.create({
username: req.body.username,
password: req.body.password,
}).then(user => res.json(user));
},
);
I jumped into the source code to try and figure out how they are preventing the function calls, but it is a little over my head. The reason I wanted to learn about this was I was interested in creating a middleware that worked in a similar fashion, where arguments could be passed without actually calling the function at runtime.
I'm not going to reverse engineer some specific code, but will explain how to achieve this in general.
See the documentation for middleware for reference.
An endpoint is a function that takes two arguments. The request and the response. They are typically named req and res.
Middleware takes three arguments. The third is next which is called to pass control to the next function.
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
Now, middleware doesn't have to pass control to the next function. It can just respond.
const middleware = (req, res, next) => {
if (typeof req.body?.username === 'undefined') {
// No username was provided
res.send("Error: No username was provided");
} else {
next();
}
}
Now you might want this to be reusable for arguments other than username, so you can write a factory function which returns the middleware function.
const createMiddleware = (propertyName) => {
const middleware = (req, res, next) => {
if (typeof req.body?.[propertyName] === 'undefined') {
// No value was provided for the propertyName
res.send(`Error: No ${propertyName} was provided`);
} else {
next();
}
}
return middleware;
}
And then use it:
app.use( createMiddleware('username') );
app.use( createMiddleware('password') );
I was trying to make a routes for each ID I using a forEach loop but It stay loading until timeout reaches, all expected values are in place, all good but the second route is not running, I was fighting it despretly until now. I made sure there is a problem.
server.js
const router = require('express').Router();
function isAuthorized(req, res, next) {
if (req.user) {
next();
}
else {
res.redirect('/login')
}
}
let myguild = [];
router.get(`*`, isAuthorized, (req, res) => {
res.status(200);
console.log("wow");
console.log(req.user.guilds.length)
req.user.guilds.forEach(guild => {
myguild.push(guild);
})
console.log("Finished");
myguild.forEach(guild => {
console.log('Started')
router.get(guild.id, (req, res) => { // here is the problem
console.log("uh")
res.send("HAMBURGER")
console.log(req, res, guild)
})
console.log("Outed")
})
});
module.exports = router;
output:
wow
23
Finished
Started
Outed
Started
Outed
Started
Outed
Star... 'there is more but this is enough'
It should behave and run within server/${guild.id} but got (failed) request
Any Ideas?
You might need to redesign the API to better fit what you're trying to accomplish. If you already know which guilds are available then you'd need to create those before the server is initialized.
Even if they come from a database or are dynamic, you can loop through the guild "options" and create endpoints then provide access to them only if the user is qualified.
const { guilds } = require('./config')
const guildHandler = (req, res) => {
// Assuming you're doing more here
res.send('Hamburger')
}
guilds.forEach(guild => router.get(`/guilds/${guildId}`, guildHandler)
Or if you are NOT doingg something different in the middleware for each guild then you could just have a single route for guild.
router.get('/guilds/:guildId, guildHandler)
Not really sure what you're trying to accomplish but checkout out the Express docs. They solve most use cases fairly easily.
https://expressjs.com/en/api.html#req
You never call res.end() from your outer res.get() handler, so the request never completes.
And, with respect, creating route handlers like that in a loop is a mistake. It will lead to real performance trouble when your app gets thousands of guilds.
You'll want to use just one route, with a named route parameter, something like this.
const createError = require('http-errors')
router.get(':guildid', isAuthorized, (req, res, next) => {
const guildid = req.params.guildid
if (req.user.guilds.includes(guild)) {
console.log("uh")
res.send("HAMBURGER").end()
console.log(req, res, guildid)
} else {
next(createError(404, guildId + ' not found'))
}
})
Thanks for everyone helped.
Inspired answer
Final Result:
server.js
router.get('/:guildid', isAuthorized, (req, res, next) => {
console.log('started')
const guildid = req.params.guildid
if (req.user.guilds.some(guild => guild.id === guildid)) {
console.log('uh')
res.send("HAMBURGER").end()
} else {
res.sendStatus(404);
}
})
I'm new to node/express. I have a simple express app with one post route ('/'). Given a list of GitHub users names, it should return information about those developers.
For example, if I post
{ "developers": ["JohnSmith", "JaneDoe"] }
It should return
[
{
"bio": "Software Engineer at Google",
"name": "John Smith"
},
{
"bio": "Product Manager, Microsoft",
"name": "Jane Doe"
}
]
This is what I have so far
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/', function(req, res, next) {
try {
let results = req.body.developers.map(async d => {
return await axios.get(`https://api.github.com/users/${d}`);
});
let out = results.map(r => ({ name: r.data.name, bio: r.data.bio }));
return res.send(out);
} catch {
next(err);
}
});
app.use((err, req, res, next) => {
res.send("There was an error");
})
app.listen(3000, function(){
console.log("Server started on port 3000!");
});
When I try this in a tool like Insomnia, I keep getting There was an error.
You can use res.formate() as per this docs...
Or set type for res as per this Docs...
res.type('json')
From middleware, you are always saying res.json("there is some error"), why are you keeping it there if not needed. if you remove app.use(). you will see the request is returning you names of the developer
read this for error handling.
https://expressjs.com/en/guide/error-handling.html
The way you return JSON in an Express API is as follows:
res.status(200).json(out)
See https://expressjs.com/it/api.html for the full documentation.
You're getting There was an error. Because, there is an error in your / route. But, the thing is you haven't wrote the err parameter with catch. If add that one in your catch (of your try/catch block) and console.log it, you'll see the error
TypeError: Cannot read property 'name' of undefined
You're getting this error because, results variable is actually an array of pending Promise. You need to first resolve these pending promises. One way to do is,
app.post("/", function (req, res, next) {
try {
const developers = req.body.developers;
let promises = [];
developers.map((d) => {
promises.push(axios.get(`https://api.github.com/users/${d}`));
});
Promise.all(promises).then((responses) => {
let out = responses.map((r) => ({
name: r.data.name,
bio: r.data.bio,
}));
return res.send(out);
});
} catch (err) {
next(err);
}
});
I am not really sure what to title this, but I'm new to Node.js. I just found a neat REST API project on GitHub to implement but I'm not sure how I can split all GET and POST etc. to separate files.
I have one singular api.js file where I have
function API_ROUTER(router, connection, md5) {
var self = this;
self.handleRoutes(router, connection, md5);
}
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
router.get("/", function(req, res) {
res.json({"Message" : "Hello World !"});
});
};
module.exports = API_ROUTER;
Now how can I create a sibling other.js and use:
var api = require('./api.js');
// Create router.get, router.post etc. here?
but I'm not sure how I can split all GET and POST etc. to separate files.
One way you can organize your routes would be to have a separate object for each route that has the handlers (separated by HTTP methods) and other needed info such as the path:
api/home.js
module.exports = {
path: '/',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Hello World !"});
},
'post': {
// ...
}
// ...
}
}
api/other.js
module.exports = {
path: '/other',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Other !"});
},
// ...
Then you can load all of these inside the handleRoutes method:
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
var routes = ['home', 'other'];
routes.forEach(function(name) {
// load the current route object (NOTE: you should use the path module for determining file path in a cross-platform manner)
var routeObject = require('./' + name + '.js');
var apiPath = routeObject.path;
var handlers = routeObject.handlers;
var methods = Object.keys(handlers);
// assign handlers for each method
methods.forEach(function(method) {
router[method](apiPath, handlers[method]);
});
});
};
This will install all your routes with the appropriate information and handlers.
Now you can call this code by instantiating your API_ROUTER with the necessary data:
// initialize the api (and handle the routes internally)
var Api = new require('./api.js')(router, connection, md5);
If you implement a RESTful API, then you should keep in mind that this is just one way how you can provide data, and you might want to change it in future, as of that the API will most of the time only be a translation layer.
Normally you will split your code based on the resources, and the code that is handling the request won't have so much logic, it will just take the request and pass it to you internal API. For that purpose you not really need an additional layer if you already use express.js or a similar library.
In express the app.use([path,] function [, function...]), already provides the functionality you would need to modularize your code. For each resource your will create an own express.Router that itself also might mount another sub module. So for this part you do not really need a library.
When might a library be useful:
if it automatically translates thrown errors to the correct response codes
if it includes a tool to automatically create a documentation to your API
if it fully abstracts the underlaying routing system so that you can hook into express, hapi, ... without the need to change the code.
Here how a setup with express.js could look like
./lib/rest/customer.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.params.id
}, function(err, customer) {
if (err) {
res.status( /*correct status code*/ ).send( /*depending on the api return json, xml, ....*/ )
} else {
res.send( /*depending on the api return json, xml, ....*/ )
}
})
});
router.delete('/:id', function(req, res, next) {
customerSystem.delete({
id: req.params.id
}, function(err) {
//...
});
});
router.post('/', function(req, res, next) {
//...
});
//save the customer id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.customerId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;
./lib/rest/customer-address.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.customerId
}, function(err, customer) {
// ...
})
});
/* ..... */
//save the address id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.addressId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;
I'm trying to avoid callback hell by breaking down my Express / Kraken.js controller into smaller callback functions.
I was processing a request and had about 6 levels of nested anonymous callback functions.
so now I have my main function that looks like this:
// ugh, I know this isn't right
var globalProducts = {};
module.exports = function (server) {
server.get('/project', function (req, res) {
var data = req.query;
globalData = data;
if(!data.projectId || !data.ruleSetId)
res.json({error: "Incomplete input data."});
// pass response to products call back
Project.findOne({ _id: mongoose.Types.ObjectId(data.projectId) }, setUpProducts);
});
};
function setUpProducts(err, project){
// get all products and pass them down the pipe
project.findAllChildren(setUpRules);
}
function setUpRules(err, products) {
// we need to access products in another function
globalProducts = products;
// find the rule set and build the rule Flow
RuleSet.findOne({ _id: mongoose.Types.ObjectId(globalData.ruleSetId) }, function(err, ruleSet) {
ruleSet.buildFlow(processFlow);
});
}
My question is what is the best way to pass around info between callbacks ? My solution was var globalProducts = {}; but to me, the controller contain any 'global state' .. whats the best way to handle this ?
Doing this is a bad idea. It will cause race condition type issue — basically it's the same as sharing data in multithreaded environment. Instead you can use req or res to store data. To do that you need them in scope, so you can define all functions inside route handler or make each function a middleware so it will have req and res as arguments. Here is an example of this approach:
function check (req, res, next) {
if(!req.query.projectId || !req.query.ruleSetId) return res.json({error: "Incomplete input data."});
next()
}
function findProject (req, res, next) {
Project.findOne({ _id: mongoose.Types.ObjectId(req.query.projectId) }, after);
function after (err, project) {
if (err) return next(err);
req.project = project;
next();
}
}
function findProducts (req, res, next) {
req.project.findAllChildren(after)
function after (err, products) {
if (err) return next(err);
req.products = products;
next();
}
}
function respond (req, res) {
res.render('view', {
products : req.products,
project : req.project
});
}
module.exports = function (server) {
server.get('/project', check, findProject, findProducts, respond);
};