How to Use Multiple Route in ExpressJS? - express

Is it possible to make router in ExpressJS like this?
users.js
const userController = ('../controllers/userController.js');
router.get('/:userName', userController.paramByUsername);
router.get('/:id', userController.paramByUserId);
In the controller, the code look like this
userController.js
function paramByUsername(req, res) {
User.findOne({
where: {
userId: req.params.userId
}
})
.then((user) => {
if(!user) {
return res.status(404).json({ message: "User not found."});
}
return res.status(200).json(user);
})
.catch((error) => {
return res.status(400).json(error);
});
}
function paramByUserId(req, res) {
User.findByPk(req.params.id)
.then((user) => {
if(!user) {
return res.status(404).json({ message: "User not found."});
}
}).catch((error) => {
return res.status(400).json(error);
});
}
By the code above, what I wanted to achieve is the endpoint like this:
/users/1 this should response same as /users/username.
I have tried the code above, but what I see is an error when I get /users/:id

You can't do both of these together:
router.get('/:userName', userController.paramByUsername);
router.get('/:id', userController.paramByUserId);
From a pure routing point of view, there is no way to tell the difference between these two. Whichever route you declare first will grab everything at the top level and the second will never get hit.
So, in route design, you HAVE to make sure that each route is uniquely recognizable to the Express route matcher based on what you put in the route pattern.
I suppose that if an id was always just numbers and a username could never be just numbers, then you could use a regex route and match only numbers for the id and everything else for the username, but that seems a bit fragile to me and I'd prefer something a bit more explicit.
I don't know the overall landscape of your app, but you may want to do:
router.get('/user/:userName', userController.paramByUsername);
router.get('/id/:id', userController.paramByUserId);
Or, you could use the query string with URLs like this:
/search?user=John
/search?id=4889
And, then you'd just have one route:
router.get("/search", ....);
And you would examine which properties are present in req.query to decide which item you were looking for.

Related

express-validator on PUT methods

I'm creating an API and decided to use express-validator for validation (duh), I've never used this before so I'm unsure on some aspects of it so my validations might not be the best but I'm getting by.
I have built two validation middle ware using this and export them from the same folder like this:
module.exports = {
create: require('./create'),
update: require('./update')
}
So I can then do this in my router:
const validation = require('../validations/plotValidation')
// ...
router.get('/', controller.all)
router.post('/create', validation.create(), controller.create)
router.get('/:plotId', controller.read)
router.put('/:plotId/update', validation.update(), controller.update)
router.delete('/:plotId/delete', controller.delete)
// ...
I'm not good enough with express-validator to do both validate both routes with the same file, maybe I'll try it at some point, anyway.
The .post method works fine and validates everything I want it to however the .put method just seems to be ignoring every check here are is file in case you want to see the checks:
const { body, check, param } = require('express-validator');
module.exports = () => {
return [
param('plotId')
.exists().withMessage('URI requires plot id'),
body('price')
.optional()
.isObject()
]
}
As you can probably tell I only just stated it, but even with only these two tiny checks it just doesn't seem to run.
Does express-validator not work on PUT methods?
For anyone else who has this issue I solved this by using .run on my checks, you can read more about this here essentially this is the code that saved me:
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
};
};

Best way to retrieve and filter a directus collection inside a custom endpoint?

I would need to search an item inside a collection and then work on that item.
What is the correct way to query a collection inside a custom endpoint?
Should i use the API or do you provide any classes?
I tried to follow the current guide https://github.com/directus/docs/blob/master/api/data.md
but i get a 403 (Forbidden) error.
I came around the same problem and now working with directus for some time. I asked the same question at the discussion section but the example in the guidebook should help:
export default (router, { services, exceptions }) => {
const { ItemsService } = services;
const { ServiceUnavailableException } = exceptions;
router.get('/', (req, res, next) => {
const recipeService = new ItemsService('recipes', { schema: req.schema, accountability: req.accountability });
recipeService
.readByQuery({ sort: ['name'], fields: ['*'] })
.then((results) => res.json(results))
.catch((error) => {
return next(new ServiceUnavailableException(error.message));
});
});
};
Maybe it is a bit late but hopefully it inspires others. Directus is from my first experiences now really great and worth to try.

Async Await with Mongoose Returns Empty Object

I have my mongoose schema for a user's profile where they can add work experience (currently an array of objects in schema).
I have used the following code to find the user's profile and get the experience object as input then attach it to the array in schema and return the saved profile with experience:
Router.post('/experience',
Passport.authenticate('jwt', {session: false}), async (req, res) => {
try {
const myProfile = await Profile.findOne({user: req.user._id});
if (myProfile) {
const exp = {
title: req.body.title,
company: req.body.company,
location: req.body.location,
from: req.body.from,
to: req.body.to,
isCurrent: req.body.isCurrent,
description: req.body.description
};
// add to Profile experience array
Profile.experience.unshift(exp); // adds to beginning of array
const savedProfile = await Profile.save(); // have also tried myProfile.save() but that doesn't work too
if (savedProfile) {
console.log(savedProfile);
res.json({message: `Profile Updated Successfully`, details: savedProfile})
}
else { throw `Experience Details Not Saved`}
}
} catch (err) { res.json(err); }
});
The problem here is that the response is always an empty object and when I check my database, there is no experience saved. Is this code wrong? Same thing works with Promises but I want to try a new way of doing things.
The async-await pattern is another way to write Promise, the return value of the function is Promise.resolve(result) or Promise.reject(reason) of the whole async.
In the outer function, Router.post in this case, it has to use async-await, or then of Promise pattern, to deal with the returned Promise. Orelse, the async function would not have chance to run, as the returned Promise would be omitted.

Restify: Set default formatter

Also asked in official Restify repo: #1224
Hi,
Is it possible to have one default formatter that can handle any accept type that is not defined.
For Example:
restify.createServer({
formatters: {
'application/json': () => {},
// All other requests that come in are handled by this, instead of throwing error
'application/every-thing-else': () => {}
}
});
By all appearances, this is impossible. Since the formatters are stored in a dictionary, there is no way to create a key that matches every input (that would kind of defeat the point of a dictionary anyway...) The only way to accomplish this kind of thing outside of JSON would be with a regular expression, and regular expressions don't work with JSON.
Here is a program I wrote to test this.
var restify = require("restify");
var server = restify.createServer({
formatters: {
'application/json': () => { console.log("JSON") },
"[\w\W]*": () => { console.log("Everything else") } // Does not work
}
});
server.get("/", (req, res, next) => {
console.log("Root");
res.setHeader("Content-Type", "not/supported");
res.send(200, {"message": "this is a test"});
next()
});
server.listen(10000);
Also here is a link to the documentation on this in case you can find some hint that I couldn't see.
Restify documentation

Auth Filter Not Working

Trying to use auth filter to keep users from accessing certain routes without being logged in. The code below redirects regardless of the user's status. I was unable to find anywhere what to put within the function. I'm using http://laravel.com/docs/security#protecting-routes for reference. Not sure if I should have an if statement or not. Not sure what to do at all.
Route:
Route::get('/account', ['before' => 'auth', function()
{
// continue to /account
}]);
Standard 'auth' filter from app/filters:
Route::filter('auth', function()
{
if (Auth::guest())
{
if (Request::ajax())
{
return Response::make('Unauthorized', 401);
}
else
{
//return Redirect::guest('login');
}
}
});
The way I understand it the code within the function should only be loaded if the user is logged in. Otherwise give 401 error.
Thanks for help.
With some help from my friend I fixed it. Some important information, Route filters will continue with intended purpose unless something is returned from filter. Also, the standard auth filter will not work. Must be modified. After that it was cake. Code is below.
Route:
Route::get('/account', ['before' => 'auth', 'uses' => 'SiteController#account']);
Auth Filter:
Route::filter('auth', function()
{
if (Auth::guest())
{
return Response::make('Unauthorized', 401);
}
});