Error messages

We have a well-defined error handler, as we saw in the previous section, so we can now write meaningful error messages that will help you with troubleshooting and make sure that sensitive information is not exposed.

Error messages will depend on the type of application you are coding for. As an example, think about what kind of error message you would like to see to help you solve an issue or understand what is happening.

We will apply some ideas and you can then go further and improve the error messages for the order-api application.

First, we will change the order.ts controller file, as follows:

import { NextFunction, Request, Response } from 'express'
import * as halson from 'halson'
import * as _ from 'lodash'
import { OrderModel } from '../schemas/order'
import { UserModel } from '../schemas/User'
import { formatOutput } from '../utility/orderApiUtility'

export let getOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id

OrderModel.findById(id, (err, order) => {
if (!order) {
return next(new Error(`Order ${id} not found.`))
}
order = halson(order.toJSON()).addLink('self', `/store/orders/${order.id}`)
return formatOutput(res, order, 200, 'order')
})
}

export let getAllOrders = (req: Request, res: Response, next: NextFunction) => {
const limit = Number(req.query.limit) || 0
const offset = Number(req.query.offset) || 0

OrderModel.find({}, null, { skip: offset, limit: limit }).then(orders => {
if (orders) {
orders = orders.map(order => {
return halson(order.toJSON())
.addLink('self', `/store/orders/${order.id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})
})
}
return formatOutput(res, orders, 200, 'order')
})
}

export let addOrder = (req: Request, res: Response, next: NextFunction) => {
const userId = req.body.userId

UserModel.findById(userId, (err, user) => {
if (!user) {
throw new Error(`There is no user with the userId ${userId}`)
}

const newOrder = new OrderModel(req.body)

newOrder.save((error, order) => {
order = halson(order.toJSON())
.addLink('self', `/store/orders/${order._id}`)
.addLink('user', {
href: `/users/${order.userId}`,
})

return formatOutput(res, order, 201, 'order')
})
})
}

export let removeOrder = (req: Request, res: Response, next: NextFunction) => {
const id = req.params.id

OrderModel.findById(id, (err, order) => {
if (!order) {
return res.status(404).send()
}
order.remove(error => {
res.status(204).send()
})
})
}

export let getInventory = (req: Request, res: Response, next: NextFunction) => {
const status = req.query.status

OrderModel.find({ status: status }, (err, orders) => {
orders = _.groupBy(orders, 'userId')
return formatOutput(res, orders, 200, 'inventory')
})
}

As you can see, we decided not to add a lot of text on the error handler for order controller. The reason for this is that, sometimes, less information is better than a lot of useless words.

The same idea was applied to the user controller in the following code snippet:

import * as bcrypt from 'bcrypt'
import { NextFunction, Request, Response } from 'express'
import * as halson from 'halson'
import * as jwt from 'jsonwebtoken'
import { UserModel } from '../schemas/User'
import { formatOutput } from '../utility/orderApiUtility'

export let getUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username

UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}

user = user.toJSON()
user._id = user._id.toString()

user = halson(user).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 200, 'user')
})
}

export let addUser = (req: Request, res: Response, next: NextFunction) => {
const newUser = new UserModel(req.body)

newUser.password = bcrypt.hashSync(newUser.password, 10)

newUser.save((error, user) => {
if (error) {
return res.status(500).send(error)
}
user = halson(user.toJSON()).addLink('self', `/users/${user._id}`)
return formatOutput(res, user, 201, 'user')
})
}

export let updateUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username

UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}

user.username = req.body.username || user.username
user.firstName = req.body.firstName || user.firstName
user.lastName = req.body.lastName || user.lastName
user.email = req.body.email || user.email
user.password = req.body.password || user.password
user.phone = req.body.phone || user.phone
user.userStatus = req.body.userStatus || user.userStatus

user.save(error => {
res.status(204).send()
})
})
}

export let removeUser = (req: Request, res: Response, next: NextFunction) => {
const username = req.params.username

UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}

user.remove(error => {
res.status(204).send()
})
})
}

export let login = (req: Request, res: Response, next: NextFunction) => {
const username = req.query.username
const password = req.query.password

UserModel.findOne({ username: username }, (err, user) => {
if (!user) {
return res.status(404).send()
}

const validate = bcrypt.compareSync(password, user.password.valueOf())

if (validate) {
const body = { _id: user._id, email: user.email }

const token = jwt.sign({ user: body }, 'top_secret')

return res.json({ token: token })
} else {
return res.status(401).send()
}
})
}

Of course, even when adopting this error handler strategy, our tests should pass as before:

$ npm run test

This will produce the following output:

baseRoute
should respond with HTTP 200 status (126ms)
should respond with success message

userRoute
should be able to login (117ms)
should respond with HTTP 404 status because there is no user
should create a new user and retrieve it back (121ms)
should return the user created on the step before
should updated the user John (45ms)
should return the user updated on the step before
should return 404 because the user does not exist
should remove an existent user
should return 404 when it is trying to remove an user because the user does not exist

userRoute
should be able to login and get the token to be used on orders requests (101ms)
should respond with HTTP 404 status because there is no order (63ms)
should create a new user for Order tests and retrieve it back (84ms)
should create a new order and retrieve it back
should return the order created on the step before
should return all orders so far
should not return orders because offset is higher than the size of the orders array
should return the inventory for all users
should remove an existing order
should return 404 when it is trying to remove an order because the order does not exist


21 passing (1s)
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.206.169