-
Notifications
You must be signed in to change notification settings - Fork 11
Code Routes
Every route exports an instance of Express's Router class. For this example, let's build a basic route for user login, which will be called user.js
.
const router = require('express').Router();
module.exports = router;
Inside of app.js
, we first load a route with require
, and then tie it to its corresponding URL using app.use
.
//Load ./routes/user.js
const user = require('./routes/user');
//Tie the route to the URL https://scoutradioz.com/user
app.use('/user', user);
For our router to respond to GET/POST requests, we use router.get
and router.post
, respectively. The first parameter is the URL, and the second parameter is a callback, which takes our req and res variables as parameters. See Function notes for info on req and res.
The URL parameter gets added on top of the "base" URL of our route, which we set inside app.js
. If we want our login page to be at /user/login, we will make a GET handler for '/login':
const router = require('express').Router();
router.get('/login', (req, res) => {
});
module.exports = router;
Note that (req, res) => {}
is arrow notation for function (req, res) {}
. Arrow notation helps keep our code from being too verbose.
Rendering a page is easy! See Code Views for notes on building views. Let's assume ./views/user/login.pug
has already been made, and has a form that submits a POST request to '/user/login'.
The method to render a view is res.render.
The first parameter is the relative path to our view (with respect to the ./views directory), and the second parameter is an object which contains variables that can be passed to our view. Our views all take a title
, which sets the title of the page.
const router = require('express').Router();
router.get('/login', (req, res) => {
res.render('./user/login', {
title: "Log In"
});
});
module.exports = router;
We wrote a wrapper module called scoutradioz-utilities
, which handles database connections for us. See Scoutradioz-Utilities for more details. To use it, we need to change our handler function to an async
function. To access GET query parameters (/path/to/url?foo=bar), we use req.query
(e.g. req.query.foo
). To access POST body parameters, we use req.body
(e.g. req.body.foo
).
Please note that this is not a complete programming tutorial. There will be not be any error handling in these code snippets, in order to reduce the size of the sample code.
const router = require('express').Router();
const utilities = require('@firstteam102/scoutradioz-utilities');
const bcrypt = require('bcryptjs');
router.get('/login', async (req, res) => {
//Access the database and find all users whose org_key matches the org_key inside our GET request
// (e.g. /users/login?org_key=frc102)
const users = await utilities.find('users', {org_key: req.query.org_key});
res.render('./user/login', {
title: "Log In",
users: users,
});
});
router.post('/login', async (req, res) => {
//Find the user that matches the selected user's ID
const user = await utilities.findOne('users', {_id: req.body.userId});
//Compare password to hash stored in database
var comparison = await bcrypt.compare(req.body.password, user.password);
//If password check passed, then log in user.
if (comparison == true) {
req.logIn(user);
}
//If password check failed, then throw an error.
else {
throw Error("Password check failed! Oh no!!");
}
});
module.exports = router;
But unfortunately, here, you will find that we run into a problem. Express handles errors in normal functions just fine. But since async functions always return a Promise, you'll find that when we throw an error, you get a message like this:
(node:5916) UnhandledPromiseRejectionWarning: ReferenceError: foo is not defined
at router.get (C:\ScoringApp-Serverless\primary\routes\index.js:162:14)
at Layer.handle [as handle_request] (C:\ScoringApp-Serverless\primary\node_modules\express\lib\router\layer.js:95:5)
at ......
(node:5916) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:5916) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Express cannot handle promise rejections by itself. So in order for a promise rejection to be properly handled, and for our route to properly throw
an Error
that Express can handle, we need to put our method into a wrapper. Thankfully, the package Express Async Handler works for this. In order to keep our code from being too verbose, let's call it wrap
when we require
it.
const router = require('express').Router();
const utilities = require('@firstteam102/scoutradioz-utilities');
const bcrypt = require('bcryptjs');
const wrap = require('express-async-handler');
router.get('/login', wrap(async (req, res) => {
//Access the database and find all users whose org_key matches the org_key inside our GET request
// (e.g. /users/login?org_key=frc102)
const users = await utilities.find('users', {org_key: req.query.org_key});
res.render('./user/login', {
title: "Log In",
users: users,
});
}));
router.post('/login', wrap(async (req, res) => {
//Find the user that matches the selected user's ID
const user = await utilities.findOne('users', {_id: req.body.userId});
//Compare password to hash stored in database
var comparison = await bcrypt.compare(req.body.password, user.password);
//If password check passed, then log in user.
if (comparison == true) {
req.logIn(user);
}
//If password check failed, then throw an error.
else {
throw Error("Password check failed! Oh no!!");
}
}));
module.exports = router;