如何通过 3 个步骤使用 JWT Auth 和 PostgreSQL 初始化多层 Node.js RESTful API
GenAI LIVE! | 2025年6月4日
鲜为人知的是,尤利乌斯·凯撒的名言“分而治之”实际上指的是使用分层架构构建Web应用程序。其原理在于将用户界面与业务逻辑分离,将业务逻辑与数据访问逻辑分离。分层架构提供了更高的灵活性、可维护性和可扩展性,并且更易于编写、测试和维护。听起来不错,但如何使用现代技术来实现它呢?
对于那些刚接触 Node.js 和 RESTful API 的新手,我们编写了一份指南,旨在帮助您创建一个可以发展成大型服务的 RESTful API。在本教程中,我们将分三步向您展示如何构建 RESTful API。JSON Web Token 技术将帮助我们处理身份验证,并使用 PostgreSQL 作为数据库。
因此,我们编写 RESTful API 的步骤是:
- 初始化 Node.js 项目
- JWT 身份验证
- 添加图层
初始化 Node.js 项目
让我们开始构建我们的应用程序。创建一个空文件夹,并使用以下命令初始化一个新项目:npm init
要安装必要的软件包,请运行以下命令:npm i bcrypt bluebird body-parser express http jsonwebtoken lodash pg sequelize sequelize-values — save
接下来,在主文件夹中创建以下文件:
- config.js(应用程序的配置,如数据库连接、密码盐等)
- db.js(负责数据库连接)
- router.js(处理 http 请求并将其分派给控制器)
- index.js —(启动文件)
以下是我们的文件包含的代码:
配置.js:
module.exports = {
port: 3000,
dbConnectionString: '你的 postgresql 连接',
saltRounds: 2,
jwtSecret: 'yo-its-a-secret',
tokenExpireTime: '6h'
}
db.js:
const config = require('./config');
const Sequelize = require('sequelize');
var sequelize = new Sequelize(config.dbConnectionString);
require('sequelize-values')(sequelize);
module.exports = sequelize;
router.js:
module.exports.set = app => {
//endpoints will be here soon
}
index.js:
const express = require('express');
const http = require('http');
const bodyParser = require('body-parser');
const app = express();
const config = require('./config');
const router = require('./router');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static('client'));
router.set(app);
app.listen(config.port, () => console.log('App listening on port '+ config.port));
在主文件夹中创建完文件后,你需要定义数据模型。为此,创建一个 models 文件夹,其中包含index.js文件。如下所示:
/models/index.js:
const Sequelize = require('sequelize');
const sequelize = require('../db');
const User = sequelize.define('user', {
login: Sequelize.STRING,
password: Sequelize.STRING,
});
const Order = sequelize.define('order', {
title: Sequelize.STRING,
date: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
user_id: {
type: Sequelize.INTEGER,
references: {
model: User,
key: 'id'
}
}
});
User.hasMany(Order, {foreignKey: 'user_id'});
module.exports = {
User,
Order
}
这就是你启动多层 Node.js 项目的方式。目前,我们有一个应用程序的入口点 ( index.js )、两个数据库模型 ( models/index.js ) 以及一些基本配置。
JWT 身份验证
在编写实际 API 之前,让我们先为应用程序添加身份验证。创建一个 services 文件夹,其中包含文件user.js和auth.js。如下所示:
/services/index.js
const sequelize = require('../db');
const Users = require('../models').User;
const addUser = user => Users.create(user);
const getUserByLogin = login => Users.findOne({where: {login}});
module.exports = {
addUser,
getUserByLogin
}
用户登录后,后续每个请求都会包含一个令牌(JWT),用户可以根据令牌中存储的权限访问路由、服务和资源。
/services/auth.js 将处理 JWT 身份验证:
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const Users = require('../models').User;
const config = require('../config');
const authenticate = params => {
return Users.findOne({
where: {
login: params.login
},
raw: true
}).then(user => {
if (!user)
throw new Error('Authentication failed. User not found.');
if (!bcrypt.compareSync(params.password || '', user.password))
throw new Error('Authentication failed. Wrong password.');
const payload = {
login: user.login,
id: user.id,
time: new Date()
};
var token = jwt.sign(payload, config.jwtSecret, {
expiresIn: config.tokenExpireTime
});
return token;
});
}
module.exports = {
authenticate
}
为了处理注册和身份验证请求,我们的应用程序应该有一个控制器。
让我们创建一个文件auth.js并将其放在 controllers 文件夹中。
/控制器/auth.js:
const config = require('../config');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const authService = require('../services/auth');
const userService = require('../services/user');
function login(req, res){
return authService.authenticate(req.body)
.then(token => {
res.send({
success: true,
data: { token }
});
})
.catch(err => {
res.send({
success: false,
message: err.message //not the best error handling.
//for better error handling visit github repository, link provided below
});
})
};
function register(req, res){
var login = req.body.login;
return userService.getUserByLogin(req.body.login || '')
.then(exists => {
if (exists){
return res.send({
success: false,
message: 'Registration failed. User with this email already registered.'
});
}
var user = {
login: req.body.login,
password: bcrypt.hashSync(req.body.password, config.saltRounds)
}
return userService.addUser(user)
.then(() => res.send({success: true}));
});
};
module.exports = {
login,
register
}
在此之后,我们需要向我们的 API 添加端点。
您可以在 router.js 文件中执行此操作:
const authController = require('./controllers/auth');
module.exports.set = app => {
app.post('/login', authController.login);
app.post('/register', authController.register);
}
让我们通过运行命令 node index.js启动服务器并测试登录和注册功能。
添加图层
现在我们的应用程序中已经有了控制器层和数据访问层。为了将它们连接在一起,我们需要在它们之间添加服务层。使用层是确保职责分离的好方法,这使得数据、业务逻辑和表示代码能够相互独立。表示层(用户)与控制器层(API)交互,控制器层使用服务层(业务规则)通过数据访问层访问和修改数据。
那么,让我们从订单模型的第一个服务开始。
创建文件 services/order.js
const Orders = require('../models').Order;
const getAll = () => Orders.findAll();
const getById = id => Orders.findById(id);
const add = order => Orders.create(order);
module.exports = {add, getAll, getById};
现在,我们可以创建一个控制器来与该服务协同工作。
控制器/order.js
const orderService = require('../services/order');
function getOrders(req, res){
orderService.getAll()
.then(data => res.send(data));
};
function getOrder(req, res){
orderService.getById(req.params.id)
.then(data => res.send(data));
}
function addOrder(req, res){
orderService.add({
title: req.body.title,
user_id: 1
})
.then(data => res.send(data));
};
module.exports = {
getOrders,
getOrder,
addOrder
}
我们还需要做一件事来完成 RESTful API 的订单部分,那就是将端点添加到 router.js:
const orderController = require('./controllers/order');
…
app.get('/orders', orderController.getOrders);
app.get('/orders/:id', orderController.getOrder);
app.post('/orders', orderController.addOrder);
以下是一些有效 API 的测试说明:
接下来我们需要做的是只允许经过身份验证的用户访问。为此,让我们添加检查用户是否已登录的中间件:
中间件/auth.js:
const jwt = require('jsonwebtoken');
const config = require('../config');
const checkAuth = (req, res, next) => {
var token = req.headers['token'];
if (!token)
return res.status(403).send({ auth: false, message: 'No token provided.' });
jwt.verify(token, config.jwtSecret, (err, decoded) => {
if (err)
return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });
req.user = {
login: decoded.login,
id: decoded.id
};
next();
});
}
module.exports = {
checkAuth
}
此后,身份验证中间件应作为端点函数中的中间件参数(第二个参数)使用。现在,用户如果不提供有效的身份验证令牌,就无法访问数据。
app.get('/orders', authMiddleware.checkAuth, orderController.getOrders);
app.get('/orders/:id', authMiddleware.checkAuth, orderController.getOrder);
app.post('/orders', authMiddleware.checkAuth, orderController.addOrder);
app.get('/user_orders', authMiddleware.checkAuth, userController.getUsersWithOrders)
正如你所见,它起作用了:)
我们需要做的最后一件事是定义 addOrder 函数。
function addOrder(req, res){
orderService.add({
title: req.body.title,
user_id: req.user.id
})
.then(data => res.send(data));
};
就这样!我们创建了一个使用 RESTful API 的小型应用。根据多层架构的概念,我们定义了控制器层、服务层和数据访问层。我们的 API 支持 JWT 授权,可以轻松添加展示层,例如 Web 应用或移动应用。
欢迎随时查看代码库中的代码。别傻傻地站在那里,赶紧尝试一下吧。ElifTech 祝您好运!
链接:https://dev.to/vitaliikulyk/how-to-initialize-multilayer-nodejs-restful-api-with-jwt-auth-and-postgresql-in-3-steps--c8c