My express.js application uses a lot of promises for interacting with a variety of services. Instead of including try/catch with each one, I've setup a fail safe factory method for all my express controllers. This should only be called when an unexpected error occurred. For example, 404 when trying a public API. I'm skeptical about how useful this is or if there is a much easier way to do this. Also looking for any other comments on this code or question.
//index.ts
import * as Express from 'express';
import { Auth } from '../class/Auth';
/**
* Handler for async static functions recognizing that errors
* should always be sent to the error handler. This is where
* our error stone drops. This will eventuall replace go().
* @param callback function to be run
* @return function for express to run
*/
function $(callback:Function):Function {
return async (req:Express.Request, res:Express.Response, next:Express.NextFunction) => {
try {
await callback(req, res, next);
} catch(e) {
console.error("Something strange occurred",e);
res.status(500).render('error', {
error: "An internal error has occurred",
errorCode: 500
});
}
}
}
/**
* Controller to log the user in
* @param req Express Request
* @param res Express Response
*/
function async loginController(req:Express.Request, res:Express.Response) {
// verify parameters
if(!(req.body['username'] && req.body['password'])){
return res.status(400).end("Please include username and password");
}
var user = await Auth.login(req.body['username'], req.body['password']);
if (user) {
var authToken = Auth.generateAuthToken(user);
res.json({
authToken:authToken
});
} else {
return res.status(401).end("Wrong username or password");
}
}
var app = Express();
app.post('/api/login', $(loginController));
app.listen(8080);
// Auth.ts
import { User } from './User';
/**
* Authentication base class
*/
export class Auth {
/**
* Log user in
* @param email
* @param password
* @returns user logged in. Returns null if user is not found or authentication fails
*/
public static async login(email:string, password:string):Promise<User> {
var response = await fetch("example.com/api/login", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email
password: password
})
});
if(response.ok){
return await response.json() as User;
}
// password or username was wrong
if(response.status == 401){
return null;
}
// an unknown error occurred
throw Error(response.status);
}
/**
* Generate AuthToken
* @param user
* @return token
*/
public static generateAuthToken(user:User):string {
return jwt.sign({ userid: user.id }, process.env.SECRET, {
expiresIn: process.env.JWT_EXPIRY
});
}
}
```
.getP()
doesn't ever lead to an unexpected application crash. And, for someone who adds to this project and just uses.get()
they have exactly the same requirement they do today (they have to catch their own rejections). So, you're just adding an option that, if used, saves you from having to catch all your own rejections. It's purely additive. And, this won't be the first convention or feature you add to your project that anyone maintaining it should know about. \$\endgroup\$