I'm new to node and express and I would appreciate any help. I'm trying to write a get request which checks if the req.params.address_line is empty and does something if it is, but can't seem to figure out how to do that. So far I have tried this:
app.get('/smth/smth_else/:address_line', function(req, res){
if(req.params.address_line===""){
res.send("Hello World.")
}
}
This isn't working though so I don't think it is correct. How can I check if the address_line is empty? I googled it extensively, but can't find a working solution. Thanks!
You'll have to check first if the whole req.params exists or not
Like this
app.get('/smth/smth_else/:address_line', function(req, res){
if(!req.params)
return res.send("NO PARAMS PASSED")
if(!req.params.address_line)
return res.send("NO address_line PASSED")
if(req.params.address_line === ""){
res.send("ADDRESS LINE EMPTY.")
} else {
res.send("ADDRESS LINE > ",req.params.address_line)
}
}
A potential option is to write a middleware that could create a nice reusable interface for requiring parameters like so:
const requireParams = params => (req, res, next) => {
const reqParamList = Object.keys(req.params);
const hasAllRequiredParams = params.every(param =>
reqParamList.includes(param)
);
if (!hasAllRequiredParams)
return res
.status(400)
.send(
`The following parameters are all required for this route: ${params.join(", ")}`
);
next();
};
app.get("/some-route", requireParams(["address_line", "zipcode"]), (req, res) => {
const { address_line, zipcode } = req.params;
if (address_line === "") return res.status(400).send("`address_line` must not be an empty string");
// continue your normal request processing...
});
This example makes use of express middlewares and Array.prototype.every(). If you are unfamiliar, these links will provide the relevant documentation for how this works.
You can use a simple falsy check !req.params.address_line for when it is an empty string "", false, null, undefined, 0, or NaN.
There are two cases, you need to handle
For unique route names
app.get('/smth/smth_else/:address_line', function(req, res){
if(!req.params){
res.send("Params Empty!")
}
}
For duplicate route names and multiple params
app.get('/smth/smth_else/:address_line1/:address_line2', function(req, res){
if(Object.keys(req.params).length === 0){
res.send("Params Empty")
}
}
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 wonder how to using path in express dynamically. For example, i'm using lodash for finding a path in different file with regex method.
routes.js
const json = require('./routes.json')
const _ = require('lodash')
routes.use(function(req, res, next) {
let str = req.path
let path = str.split('/')[1]
// [Request] => /test/123
console.log(path)
// [Result] => test
let test = _.find(json.routes, function(item) {
return item.path.match(new RegExp('^/' + path + '*'))
})
console.log(test)
//{"path" : "/test/:id", "target" : "localhost:2018", "message" : "This is Test Response" },
routes.get(test.path, function(req, res) {
res.json("Done")
})
})
On above code, i just nested the routes. But there's nothing any response. Is there any ways to do this? This method also i want to use with DB if necessary. Thanks anyway
Using middleware is impossible. When a request comes, expressjs will search a registered path first.
So here we go why that code not running as well.
For example, I'm as an user request : localhost:2018/test/123
Please following my comment in below
const json = require('./routes.json')
const _ = require('lodash')
routes.use(function(req, res, next) {
let str = req.path
let path = str.split('/')[1]
// [Request] => /test/123
console.log(path)
// [Result] => test
let test = _.find(json.routes, function(item) {
return item.path.match(new RegExp('^/' + path + '*'))
})
console.log(test)
//{"path" : "/test/:id", "target" : "localhost:2018", "message" : "This is Test Response" },
//And now, the routes has been registered by /test/:id.
//But, you never get response because you was hitting the first request and you need a second request for see if that works. But you can't do a second request, this method will reseting again. Correctmeifimwrong
routes.get(test.path, function(req, res) {
res.json("Done")
})
})
How to approach this goal then? However, we need a registering our routes inside app.use or routes.use . So far what i got, we can using loop in here.
//Now, we registering our path into routes.use
_.find(json.routes, function(item) {
routes.use(item.path, function(req, res) {
res.json("Done")
})
})
//The result become
/**
* routes.use('/test:id/', function(req, res, next){
res.json("Done")
})
routes.use('/hi/', function(req, res, next){
res.json("Done")
})
*/
Reference : Building a service API Part 4
Thanks anyway, leave me a comment if there's something wrong with this method :D
When attempting to use req.params.id inside my delete (or update for that matter) route I am getting the above message. This has stumpted me for some time and I'm sure I'm making a mistake somewhere with my routes / objects.
Changing the app from res.render("/campgrounds/ + req.params.id); to -
res.render("/campgrounds"); solves the issue but doesn't reload the same page like i'm look to have it do. I can't wrap my head around why the app is returning undefined when accessing the campground route from req.params.id.
var express= require("express");
var router = express.Router();
var Comment = require("../models/comment");
var Campground = require("../models/campgrounds");
// COMMENTS EDIT ROUTE
router.get("/campgrounds/:id/comments/:comment_id/edit", function(req, res){
Comment.findById(req.params.comment_id, function(err, foundComment){
if(err){
console.log(err)
} else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
})
})
// comment update
//campgrounds/:id/comments/:comment_id
router.put("/:comment_id", function(req, res){
Comment.findByIdAndUpdate(req.params.comment_id, req.body.comment, function(err, updatedComment){
if(err){
console.log(err)
} else {
// KNOWN BUG - /campgrounds/ + req.params.id will return cast to boject failed for value undefined at path _id. having the app redirect to all campgrounds page as a work around
res.redirect("/campgrounds");
}
})
})
// DELETE ROUTER
router.delete("/:comment_id", function(req, res){
Comment.findByIdAndRemove(req.params.comment_id, function(err){
if(err){
res.redirect("back");
} else {
res.redirect("/campgrounds/" + req.params.id);
}
})
})
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
return next();
} else {
res.redirect("/login");
}
}
module.exports = router;
I've ran into the same issue, and it was due to route order. I moved the show route below the index route cuz "it flows better", but that broke the code, and confused it since I think route order matters. make sure your route make sense from the applications point of view if you reordered them
Make sure you have input field name "id" in comment form (or "id" in ajax request).
router.put("/:comment_id", function(req, res){
const id = req.params.id;
console.log(id);
Comment.findByIdAndUpdate(req.params.comment_id, req.body.comment, function(err, updatedComment){
if(err){
console.log(err)
} else {
res.redirect("/campgrounds/" + id);
}
})
})
I think your problem is you are not sending the comment_id from html to controller try printing the req.params.comnent_id
then try this
var express= require("express");
var router = express.Router();
var Comment = require("../models/comment");
var Campground = require("../models/campgrounds");
// COMMENTS EDIT ROUTE
router.get("/campgrounds/:id/comments/:comment_id/edit", function(req, res){
console.log("params.comment_id",params.comment_id);
if(req.params.comment_id){
Comment.findById(req.params.comment_id, function(err, foundComment){
if(err){
console.log(err)
} else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
}else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
})
})
Ok guys, I recently encountered the same problem. I tried finding solutions all over the internet but failed to get any useful answers.
Then I tried looking hard into the "CastError" and found that the ID I am getting from 'req.params.id' had an extra white space in front of it.
For example: instead of '5bed4f6276c4920db404eb25', I got ' 5bed4f6276c4920db404eb25' as the ID. I don't know (yet) why I get the id with that extra white space but I figured that white space must be the issue.
So I stripped the ID for white space with javascript replace function as follows:
var curid = req.params.id;
curid = curid.replace(/\s/g,'');
and it worked!
So instead of
Campground.findByIdAndUpdate(req.params.id, req.body.campground, function(err, updatedCamp){..}
now use:
Campground.findByIdAndUpdate(curid, req.body.campground, function(err, updatedCamp){..}
So,you have to replace all
req.params.id
in your code block with
curid
And you are good to go!
Here is the whole code block for your reference:
router.put("/:id", function(req, res){
var curid = req.params.id;
curid = curid.replace(/\s/g,'');
Campground.findByIdAndUpdate(curid, req.body.campground, function(err, updatedCamp){
if(err){
console.log(err);
res.redirect("/campgrounds");
} else{
//redirect somewhere (show page)
res.redirect("/campgrounds/" + curid);
}
});
The best solution to this problem is reformat the _id by cleaning the blank space it added (in my case coming from a form in my template "x.ejs")
const to_remove = req.body.checkbox;//id from template is wrong
const listname = req.body.check_list_name;
let **to_rem_cured** = to_remove.replace(/\s/g,'');
List.findOneAndUpdate({name:listname},{'$pull':{list:{_id: **to_rem_cured** }}},function(err,doc){
if(!err){
console.log(doc);
res.redirect("/"+listname);
}
else{
console.log(err);
}
});
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);