Node.js、Express、API设计详解 | 从零基础到精通 | 包含认证、数据库和部署
后端开发是指运行在服务器上的代码,负责处理业务逻辑、数据存储和API接口。用户看不到后端代码,但它是整个应用的核心。
Node.js是一个JavaScript运行时环境,允许在服务器上运行JavaScript代码。它基于Chrome的V8引擎,具有高性能和非阻塞I/O。
# 安装Node.js
# https://nodejs.org/
# 验证安装
node --version
npm --version
# 创建项目
mkdir myapp
cd myapp
npm init -y
# 创建服务器
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!');
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
# 运行服务器
node server.js// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = { add, subtract };
// main.js
const math = require('./math');
console.log(math.add(5, 3)); // 8
console.log(math.subtract(5, 3)); // 2
// 使用ES6模块
// math.js
export function add(a, b) {
return a + b;
}
// main.js
import { add } from './math.js';
console.log(add(5, 3)); // 8// 回调函数
function readFile(filename, callback) {
setTimeout(() => {
callback(null, 'file content');
}, 1000);
}
readFile('file.txt', (err, data) => {
if (err) console.error(err);
else console.log(data);
});
// Promise
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('file content');
}, 1000);
});
}
readFilePromise('file.txt')
.then(data => console.log(data))
.catch(err => console.error(err));
// async/await
async function main() {
try {
const data = await readFilePromise('file.txt');
console.log(data);
} catch (err) {
console.error(err);
}
}
main();Express是最流行的Node.js Web框架。它提供了路由、中间件、模板引擎等功能,使开发Web应用变得简单。
# 安装Express
npm install express
// app.js
const express = require('express');
const app = express();
// 中间件
app.use(express.json());
app.use(express.static('public'));
// 路由
app.get('/', (req, res) => {
res.json({ message: 'Hello, World!' });
});
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ userId });
});
app.post('/users', (req, res) => {
const { name, email } = req.body;
res.json({ name, email });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});// 获取请求数据
app.post('/api/users', (req, res) => {
// 获取请求体
const { name, email } = req.body;
// 获取查询参数
const { page, limit } = req.query;
// 获取路由参数
const { id } = req.params;
// 获取请求头
const token = req.headers.authorization;
// 获取cookies
const sessionId = req.cookies.sessionId;
// 发送响应
res.status(201).json({
success: true,
data: { name, email }
});
});
// 发送文件
app.get('/download', (req, res) => {
res.download('file.pdf');
});
// 重定向
app.get('/old-path', (req, res) => {
res.redirect('/new-path');
});// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
success: false,
error: err.message
});
});
// 404处理
app.use((req, res) => {
res.status(404).json({
success: false,
error: 'Not Found'
});
});// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({ users: [] });
});
router.get('/:id', (req, res) => {
res.json({ userId: req.params.id });
});
router.post('/', (req, res) => {
res.status(201).json({ created: true });
});
module.exports = router;
// app.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);// 应用级中间件
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
// 路由级中间件
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
};
app.get('/protected', authenticate, (req, res) => {
res.json({ data: 'secret' });
});
// 错误处理中间件
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
// 条件中间件
app.get('/api/data',
authenticate,
(req, res, next) => {
// 验证权限
next();
},
(req, res) => {
res.json({ data: [] });
}
);const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const app = express();
// 安全
app.use(helmet());
// CORS
app.use(cors());
// 日志
app.use(morgan('combined'));
// 解析请求体
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 解析cookies
app.use(cookieParser());const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// 查询
app.get('/api/users', async (req, res) => {
try {
const [rows] = await pool.query('SELECT * FROM users');
res.json(rows);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 插入
app.post('/api/users', async (req, res) => {
try {
const { username, email } = req.body;
const [result] = await pool.query(
'INSERT INTO users (username, email) VALUES (?, ?)',
[username, email]
);
res.status(201).json({ id: result.insertId });
} catch (error) {
res.status(500).json({ error: error.message });
}
});const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
app.get('/api/users', async (req, res) => {
try {
await client.connect();
const db = client.db('myapp');
const users = await db.collection('users').find().toArray();
res.json(users);
} finally {
await client.close();
}
});
// 使用Mongoose ORM
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
app.get('/api/users', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
});const bcrypt = require('bcrypt');
// 注册
app.post('/api/register', async (req, res) => {
try {
const { username, password } = req.body;
// 密码加密
const hashedPassword = await bcrypt.hash(password, 10);
// 保存到数据库
await User.create({
username,
password: hashedPassword
});
res.status(201).json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 登录
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// 验证密码
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
res.json({ success: true, userId: user._id });
} catch (error) {
res.status(500).json({ error: error.message });
}
});const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET || 'your-secret-key';
// 生成token
function generateToken(userId) {
return jwt.sign({ userId }, SECRET, { expiresIn: '24h' });
}
// 验证token
function verifyToken(token) {
try {
return jwt.verify(token, SECRET);
} catch (error) {
return null;
}
}
// 登录
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = generateToken(user._id);
res.json({ token });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 认证中间件
const authenticate = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Invalid token' });
}
req.userId = decoded.userId;
next();
};
// 使用认证中间件
app.get('/api/profile', authenticate, async (req, res) => {
const user = await User.findById(req.userId);
res.json(user);
});// 获取所有用户
app.get('/api/users', async (req, res) => {
try {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const users = await User.find()
.skip(skip)
.limit(limit);
const total = await User.countDocuments();
res.json({
data: users,
pagination: { page, limit, total }
});
} catch (error) {
res.status(500).json({ error: 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({ error: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 创建用户
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({ error: error.message });
}
});
// 更新用户
app.put('/api/users/:id', async (req, res) => {
try {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true }
);
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 删除用户
app.delete('/api/users/:id', async (req, res) => {
try {
await User.findByIdAndDelete(req.params.id);
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'My API',
version: '1.0.0'
},
servers: [
{ url: 'http://localhost:3000' }
]
},
apis: ['./routes/*.js']
};
const specs = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));// 自定义错误类
class AppError extends Error {
constructor(message, status) {
super(message);
this.status = status;
}
}
// 异步错误包装
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// 使用
app.get('/api/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new AppError('User not found', 404);
}
res.json(user);
}));
// 全局错误处理
app.use((err, req, res, next) => {
const status = err.status || 500;
const message = err.message || 'Internal Server Error';
console.error(`[${status}] ${message}`);
res.status(status).json({
success: false,
error: message
});
});const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: 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()
}));
}
// 使用日志
app.get('/api/users', (req, res) => {
logger.info('Fetching users');
res.json({ users: [] });
});# .env
NODE_ENV=production
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key
LOG_LEVEL=info
// app.js
require('dotenv').config();
const PORT = process.env.PORT || 3000;
const DATABASE_URL = process.env.DATABASE_URL;# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
depends_on:
- mongo
mongo:
image: mongo:5
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
volumes:
mongo_data:# 安装PM2
npm install -g pm2
# 启动应用
pm2 start app.js --name "myapp"
# 监控
pm2 monit
# 查看日志
pm2 logs myapp
# 重启
pm2 restart myapp
# 停止
pm2 stop myappproject/
├── src/
│ ├── routes/
│ │ ├── users.js
│ │ ├── posts.js
│ │ └── comments.js
│ ├── models/
│ │ ├── User.js
│ │ ├── Post.js
│ │ └── Comment.js
│ ├── middleware/
│ │ ├── auth.js
│ │ └── errorHandler.js
│ ├── controllers/
│ │ ├── userController.js
│ │ ├── postController.js
│ │ └── commentController.js
│ ├── config/
│ │ └── database.js
│ └── app.js
├── .env
├── .env.example
├── package.json
└── README.md// src/models/Post.js
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Post', postSchema);
// src/controllers/postController.js
const Post = require('../models/Post');
exports.getPosts = async (req, res, next) => {
try {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const posts = await Post.find()
.populate('author', 'username')
.skip(skip)
.limit(limit)
.sort({ createdAt: -1 });
const total = await Post.countDocuments();
res.json({
data: posts,
pagination: { page, limit, total }
});
} catch (error) {
next(error);
}
};
exports.createPost = async (req, res, next) => {
try {
const { title, content } = req.body;
const post = await Post.create({
title,
content,
author: req.userId
});
res.status(201).json(post);
} catch (error) {
next(error);
}
};通过这个项目,你学会了如何构建一个完整的、生产级别的后端API系统。
现在你已经掌握了后端开发的核心技能。
后端开发的关键是理解业务逻辑和系统设计。不断实践和优化是成为优秀后端工程师的必经之路。