Node.js服务器开发完全指南 作为一名从零开始学习Node.js的开发者,我深知这个旅程的不易。还记得第一次用Node.js搭建服务器时的兴奋,也有被异步编程搞晕的瞬间。今天,我想以过来人的身份,系统地为大家梳理Node.js服务器开发的方方面面,帮你少走弯路。
一、Node.js简介 什么是Node.js? Node.js是一个基于Chrome V8引擎的JavaScript运行时环境。它让JavaScript可以在服务器端运行,实现了”用JavaScript写全栈”的梦想。
node --version npm --version
Node.js的特点 异步非阻塞I/O :事件循环机制让Node.js能处理大量并发连接单线程事件驱动 :通过事件循环实现高并发轻量高效 :相比传统的多线程服务器,资源占用更少丰富的npm生态 :拥有世界上最大的包管理系统为什么选择Node.js? 二、基础概念 事件循环 Node.js的核心是事件循环机制。让我们深入理解它的工作原理:
console .log ('1' ) console .log ('2' ) setTimeout (() => console .log ('3' ), 0 ) console .log ('4' ) const start = Date .now ()console .log ('开始同步操作' )for (let i = 0 ; i < 1000000 ; i++) { Math .sqrt (i) } console .log ('同步操作完成,耗时:' , Date .now () - start, 'ms' )setTimeout (() => console .log ('定时器回调' ), 0 )
异步编程模式 Node.js支持多种异步编程模式:
fs.readFile ('file.txt' , (err, data ) => { if (err) { console .error ('读取失败:' , err) return } console .log (data) }) fs.promises .readFile ('file.txt' ) .then (data => console .log (data)) .catch (err => console .error ('读取失败:' , err)) async function readFile ( ) { try { const data = await fs.promises .readFile ('file.txt' ) console .log (data) } catch (err) { console .error ('读取失败:' , err) } }
模块系统 Node.js使用CommonJS模块系统:
const PI = 3.14159 const circleArea = (radius ) => PI * radius * radiusconst squareArea = (side ) => side * sidemodule .exports = { PI , circleArea, squareArea } const { PI , circleArea, squareArea } = require ('./module' )console .log (PI ) console .log (circleArea (5 )) console .log (squareArea (4 )) export const PI = 3.14159 export const circleArea = (radius ) => PI * radius * radiusimport { PI , circleArea } from './module.mjs'
三、搭建基础服务器 简单HTTP服务器 const http = require ('http' )const url = require ('url' )const server = http.createServer ((req, res ) => { const parsedUrl = url.parse (req.url , true ) res.writeHead (200 , { 'Content-Type' : 'text/plain' , 'X-Powered-By' : 'Node.js' }) if (parsedUrl.pathname === '/' ) { res.end ('Welcome to Node.js Server!' ) } else if (parsedUrl.pathname === '/about' ) { res.end ('About Us' ) } else if (parsedUrl.pathname === '/api/users' ) { const users = [ { id : 1 , name : '张三' }, { id : 2 , name : '李四' } ] res.end (JSON .stringify (users)) } else { res.writeHead (404 ) res.end ('404 Not Found' ) } }) const PORT = 3000 server.listen (PORT , () => { console .log (`Server running on port ${PORT} ` ) })
使用中间件概念 function logger (req, res, next ) { console .log (`${new Date ().toISOString()} - ${req.method} ${req.url} ` ) next () } function authenticate (req, res, next ) { const token = req.headers .authorization if (token === 'secret-token' ) { req.user = { id : 1 , name : '张三' } next () } else { res.writeHead (401 ) res.end ('Unauthorized' ) } } const server = http.createServer ((req, res ) => { logger (req, res, () => { if (req.url .startsWith ('/protected' )) { authenticate (req, res, () => { res.writeHead (200 , { 'Content-Type' : 'application/json' }) res.end (JSON .stringify (req.user )) }) } else { res.writeHead (200 , { 'Content-Type' : 'text/plain' }) res.end ('Public content' ) } }) })
四、Express框架 安装Express npm init -y npm install express
基础Express应用 const express = require ('express' )const app = express ()const port = 3000 app.use (express.json ()) app.use (express.urlencoded ({ extended : true })) app.use (express.static ('public' )) app.get ('/' , (req, res ) => { res.send ('Hello World!' ) }) app.get ('/users' , (req, res ) => { const users = [ { id : 1 , name : '张三' }, { id : 2 , name : '李四' } ] res.json (users) }) app.get ('/users/:id' , (req, res ) => { const { id } = req.params const user = { id : parseInt (id), name : '用户' + id } res.json (user) }) app.get ('/search' , (req, res ) => { const { q, limit } = req.query res.json ({ searchQuery : q, limit : limit || 10 }) }) app.post ('/users' , (req, res ) => { const { name, email } = req.body const newUser = { id : Date .now (), name, email } res.status (201 ).json (newUser) }) app.use ((err, req, res, next ) => { console .error (err.stack ) res.status (500 ).send ('Something broke!' ) }) app.use ((req, res ) => { res.status (404 ).send ('Not Found' ) }) app.listen (port, () => { console .log (`Server running on port ${port} ` ) })
路由分离 const express = require ('express' )const router = express.Router ()router.get ('/' , (req, res ) => { res.json ([{ id : 1 , name : '张三' }, { id : 2 , name : '李四' }]) }) router.get ('/:id' , (req, res ) => { res.json ({ id : req.params .id , name : '用户' + req.params .id }) }) router.post ('/' , (req, res ) => { res.status (201 ).json ({ ...req.body , id : Date .now () }) }) module .exports = routerconst express = require ('express' )const router = express.Router ()router.get ('/' , (req, res ) => { res.json ([{ id : 1 , title : '第一篇文章' }]) }) router.get ('/:id' , (req, res ) => { res.json ({ id : req.params .id , title : '文章' + req.params .id }) }) module .exports = routerconst express = require ('express' )const app = express ()const port = 3000 const userRoutes = require ('./routes/users' )const postRoutes = require ('./routes/posts' )app.use (express.json ()) app.use ('/api/users' , userRoutes) app.use ('/api/posts' , postRoutes) app.get ('/' , (req, res ) => { res.send ('API Server' ) }) app.listen (port, () => { console .log (`Server running on port ${port} ` ) })
五、数据库集成 MongoDB + Mongoose const mongoose = require ('mongoose' )const userSchema = new mongoose.Schema ({ name : { type : String , required : true , trim : true }, email : { type : String , required : true , unique : true , lowercase : true }, age : { type : Number , min : 0 , max : 120 }, isActive : { type : Boolean , default : true } }, { timestamps : true }) userSchema.virtual ('fullName' ).get (function ( ) { return `${this .name} (${this .email} )` }) userSchema.methods .sayHello = function ( ) { return `Hello, my name is ${this .name} ` } userSchema.statics .findByEmail = function (email ) { return this .findOne ({ email }) } const User = mongoose.model ('User' , userSchema)module .exports = User
const User = require ('../models/User' )exports .getAllUsers = async (req, res) => { try { const users = await User .find () res.json (users) } catch (err) { res.status (500 ).json ({ message : err.message }) } } exports .getUserById = 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 (err) { res.status (500 ).json ({ message : err.message }) } } exports .createUser = async (req, res) => { const user = new User ({ name : req.body .name , email : req.body .email , age : req.body .age }) try { const newUser = await user.save () res.status (201 ).json (newUser) } catch (err) { res.status (400 ).json ({ message : err.message }) } } exports .updateUser = async (req, res) => { try { const updatedUser = await User .findByIdAndUpdate ( req.params .id , req.body , { new : true , runValidators : true } ) if (!updatedUser) { return res.status (404 ).json ({ message : 'User not found' }) } res.json (updatedUser) } catch (err) { res.status (400 ).json ({ message : err.message }) } } exports .deleteUser = async (req, res) => { try { const user = await User .findByIdAndDelete (req.params .id ) if (!user) { return res.status (404 ).json ({ message : 'User not found' }) } res.json ({ message : 'User deleted' }) } catch (err) { res.status (500 ).json ({ message : err.message }) } }
const express = require ('express' )const router = express.Router ()const userController = require ('../controllers/userController' )router.get ('/' , userController.getAllUsers ) router.get ('/:id' , userController.getUserById ) router.post ('/' , userController.createUser ) router.put ('/:id' , userController.updateUser ) router.delete ('/:id' , userController.deleteUser ) module .exports = router
MySQL + Sequelize npm install mysql2 sequelize
const { Sequelize } = require ('sequelize' )const sequelize = new Sequelize ('test_db' , 'root' , 'password' , { host : 'localhost' , dialect : 'mysql' , logging : console .log , pool : { max : 5 , min : 0 , acquire : 30000 , idle : 10000 } }) module .exports = sequelize
const { DataTypes } = require ('sequelize' )const sequelize = require ('../config/database' )const User = sequelize.define ('User' , { name : { type : DataTypes .STRING , allowNull : false , validate : { len : [2 , 50 ] } }, email : { type : DataTypes .STRING , allowNull : false , unique : true , validate : { isEmail : true } }, age : { type : DataTypes .INTEGER , allowNull : true , validate : { min : 0 , max : 150 } }, isActive : { type : DataTypes .BOOLEAN , defaultValue : true } }, { tableName : 'users' , timestamps : true }) module .exports = User
const sequelize = require ('./database' )const User = require ('./User' )const syncDatabase = async ( ) => { try { await sequelize.authenticate () console .log ('数据库连接成功' ) await sequelize.sync ({ force : false }) console .log ('数据库同步完成' ) } catch (error) { console .error ('数据库连接失败:' , error) } } module .exports = { sequelize, User , syncDatabase }
六、身份验证与授权 JWT认证 npm install jsonwebtoken bcryptjs
const jwt = require ('jsonwebtoken' )const bcrypt = require ('bcryptjs' )const JWT_SECRET = 'your-secret-key' const generateToken = (user ) => { return jwt.sign ( { id : user.id , email : user.email }, JWT_SECRET , { expiresIn : '1h' } ) } const verifyToken = (token ) => { try { return jwt.verify (token, JWT_SECRET ) } catch (err) { return null } } const hashPassword = async (password ) => { const saltRounds = 10 return await bcrypt.hash (password, saltRounds) } const verifyPassword = async (password, hashedPassword ) => { return await bcrypt.compare (password, hashedPassword) } module .exports = { generateToken, verifyToken, hashPassword, verifyPassword }
const { verifyToken } = require ('../utils/auth' )exports .authenticate = (req, res, next ) => { const token = req.header ('Authorization' )?.replace ('Bearer ' , '' ) if (!token) { return res.status (401 ).json ({ message : 'Access denied' }) } const decoded = verifyToken (token) if (!decoded) { return res.status (401 ).json ({ message : 'Invalid token' }) } req.user = decoded next () } exports .authorize = (roles ) => { return (req, res, next ) => { if (!roles.includes (req.user .role )) { return res.status (403 ).json ({ message : 'Access denied' }) } next () } }
const express = require ('express' )const router = express.Router ()const User = require ('../models/User' )const { generateToken, hashPassword, verifyPassword } = require ('../utils/auth' )router.post ('/register' , async (req, res) => { try { const { name, email, password, role = 'user' } = req.body const existingUser = await User .findByEmail (email) if (existingUser) { return res.status (400 ).json ({ message : 'User already exists' }) } const hashedPassword = await hashPassword (password) const user = new User ({ name, email, password : hashedPassword, role }) await user.save () const token = generateToken (user) res.status (201 ).json ({ message : 'User created successfully' , token, user : { id : user._id , name : user.name , email : user.email , role : user.role } }) } catch (err) { res.status (500 ).json ({ message : err.message }) } }) router.post ('/login' , async (req, res) => { try { const { email, password } = req.body const user = await User .findByEmail (email) if (!user) { return res.status (400 ).json ({ message : 'Invalid credentials' }) } const isPasswordValid = await verifyPassword (password, user.password ) if (!isPasswordValid) { return res.status (400 ).json ({ message : 'Invalid credentials' }) } const token = generateToken (user) res.json ({ message : 'Login successful' , token, user : { id : user._id , name : user.name , email : user.email , role : user.role } }) } catch (err) { res.status (500 ).json ({ message : err.message }) } }) module .exports = router
七、文件上传 处理文件上传 const multer = require ('multer' )const path = require ('path' )const storage = multer.diskStorage ({ destination : (req, file, cb ) => { cb (null , 'uploads/' ) }, filename : (req, file, cb ) => { const uniqueSuffix = Date .now () + '-' + Math .round (Math .random () * 1E9 ) cb (null , file.fieldname + '-' + uniqueSuffix + path.extname (file.originalname )) } }) const fileFilter = (req, file, cb ) => { const allowedTypes = ['image/jpeg' , 'image/png' , 'image/gif' ] if (allowedTypes.includes (file.mimetype )) { cb (null , true ) } else { cb (new Error ('Invalid file type' ), false ) } } const upload = multer ({ storage : storage, limits : { fileSize : 5 * 1024 * 1024 , files : 10 }, fileFilter : fileFilter }) module .exports = upload
const express = require ('express' )const router = express.Router ()const upload = require ('../utils/upload' )const path = require ('path' )router.post ('/single' , upload.single ('file' ), (req, res ) => { if (!req.file ) { return res.status (400 ).json ({ message : 'No file uploaded' }) } res.json ({ message : 'File uploaded successfully' , file : { originalName : req.file .originalname , filename : req.file .filename , path : req.file .path , size : req.file .size } }) }) router.post ('/multiple' , upload.array ('files' , 5 ), (req, res ) => { if (!req.files || req.files .length === 0 ) { return res.status (400 ).json ({ message : 'No files uploaded' }) } const files = req.files .map (file => ({ originalName : file.originalname , filename : file.filename , path : file.path , size : file.size })) res.json ({ message : 'Files uploaded successfully' , files : files }) }) module .exports = router
八、API设计最佳实践 RESTful API设计 const express = require ('express' )const router = express.Router ()const Product = require ('../../models/Product' )router.get ('/' , async (req, res) => { try { const { page = 1 , limit = 10 , category, minPrice, maxPrice } = req.query const query = {} if (category) query.category = category if (minPrice || maxPrice) { query.price = {} if (minPrice) query.price .$gte = parseFloat (minPrice) if (maxPrice) query.price .$lte = parseFloat (maxPrice) } const products = await Product .find (query) .limit (limit * 1 ) .skip ((page - 1 ) * limit) .lean () const total = await Product .countDocuments (query) res.json ({ products, pagination : { current : parseInt (page), pages : Math .ceil (total / limit), total } }) } catch (err) { res.status (500 ).json ({ message : err.message }) } }) router.get ('/:id' , async (req, res) => { try { const product = await Product .findById (req.params .id ) if (!product) { return res.status (404 ).json ({ message : 'Product not found' }) } res.json (product) } catch (err) { res.status (500 ).json ({ message : err.message }) } }) router.post ('/' , async (req, res) => { const product = new Product ({ name : req.body .name , description : req.body .description , price : req.body .price , category : req.body .category , stock : req.body .stock }) try { const newProduct = await product.save () res.status (201 ).json (newProduct) } catch (err) { res.status (400 ).json ({ message : err.message }) } }) router.put ('/:id' , async (req, res) => { try { const product = await Product .findByIdAndUpdate ( req.params .id , req.body , { new : true , runValidators : true } ) if (!product) { return res.status (404 ).json ({ message : 'Product not found' }) } res.json (product) } catch (err) { res.status (400 ).json ({ message : err.message }) } }) router.delete ('/:id' , async (req, res) => { try { const product = await Product .findByIdAndDelete (req.params .id ) if (!product) { return res.status (404 ).json ({ message : 'Product not found' }) } res.json ({ message : 'Product deleted' }) } catch (err) { res.status (500 ).json ({ message : err.message }) } }) module .exports = router
API响应标准化 class ApiResponse { constructor (success, statusCode, message, data = null ) { this .success = success this .statusCode = statusCode this .message = message this .data = data this .timestamp = new Date ().toISOString () } static success (message, data = null , statusCode = 200 ) { return new ApiResponse (true , statusCode, message, data) } static error (message, statusCode = 500 ) { return new ApiResponse (false , statusCode, message, null ) } static created (message, data = null ) { return this .success (message, data, 201 ) } static badRequest (message ) { return this .error (message, 400 ) } static unauthorized (message ) { return this .error (message, 401 ) } static forbidden (message ) { return this .error (message, 403 ) } static notFound (message ) { return this .error (message, 404 ) } static internalError (message ) { return this .error (message, 500 ) } } module .exports = ApiResponse
const ApiResponse = require ('../utils/response' )exports .getProduct = async (req, res) => { try { const product = await Product .findById (req.params .id ) if (!product) { return res.status (404 ).json (ApiResponse .notFound ('Product not found' )) } res.json (ApiResponse .success ('Product retrieved successfully' , product)) } catch (err) { res.status (500 ).json (ApiResponse .internalError (err.message )) } }
九、性能优化 缓存策略 const NodeCache = require ('node-cache' )const cache = new NodeCache ({ stdTTL : 1000 , checkperiod : 600 , useClones : false }) const generateCacheKey = (prefix, params ) => { return `${prefix} :${JSON .stringify(params)} ` } const cachedQuery = async (key, queryFn, ttl = 300 ) => { const cached = cache.get (key) if (cached) { console .log (`Cache hit for key: ${key} ` ) return cached } const result = await queryFn () cache.set (key, result, ttl) console .log (`Cache set for key: ${key} ` ) return result } module .exports = { cache, generateCacheKey, cachedQuery }
const { generateCacheKey, cachedQuery } = require ('../utils/cache' )exports .getProducts = async (req, res) => { const { category } = req.query const cacheKey = generateCacheKey ('products' , { category }) const products = await cachedQuery ( cacheKey, async () => Product .find ({ category : category || { $exists : true } }), 600 ) res.json (ApiResponse .success ('Products retrieved' , products)) }
数据库优化 const sequelize = require ('./database' )const { DataTypes } = require ('sequelize' )const User = sequelize.define ('User' , { name : DataTypes .STRING , email : { type : DataTypes .STRING , unique : true }, createdAt : DataTypes .DATE , updatedAt : DataTypes .DATE }) const Product = sequelize.define ('Product' , { name : DataTypes .STRING , price : DataTypes .DECIMAL (10 , 2 ), category : DataTypes .STRING , stock : DataTypes .INTEGER , createdAt : DataTypes .DATE , updatedAt : DataTypes .DATE }) User .hasMany (Product , { as : 'products' , foreignKey : 'userId' })Product .belongsTo (User , { as : 'user' , foreignKey : 'userId' })const optimizeDatabase = async ( ) => { await User .sync () await Product .sync () await sequelize.query (` CREATE INDEX IF NOT EXISTS idx_products_category ON Products(category) ` ) await sequelize.query (` CREATE INDEX IF NOT EXISTS idx_products_price ON Products(price) ` ) console .log ('Database optimization completed' ) } module .exports = { sequelize, User , Product , optimizeDatabase }
十、错误处理 自定义错误类 class AppError extends Error { constructor (message, statusCode = 500 ) { super (message) this .statusCode = statusCode this .isOperational = true Error .captureStackTrace (this , this .constructor ) } } class BusinessError extends AppError { constructor (message ) { super (message, 400 ) } } class AuthenticationError extends AppError { constructor (message ) { super (message, 401 ) } } class AuthorizationError extends AppError { constructor (message ) { super (message, 403 ) } } class NotFoundError extends AppError { constructor (resource ) { super (`${resource} not found` , 404 ) } } module .exports = { AppError , BusinessError , AuthenticationError , AuthorizationError , NotFoundError }
全局错误处理 const { AppError } = require ('../utils/errors' )const errorHandler = (err, req, res, next ) => { let error = { ...err } error.message = err.message console .error (err) if (err.name === 'SequelizeValidationError' ) { const message = err.errors .map (error => error.message ).join (', ' ) error = new AppError (message, 400 ) } if (err.name === 'SequelizeUniqueConstraintError' ) { const message = 'Duplicate field value entered' error = new AppError (message, 400 ) } if (err.name === 'SequelizeForeignKeyConstraintError' ) { const message = 'Invalid reference ID' error = new AppError (message, 400 ) } if (err.name === 'CastError' ) { const message = `Resource not found with id of ${err.value} ` error = new AppError (message, 404 ) } if (err.name === 'ValidationError' ) { const message = Object .values (err.errors ).map (val => val.message ).join (', ' ) error = new AppError (message, 400 ) } if (err.code === 11000 ) { const field = Object .keys (err.keyPattern )[0 ] const message = `Duplicate value entered for ${field} field` error = new AppError (message, 400 ) } if (err.name === 'JsonWebTokenError' ) { const message = 'Invalid token. Please log in again.' error = new AppError (message, 401 ) } if (err.name === 'TokenExpiredError' ) { const message = 'Your token has expired. Please log in again.' error = new AppError (message, 401 ) } res.status (error.statusCode || 500 ).json ({ success : false , error : error.message || 'Internal Server Error' , ...(process.env .NODE_ENV === 'development' && { stack : err.stack }) }) } module .exports = errorHandler
const { NotFoundError } = require ('../utils/errors' )exports .getProduct = async (req, res, next) => { try { const product = await Product .findById (req.params .id ) if (!product) { return next (new NotFoundError ('Product' )) } res.json (ApiResponse .success ('Product retrieved' , product)) } catch (err) { next (err) } }
十一、测试 单元测试 const request = require ('supertest' )const app = require ('../app' )const User = require ('../models/User' )const { generateToken } = require ('../utils/auth' )describe ('User API' , () => { let testToken let testUser beforeEach (async () => { await User .deleteMany ({}) testUser = new User ({ name : 'Test User' , email : 'test@example.com' , password : 'password123' }) await testUser.save () testToken = generateToken (testUser) }) describe ('POST /api/users' , () => { it ('should create a new user' , async () => { const res = await request (app) .post ('/api/users' ) .send ({ name : 'New User' , email : 'new@example.com' , password : 'password123' }) expect (res.statusCode ).toEqual (201 ) expect (res.body .success ).toBe (true ) expect (res.body .data .name ).toBe ('New User' ) }) it ('should not create user with duplicate email' , async () => { const res = await request (app) .post ('/api/users' ) .send ({ name : 'Duplicate User' , email : 'test@example.com' , password : 'password123' }) expect (res.statusCode ).toEqual (400 ) expect (res.body .success ).toBe (false ) }) }) describe ('GET /api/users' , () => { it ('should get all users' , async () => { const res = await request (app) .get ('/api/users' ) .set ('Authorization' , `Bearer ${testToken} ` ) expect (res.statusCode ).toEqual (200 ) expect (res.body .success ).toBe (true ) expect (res.body .data .length ).toBeGreaterThan (0 ) }) }) describe ('GET /api/users/:id' , () => { it ('should get user by id' , async () => { const res = await request (app) .get (`/api/users/${testUser._id} ` ) .set ('Authorization' , `Bearer ${testToken} ` ) expect (res.statusCode ).toEqual (200 ) expect (res.body .success ).toBe (true ) expect (res.body .data .name ).toBe ('Test User' ) }) it ('should not get non-existent user' , async () => { const res = await request (app) .get ('/api/users/507f1f77bcf86cd799439011' ) .set ('Authorization' , `Bearer ${testToken} ` ) expect (res.statusCode ).toEqual (404 ) }) }) })
集成测试 const request = require ('supertest' )const app = require ('../app' )const User = require ('../models/User' )describe ('Auth Integration Tests' , () => { beforeEach (async () => { await User .deleteMany ({}) }) describe ('POST /api/auth/register' , () => { it ('should register a new user and return token' , async () => { const res = await request (app) .post ('/api/auth/register' ) .send ({ name : 'John Doe' , email : 'john@example.com' , password : 'password123' }) expect (res.statusCode ).toEqual (201 ) expect (res.body .success ).toBe (true ) expect (res.body .token ).toBeDefined () expect (res.body .data .name ).toBe ('John Doe' ) }) }) describe ('POST /api/auth/login' , () => { beforeEach (async () => { await request (app) .post ('/api/auth/register' ) .send ({ name : 'John Doe' , email : 'john@example.com' , password : 'password123' }) }) it ('should login with valid credentials' , async () => { const res = await request (app) .post ('/api/auth/login' ) .send ({ email : 'john@example.com' , password : 'password123' }) expect (res.statusCode ).toEqual (200 ) expect (res.body .success ).toBe (true ) expect (res.body .token ).toBeDefined () }) it ('should not login with invalid credentials' , async () => { const res = await request (app) .post ('/api/auth/login' ) .send ({ email : 'john@example.com' , password : 'wrongpassword' }) expect (res.statusCode ).toEqual (400 ) expect (res.body .success ).toBe (false ) }) }) })
十二、部署 生产环境配置 module .exports = { port : process.env .PORT || 8080 , dbUrl : process.env .DATABASE_URL , jwtSecret : process.env .JWT_SECRET , jwtExpiresIn : process.env .JWT_EXPIRES_IN || '1h' , nodeEnv : process.env .NODE_ENV || 'production' }
PM2进程管理 module.exports = { apps: [ { name: 'node-api', script: 'app.js', instances: 'max', exec_mode: 'cluster', env: { NODE_ENV: 'development' } , env_production: { NODE_ENV: 'production', PORT: 8080 } } ] }
Docker部署 FROM node:16 -alpineWORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 8080 CMD ["npm" , "start" ]
version: '3.8' services: node-api: build: . ports: - "8080:8080" environment: - NODE_ENV=production - PORT=8080 - DATABASE_URL=postgresql://user:pass@db:5432/mydb depends_on: - db restart: unless-stopped db: image: postgres:13 environment: POSTGRES_DB: mydb POSTGRES_USER: user POSTGRES_PASSWORD: pass volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" restart: unless-stopped volumes: postgres_data:
十三、完整项目示例 const express = require ('express' )const cors = require ('cors' )const morgan = require ('morgan' )const helmet = require ('helmet' )const compression = require ('compression' )const rateLimit = require ('express-rate-limit' )const userRoutes = require ('./routes/users' )const authRoutes = require ('./routes/auth' )const productRoutes = require ('./routes/products' )const uploadRoutes = require ('./routes/upload' )const { authenticate } = require ('./middleware/auth' )const errorHandler = require ('./middleware/errorHandler' )const config = require ('./config/production' )const app = express ()app.use (helmet ()) app.use (cors ({ origin : process.env .NODE_ENV === 'production' ? 'https://yourdomain.com' : 'http://localhost:3000' , credentials : true })) if (process.env .NODE_ENV === 'development' ) { app.use (morgan ('dev' )) } else { app.use (morgan ('combined' )) } const limiter = rateLimit ({ windowMs : 15 * 60 * 1000 , max : 100 , message : 'Too many requests from this IP, please try again later.' }) app.use ('/api/' , limiter) app.use (compression ()) app.use (express.json ({ limit : '10mb' })) app.use (express.urlencoded ({ extended : true })) app.get ('/health' , (req, res ) => { res.json ({ status : 'OK' , timestamp : new Date ().toISOString (), uptime : process.uptime () }) }) app.use ('/api/auth' , authRoutes) app.use ('/api/users' , authenticate, userRoutes) app.use ('/api/products' , authenticate, productRoutes) app.use ('/api/upload' , authenticate, uploadRoutes) app.use (errorHandler) app.use ('*' , (req, res ) => { res.status (404 ).json ({ success : false , message : 'Route not found' }) }) const PORT = config.port || 3000 app.listen (PORT , () => { console .log (`Server running on port ${PORT} in ${config.nodeEnv} mode` ) }) process.on ('SIGTERM' , () => { console .log ('SIGTERM received. Shutting down gracefully...' ) server.close (() => { console .log ('Process terminated' ) process.exit (0 ) }) }) process.on ('SIGINT' , () => { console .log ('SIGINT received. Shutting down gracefully...' ) server.close (() => { console .log ('Process terminated' ) process.exit (0 ) }) })
十四、总结 Node.js服务器开发是一个广阔的领域,从基础的HTTP服务器到复杂的微服务架构,都需要深入理解和实践经验。
关键要点: 基础扎实 :理解事件循环、异步编程、模块系统框架选择 :Express是行业标准,但也可以考虑NestJS、Fastify等数据库集成 :MongoDB+Mongoose或MySQL+Sequelize安全认证 :JWT是目前最流行的认证方式性能优化 :缓存、数据库索引、集群模式测试 :单元测试和集成测试确保代码质量部署 :容器化、进程管理、监控最佳实践: 代码组织 :按功能模块组织代码错误处理 :统一的错误处理机制API设计 :RESTful风格,标准响应格式安全防护 :输入验证、SQL注入防护、XSS防护性能监控 :响应时间、错误率、资源使用Node.js的优势在于其JavaScript生态系统和高效的并发处理能力。选择合适的工具和架构模式,可以构建出高性能、可扩展的后端应用。
如果你在实际开发中遇到任何问题,欢迎在评论区留言交流。祝学习愉快!🎉
最后更新:2026年5月14日 分类:Node.js | 服务器开发 | 后端开发 | JavaScript后端 | API