mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1206 字
3 分钟
Node.js和Express后端开发完全指南

Node.js和Express后端开发完全指南#

Node.js已经成为现代后端开发的重要工具,它允许我们使用JavaScript编写服务器端代码,实现全栈JavaScript开发。Express是Node.js生态系统中最流行的Web框架,它提供了简洁、灵活的API,帮助我们快速构建Web应用。本文将深入探讨Node.js和Express的使用技巧和最佳实践,帮助你构建高性能、可维护的后端应用。

1. Node.js基础#

1.1 什么是Node.js?#

Node.js是一个基于Chrome V8引擎的JavaScript运行时,它允许我们在服务器端运行JavaScript代码。Node.js采用事件驱动、非阻塞I/O模型,使其轻量且高效。

1.2 安装Node.js#

Windows和macOS:Node.js官网下载安装包。

Linux: 使用包管理器安装。

# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# CentOS/RHEL
curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
sudo yum install -y nodejs

1.3 NPM和Yarn#

NPM: Node.js自带的包管理器。

Yarn: Facebook开发的包管理器,提供更快的安装速度和更可靠的依赖管理。

# 安装Yarn
npm install -g yarn

1.4 基本模块#

内置模块:

  • fs:文件系统操作
  • path:路径处理
  • http:HTTP服务器和客户端
  • https:HTTPS服务器和客户端
  • events:事件处理
  • stream:流处理
  • buffer:缓冲区处理
  • util:实用工具

使用内置模块:

const fs = require('fs');
const path = require('path');
const http = require('http');

2. Express基础#

2.1 什么是Express?#

Express是一个基于Node.js的Web应用框架,它提供了简洁、灵活的API,帮助我们快速构建Web应用。Express是Node.js生态系统中最流行的Web框架,被广泛用于构建RESTful API、Web应用和单页应用的后端。

2.2 安装Express#

# 使用NPM
npm init -y
npm install express
# 使用Yarn
yarn init -y
yarn add express

2.3 基本用法#

创建简单的Express应用:

const express = require('express');
const app = express();
const port = 3000;
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

2.4 路由#

基本路由:

// GET请求
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
});
// POST请求
app.post('/api/users', (req, res) => {
const user = req.body;
res.status(201).json(user);
});
// PUT请求
app.put('/api/users/:id', (req, res) => {
const { id } = req.params;
const user = req.body;
res.json({ id, ...user });
});
// DELETE请求
app.delete('/api/users/:id', (req, res) => {
const { id } = req.params;
res.status(204).send();
});

路由参数:

app.get('/api/users/:id', (req, res) => {
const { id } = req.params;
res.json({ id, name: 'John' });
});

查询参数:

app.get('/api/users', (req, res) => {
const { page, limit } = req.query;
res.json({ page, limit, users: [] });
});

2.5 中间件#

内置中间件:

// 解析JSON请求体
app.use(express.json());
// 解析URL编码的请求体
app.use(express.urlencoded({ extended: true }));
// 提供静态文件
app.use(express.static('public'));

自定义中间件:

// 日志中间件
const logger = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
};
app.use(logger);
// 认证中间件
const auth = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ message: 'Unauthorized' });
}
// 验证token
next();
};
app.use('/api', auth);

错误处理中间件:

app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: 'Internal Server Error' });
});

3. 高级Express功能#

3.1 路由模块化#

创建路由模块:

routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
});
router.post('/', (req, res) => {
const user = req.body;
res.status(201).json(user);
});
module.exports = router;

使用路由模块:

const express = require('express');
const app = express();
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);

3.2 中间件链#

const express = require('express');
const app = express();
// 中间件1
const middleware1 = (req, res, next) => {
console.log('Middleware 1');
next();
};
// 中间件2
const middleware2 = (req, res, next) => {
console.log('Middleware 2');
next();
};
// 应用中间件链
app.get('/api/users', middleware1, middleware2, (req, res) => {
res.json([{ id: 1, name: 'John' }]);
});

3.3 错误处理#

同步错误:

app.get('/api/error', (req, res) => {
throw new Error('Sync error');
});

异步错误:

// 方法1:使用try-catch
app.get('/api/async-error', async (req, res, next) => {
try {
const result = await someAsyncOperation();
res.json(result);
} catch (error) {
next(error);
}
});
// 方法2:直接传递错误
app.get('/api/async-error', async (req, res, next) => {
const result = await someAsyncOperation().catch(next);
res.json(result);
});

自定义错误类:

class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// 使用自定义错误
app.get('/api/error', (req, res, next) => {
next(new AppError('Not found', 404));
});
// 错误处理中间件
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
});

3.4 环境变量#

使用dotenv:

npm install dotenv

创建.env文件:

NODE_ENV=development
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key

使用环境变量:

require('dotenv').config();
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const databaseUrl = process.env.DATABASE_URL;
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

4. 数据库集成#

4.1 MongoDB#

安装Mongoose:

npm install mongoose

连接MongoDB:

const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('Connected to MongoDB');
}).catch((error) => {
console.error('Error connecting to MongoDB:', error);
});

创建模型:

const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
const User = mongoose.model('User', userSchema);
module.exports = User;

使用模型:

const User = require('./models/User');
// 创建用户
app.post('/api/users', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 获取所有用户
app.get('/api/users', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 获取单个用户
app.get('/api/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 更新用户
app.put('/api/users/:id', async (req, res) => {
try {
const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 删除用户
app.delete('/api/users/:id', async (req, res) => {
try {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.status(204).send();
} catch (error) {
res.status(500).json({ message: error.message });
}
});

4.2 PostgreSQL#

安装Sequelize:

npm install sequelize pg pg-hstore

连接PostgreSQL:

const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL, {
dialect: 'postgres',
logging: false
});
sequelize.authenticate().then(() => {
console.log('Connected to PostgreSQL');
}).catch((error) => {
console.error('Error connecting to PostgreSQL:', error);
});

创建模型:

const { DataTypes } = require('sequelize');
const sequelize = require('./sequelize');
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
module.exports = User;

使用模型:

const User = require('./models/User');
// 创建用户
app.post('/api/users', async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201).json(user);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 获取所有用户
app.get('/api/users', async (req, res) => {
try {
const users = await User.findAll();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 获取单个用户
app.get('/api/users/:id', async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 更新用户
app.put('/api/users/:id', async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
await user.update(req.body);
res.json(user);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 删除用户
app.delete('/api/users/:id', async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
await user.destroy();
res.status(204).send();
} catch (error) {
res.status(500).json({ message: error.message });
}
});

4.3 MySQL#

安装Sequelize:

npm install sequelize mysql2

连接MySQL:

const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL, {
dialect: 'mysql',
logging: false
});
sequelize.authenticate().then(() => {
console.log('Connected to MySQL');
}).catch((error) => {
console.error('Error connecting to MySQL:', error);
});

创建模型和使用方法与PostgreSQL类似。

5. 认证和授权#

5.1 JWT认证#

安装jsonwebtoken:

npm install jsonwebtoken bcryptjs

生成和验证JWT:

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
// 生成token
const generateToken = (userId) => {
return jwt.sign({ id: userId }, process.env.JWT_SECRET, {
expiresIn: '1h'
});
};
// 验证token
const verifyToken = (token) => {
return jwt.verify(token, process.env.JWT_SECRET);
};
// 密码加密
const hashPassword = async (password) => {
const salt = await bcrypt.genSalt(10);
return await bcrypt.hash(password, salt);
};
// 密码验证
const comparePassword = async (password, hashedPassword) => {
return await bcrypt.compare(password, hashedPassword);
};

认证流程:

const User = require('./models/User');
const { generateToken, hashPassword, comparePassword } = require('./utils/auth');
// 注册
app.post('/api/auth/register', async (req, res) => {
try {
const { name, email, password } = req.body;
// 检查用户是否存在
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ message: 'User already exists' });
}
// 密码加密
const hashedPassword = await hashPassword(password);
// 创建用户
const user = await User.create({ name, email, password: hashedPassword });
// 生成token
const token = generateToken(user.id);
res.status(201).json({ user, token });
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 登录
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
// 检查用户是否存在
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: 'Invalid email or password' });
}
// 验证密码
const isPasswordValid = await comparePassword(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({ message: 'Invalid email or password' });
}
// 生成token
const token = generateToken(user.id);
res.json({ user, token });
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// 认证中间件
const auth = async (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Access token required' });
}
const decoded = verifyToken(token);
const user = await User.findById(decoded.id);
if (!user) {
return res.status(401).json({ message: 'Invalid token' });
}
req.user = user;
next();
} catch (error) {
res.status(401).json({ message: 'Invalid token' });
}
};
// 保护路由
app.get('/api/profile', auth, (req, res) => {
res.json({ user: req.user });
});

5.2 OAuth认证#

安装passport:

npm install passport passport-google-oauth20

配置Google OAuth:

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User = require('./models/User');
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/api/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
try {
// 检查用户是否存在
let user = await User.findOne({ googleId: profile.id });
if (!user) {
// 创建新用户
user = await User.create({
googleId: profile.id,
name: profile.displayName,
email: profile.emails[0].value
});
}
done(null, user);
} catch (error) {
done(error, null);
}
}));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id);
done(null, user);
} catch (error) {
done(error, null);
}
});

OAuth路由:

const passport = require('passport');
// 发起OAuth认证
app.get('/api/auth/google', passport.authenticate('google', {
scope: ['profile', 'email']
}));
// OAuth回调
app.get('/api/auth/google/callback', passport.authenticate('google', {
failureRedirect: '/api/auth/failure'
}), (req, res) => {
// 生成token
const token = generateToken(req.user.id);
res.json({ user: req.user, token });
});
// 认证失败
app.get('/api/auth/failure', (req, res) => {
res.status(401).json({ message: 'Authentication failed' });
});

6. 性能优化#

6.1 数据库优化#

索引: 为频繁查询的字段添加索引。

查询优化:

  • 只查询需要的字段
  • 使用分页
  • 避免N+1查询

连接池: 使用连接池管理数据库连接。

6.2 缓存#

安装redis:

npm install redis

使用Redis缓存:

const redis = require('redis');
const redisClient = redis.createClient({
url: process.env.REDIS_URL
});
redisClient.connect().then(() => {
console.log('Connected to Redis');
}).catch((error) => {
console.error('Error connecting to Redis:', error);
});
// 缓存中间件
const cache = (duration) => {
return async (req, res, next) => {
const key = `cache:${req.originalUrl}`;
try {
const cachedData = await redisClient.get(key);
if (cachedData) {
return res.json(JSON.parse(cachedData));
}
const originalSend = res.json;
res.json = async (data) => {
await redisClient.set(key, JSON.stringify(data), {
EX: duration
});
originalSend.call(res, data);
};
next();
} catch (error) {
next();
}
};
};
// 应用缓存
app.get('/api/users', cache(3600), async (req, res) => {
const users = await User.find();
res.json(users);
});

6.3 负载均衡#

使用PM2:

npm install -g pm2

启动应用:

pm run build
pm run start:prod

配置PM2:

ecosystem.config.js
module.exports = {
apps: [
{
name: 'myapp',
script: 'index.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production'
}
}
]
};

启动PM2:

pm run build
pm run start:prod

6.4 代码优化#

使用async/await: 异步代码更简洁,更易读。

使用Promise.all: 并行执行多个异步操作。

app.get('/api/dashboard', async (req, res) => {
try {
const [users, posts, comments] = await Promise.all([
User.countDocuments(),
Post.countDocuments(),
Comment.countDocuments()
]);
res.json({ users, posts, comments });
} catch (error) {
res.status(500).json({ message: error.message });
}
});

避免阻塞操作: 避免在主线程中执行阻塞操作,如文件I/O、网络请求等。

使用流: 对于大文件,使用流处理。

const fs = require('fs');
const path = require('path');
app.get('/api/download', (req, res) => {
const filePath = path.join(__dirname, 'large-file.txt');
const stream = fs.createReadStream(filePath);
stream.pipe(res);
});

7. 测试#

7.1 单元测试#

安装Jest:

npm install --save-dev jest

编写单元测试:

utils/auth.test.js
const { hashPassword, comparePassword } = require('./auth');
describe('Auth utils', () => {
test('hashPassword should return hashed password', async () => {
const password = 'password123';
const hashedPassword = await hashPassword(password);
expect(hashedPassword).not.toBe(password);
});
test('comparePassword should return true for correct password', async () => {
const password = 'password123';
const hashedPassword = await hashPassword(password);
const result = await comparePassword(password, hashedPassword);
expect(result).toBe(true);
});
test('comparePassword should return false for incorrect password', async () => {
const password = 'password123';
const wrongPassword = 'wrongpassword';
const hashedPassword = await hashPassword(password);
const result = await comparePassword(wrongPassword, hashedPassword);
expect(result).toBe(false);
});
});

运行测试:

npm test

7.2 集成测试#

安装supertest:

npm install --save-dev supertest

编写集成测试:

app.test.js
const request = require('supertest');
const app = require('./app');
describe('API endpoints', () => {
test('GET /api/users should return 200', async () => {
const response = await request(app).get('/api/users');
expect(response.statusCode).toBe(200);
expect(Array.isArray(response.body)).toBe(true);
});
test('POST /api/users should return 201', async () => {
const user = { name: 'John', email: 'john@example.com', password: 'password123' };
const response = await request(app).post('/api/users').send(user);
expect(response.statusCode).toBe(201);
expect(response.body.name).toBe(user.name);
});
});

运行测试:

npm test

8. 部署#

8.1 部署到Heroku#

创建Heroku应用:

heroku create myapp

配置环境变量:

heroku config:set NODE_ENV=production
heroku config:set DATABASE_URL=mongodb://localhost:27017/myapp
heroku config:set JWT_SECRET=your-secret-key

部署应用:

git push heroku main

8.2 部署到AWS#

使用Elastic Beanstalk:

  1. 安装AWS CLI:npm install -g aws-cli
  2. 配置AWS CLI:aws configure
  3. 创建Elastic Beanstalk应用:eb init
  4. 部署应用:eb deploy

8.3 部署到Docker#

创建Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

创建docker-compose.yml:

version: '3'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
- JWT_SECRET=your-secret-key
depends_on:
- mongo
mongo:
image: mongo:4.4
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:

构建和运行容器:

docker-compose up -d

9. 最佳实践#

9.1 项目结构#

myapp/
├── src/
│ ├── config/
│ │ └── database.js
│ ├── controllers/
│ │ └── userController.js
│ ├── middlewares/
│ │ ├── auth.js
│ │ └── errorHandler.js
│ ├── models/
│ │ └── User.js
│ ├── routes/
│ │ └── userRoutes.js
│ ├── utils/
│ │ └── auth.js
│ └── app.js
├── index.js
├── package.json
└── .env

9.2 代码风格#

使用ESLint和Prettier:

npm install --save-dev eslint prettier eslint-config-prettier eslint-plugin-prettier

配置ESLint:

.eslintrc.js
module.exports = {
env: {
node: true,
commonjs: true,
es2021: true,
jest: true
},
extends: [
'eslint:recommended',
'prettier'
],
parserOptions: {
ecmaVersion: 12
},
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error'
}
};

配置Prettier:

.prettierrc.js
module.exports = {
singleQuote: true,
trailingComma: 'es5',
tabWidth: 2,
semi: true
};

9.3 错误处理#

统一错误处理:

middlewares/errorHandler.js
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
};
module.exports = errorHandler;
// app.js
const errorHandler = require('./middlewares/errorHandler');
app.use(errorHandler);

9.4 日志#

安装winston:

npm install winston

配置日志:

const winston = require('winston');
const logger = winston.createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
module.exports = logger;
// 使用日志
const logger = require('./logger');
app.get('/api/users', async (req, res) => {
try {
const users = await User.find();
logger.info('Fetched users');
res.json(users);
} catch (error) {
logger.error('Error fetching users:', error);
res.status(500).json({ message: 'Internal server error' });
}
});

9.5 安全#

安装helmet和cors:

npm install helmet cors

使用helmet和cors:

const helmet = require('helmet');
const cors = require('cors');
app.use(helmet());
app.use(cors());
// 防止XSS攻击
app.use((req, res, next) => {
res.setHeader('X-XSS-Protection', '1; mode=block');
next();
});
// 防止CSRF攻击
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
app.get('/api/csrf-token', (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});

10. 总结#

Node.js和Express为我们提供了一种强大、灵活的方式来构建后端应用。通过本文介绍的各种技术和最佳实践,你可以:

  • 构建高性能、可维护的后端应用
  • 集成各种数据库
  • 实现认证和授权
  • 优化应用性能
  • 编写测试
  • 部署应用到各种平台

希望本文对你有所帮助,祝你编码愉快!

11. 参考资料#

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Node.js和Express后端开发完全指南
https://sakumonet.top/posts/nodejs-express-backend-development/
作者
SakuMonet
发布于
2026-02-28
许可协议
Unlicensed

部分信息可能已经过时