Ответ 1
Я решил обработать его, используя синтаксис es2017 и Babel, чтобы перевести на es2016, который поддерживает Node.
Более новые версии Node.js поддерживают этот синтаксис без преобразования.
Вот пример:
'use strict';
const express = require('express');
const router = express.Router();
const Promise = require('bluebird');
const HttpStatus = require('http-status-codes');
const fs = Promise.promisifyAll(require('fs'));
const pool = require('./pool'); // my database pool module, using promise-mysql
const Errors = require('./errors'); // my collection of custom exceptions
////////////////////////////////////////////////////////////////////////////////
// GET /v1/provinces/:id
////////////////////////////////////////////////////////////////////////////////
router.get('/provinces/:id', async (req, res) => {
try {
// get a connection from the pool
const connection = await pool.createConnection();
try {
// retrieve the list of provinces from the database
const sql_p = `SELECT p.id, p.code, p.name, p.country_id
FROM provinces p
WHERE p.id = ?
LIMIT 1`;
const provinces = await connection.query(sql_p);
if (!provinces.length)
throw new Errors.NotFound('province not found');
const province = provinces[0];
// retrieve the associated country from the database
const sql_c = `SELECT c.code, c.name
FROM countries c
WHERE c.id = ?
LIMIT 1`;
const countries = await connection.query(sql_c, province.country_id);
if (!countries.length)
throw new Errors.InternalServerError('country not found');
province.country = countries[0];
return res.send({ province });
} finally {
pool.releaseConnection(connection);
}
} catch (err) {
if (err instanceof Errors.NotFound)
return res.status(HttpStatus.NOT_FOUND).send({ message: err.message }); // 404
console.log(err);
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); // 500
}
});
////////////////////////////////////////////////////////////////////////////////
// GET /v1/provinces
////////////////////////////////////////////////////////////////////////////////
router.get('/provinces', async (req, res) => {
try {
// get a connection from the pool
const connection = await pool.createConnection();
try {
// retrieve the list of provinces from the database
const sql_p = `SELECT p.id, p.code, p.name, p.country_id
FROM provinces p`;
const provinces = await connection.query(sql_p);
const sql_c = `SELECT c.code, c.name
FROM countries c
WHERE c.id = ?
LIMIT 1`;
const promises = provinces.map(async p => {
// retrieve the associated country from the database
const countries = await connection.query(sql_c, p.country_id);
if (!countries.length)
throw new Errors.InternalServerError('country not found');
p.country = countries[0];
});
await Promise.all(promises);
return res.send({ total: provinces.length, provinces });
} finally {
pool.releaseConnection(connection);
}
} catch (err) {
console.log(err);
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); // 500
}
});
////////////////////////////////////////////////////////////////////////////////
// OPTIONS /v1/provinces
////////////////////////////////////////////////////////////////////////////////
router.options('/provinces', async (req, res) => {
try {
const data = await fs.readFileAsync('./options/provinces.json');
res.setHeader('Access-Control-Allow-Methods', 'HEAD,GET,OPTIONS');
res.setHeader('Allow', 'HEAD,GET,OPTIONS');
res.send(JSON.parse(data));
} catch (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message });
}
});
module.exports = router;
Использование async
/await
вместе с этим try { try { } finally { } } catch { } pattern
обеспечивает чистую обработку ошибок, где вы можете собирать и обрабатывать все свои ошибки в одном месте. Блок finally закрывает соединение с базой данных независимо от того, что.
Вам просто нужно убедиться, что вы имеете дело с promises полностью. Для доступа к базе данных я использую модуль promise-mysql
вместо простого mysql
модуля. Для всего остального я использую модуль bluebird
и promisifyAll()
.
У меня также есть пользовательские классы исключений, которые я могу бросить при определенных обстоятельствах, а затем обнаруживать их в блоке catch. В зависимости от того, какие исключения могут быть выбраны в блоке try, мой блок catch может выглядеть примерно так:
catch (err) {
if (err instanceof Errors.BadRequest)
return res.status(HttpStatus.BAD_REQUEST).send({ message: err.message }); // 400
if (err instanceof Errors.Forbidden)
return res.status(HttpStatus.FORBIDDEN).send({ message: err.message }); // 403
if (err instanceof Errors.NotFound)
return res.status(HttpStatus.NOT_FOUND).send({ message: err.message }); // 404
if (err instanceof Errors.UnprocessableEntity)
return res.status(HttpStatus.UNPROCESSABLE_ENTITY).send({ message: err.message }); // 422
console.log(err);
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message });
}
pool.js:
'use strict';
const mysql = require('promise-mysql');
const pool = mysql.createPool({
connectionLimit: 100,
host: 'localhost',
user: 'user',
password: 'password',
database: 'database',
charset: 'utf8mb4',
debug: false
});
module.exports = pool;
errors.js:
'use strict';
class ExtendableError extends Error {
constructor(message) {
if (new.target === ExtendableError)
throw new TypeError('Abstract class "ExtendableError" cannot be instantiated directly.');
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.contructor);
}
}
// 400 Bad Request
class BadRequest extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('bad request');
else
super(m);
}
}
// 401 Unauthorized
class Unauthorized extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('unauthorized');
else
super(m);
}
}
// 403 Forbidden
class Forbidden extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('forbidden');
else
super(m);
}
}
// 404 Not Found
class NotFound extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('not found');
else
super(m);
}
}
// 409 Conflict
class Conflict extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('conflict');
else
super(m);
}
}
// 422 Unprocessable Entity
class UnprocessableEntity extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('unprocessable entity');
else
super(m);
}
}
// 500 Internal Server Error
class InternalServerError extends ExtendableError {
constructor(m) {
if (arguments.length === 0)
super('internal server error');
else
super(m);
}
}
module.exports.BadRequest = BadRequest;
module.exports.Unauthorized = Unauthorized;
module.exports.Forbidden = Forbidden;
module.exports.NotFound = NotFound;
module.exports.Conflict = Conflict;
module.exports.UnprocessableEntity = UnprocessableEntity;
module.exports.InternalServerError = InternalServerError;