Node.js 入门教程 — JavaScript 后端开发

前置要求:JavaScript基础
学习时长:约2-3天(每天4小时)
Node.js版本:20+(LTS)
适用场景:后端API、实时应用、CLI工具、微服务

一、Node.js 是什么?

Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时,让 JavaScript 可以在服务器端运行。

特点说明
语言JavaScript(前端开发者无缝切换)
性能非阻塞I/O,高并发处理能力强
生态npm是全球最大的包管理器
适用场景REST API、实时应用、CLI工具、BFF层

二、安装与配置

# macOS
brew install node

# Ubuntu
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs

# Windows
# 下载:https://nodejs.org/

# 验证
node -v
npm -v

# 包管理器(推荐pnpm)
npm install -g pnpm
pnpm -v


三、基础语法

// ====================
// 模块系统
// ====================

// ES Modules(推荐)
import fs from 'fs'
import path from 'path'
import { readFile, writeFile } from 'fs/promises'

// CommonJS(传统)
const fs = require('fs')
const path = require('path')

// ====================
// 内置模块
// ====================
import fs from 'fs/promises'
import path from 'path'
import os from 'os'
import crypto from 'crypto'

// 文件操作
const content = await fs.readFile('data.txt', 'utf-8')
await fs.writeFile('output.txt', 'Hello, Node.js!')
await fs.mkdir('data', { recursive: true })

// 路径操作
const fullPath = path.join(__dirname, 'data', 'file.txt')
const ext = path.extname('file.txt')  // '.txt'
const basename = path.basename('/path/to/file.txt')  // 'file.txt'

// 系统信息
console.log(os.platform())     // 'win32' / 'darwin' / 'linux'
console.log(os.cpus().length)  // CPU核心数
console.log(os.totalmem())     // 总内存

// 加密
const hash = crypto.createHash('sha256').update('password').digest('hex')
const uuid = crypto.randomUUID()

// ====================
// 环境变量
// ====================
const port = process.env.PORT || 3000
const nodeEnv = process.env.NODE_ENV || 'development'


四、Express 框架

# 安装
pnpm add express

# 创建项目
mkdir my-api && cd my-api
pnpm init
pnpm add express cors helmet morgan
pnpm add -D nodemon

// app.js
import express from 'express'
import cors from 'cors'
import helmet from 'helmet'
import morgan from 'morgan'

const app = express()
const PORT = process.env.PORT || 3000

// 中间件
app.use(helmet())         // 安全头
app.use(cors())           // 跨域
app.use(morgan('dev'))    // 日志
app.use(express.json())   // 解析JSON
app.use(express.urlencoded({ extended: true }))  // 解析表单

// 模拟数据
let users = [
  { id: 1, username: '张三', email: 'zhangsan@example.com', age: 25 },
  { id: 2, username: '李四', email: 'lisi@example.com', age: 30 }
]
let nextId = 3

// ====================
// 路由
// ====================

// GET - 用户列表
app.get('/api/users', (req, res) => {
  const { keyword, page = 1, size = 10 } = req.query
  
  let filtered = users
  if (keyword) {
    filtered = users.filter(u => 
      u.username.includes(keyword) || u.email.includes(keyword)
    )
  }
  
  const start = (page - 1) * size
  const paginated = filtered.slice(start, start + Number(size))
  
  res.json({
    code: 200,
    data: {
      items: paginated,
      total: filtered.length,
      page: Number(page),
      size: Number(size)
    }
  })
})

// GET - 单个用户
app.get('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === Number(req.params.id))
  
  if (!user) {
    return res.status(404).json({ code: 404, message: '用户不存在' })
  }
  
  res.json({ code: 200, data: user })
})

// POST - 创建用户
app.post('/api/users', (req, res) => {
  const { username, email, age } = req.body
  
  // 验证
  if (!username || !email) {
    return res.status(422).json({ code: 422, message: '用户名和邮箱不能为空' })
  }
  
  // 检查重复
  if (users.some(u => u.username === username)) {
    return res.status(409).json({ code: 409, message: '用户名已存在' })
  }
  
  const user = { id: nextId++, username, email, age }
  users.push(user)
  
  res.status(201).json({ code: 201, message: '创建成功', data: user })
})

// PUT - 更新用户
app.put('/api/users/:id', (req, res) => {
  const index = users.findIndex(u => u.id === Number(req.params.id))
  
  if (index === -1) {
    return res.status(404).json({ code: 404, message: '用户不存在' })
  }
  
  users[index] = { ...users[index], ...req.body }
  res.json({ code: 200, message: '更新成功', data: users[index] })
})

// DELETE - 删除用户
app.delete('/api/users/:id', (req, res) => {
  const index = users.findIndex(u => u.id === Number(req.params.id))
  
  if (index === -1) {
    return res.status(404).json({ code: 404, message: '用户不存在' })
  }
  
  users.splice(index, 1)
  res.json({ code: 200, message: '删除成功' })
})

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).json({ code: 500, message: '服务器内部错误' })
})

// 启动服务器
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`)
})

// package.json
{
  "type": "module",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  }
}


五、中间件模式

// ====================
// 自定义中间件
// ====================

// 日志中间件
const logger = (req, res, next) => {
  const start = Date.now()
  res.on('finish', () => {
    const duration = Date.now() - start
    console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`)
  })
  next()
}

// 认证中间件
const auth = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1]
  
  if (!token) {
    return res.status(401).json({ code: 401, message: '未登录' })
  }
  
  try {
    const decoded = jwt.verify(token, SECRET_KEY)
    req.user = decoded
    next()
  } catch (err) {
    res.status(401).json({ code: 401, message: 'token无效' })
  }
}

// 验证中间件
const validateUser = (req, res, next) => {
  const { username, email } = req.body
  const errors = []
  
  if (!username || username.length < 3) {
    errors.push('用户名至少3个字符')
  }
  if (!email || !email.includes('@')) {
    errors.push('邮箱格式不正确')
  }
  
  if (errors.length > 0) {
    return res.status(422).json({ code: 422, message: '验证失败', errors })
  }
  
  next()
}

// 使用中间件
app.post('/api/users', validateUser, (req, res) => {
  // 处理请求
})

app.get('/api/profile', auth, (req, res) => {
  res.json({ user: req.user })
})


六、数据库操作(Prisma ORM)

# 安装Prisma
pnpm add prisma @prisma/client
npx prisma init

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  username  String   @unique @db.VarChar(50)
  email     String   @unique @db.VarChar(100)
  age       Int      @default(0)
  isActive  Boolean  @default(true)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(200)
  content   String   @db.Text
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
}

# 数据库迁移
npx prisma migrate dev --name init
npx prisma generate

// 使用Prisma
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

// 创建
const user = await prisma.user.create({
  data: {
    username: '张三',
    email: 'zhangsan@example.com',
    age: 25
  }
})

// 查询
const users = await prisma.user.findMany({
  where: { isActive: true },
  include: { posts: true },
  orderBy: { createdAt: 'desc' },
  skip: 0,
  take: 10
})

// 更新
const updated = await prisma.user.update({
  where: { id: 1 },
  data: { age: 26 }
})

// 删除
await prisma.user.delete({ where: { id: 1 } })


七、项目结构

my-api/
├── src/
│   ├── controllers/      # 控制器
│   │   └── userController.js
│   ├── middleware/        # 中间件
│   │   ├── auth.js
│   │   └── validate.js
│   ├── routes/           # 路由
│   │   ├── index.js
│   │   └── userRoutes.js
│   ├── services/         # 业务逻辑
│   │   └── userService.js
│   ├── utils/            # 工具函数
│   │   └── response.js
│   └── app.js            # Express应用
├── prisma/
│   └── schema.prisma     # 数据库模型
├── .env                  # 环境变量
├── package.json
└── README.md


八、常用npm包

包名用途
expressWeb框架
cors跨域处理
helmet安全头
morganHTTP日志
jsonwebtokenJWT认证
bcryptjs密码加密
multer文件上传
dotenv环境变量
prismaORM
zod数据验证
winston应用日志
bull消息队列
ioredisRedis客户端

学习建议

  1. 先掌握Express基础,理解路由和中间件
  2. 学习Prisma或TypeORM,数据库操作是核心
  3. 理解异步编程,async/await是必须掌握的
  4. 学会项目分层,controller/service/repository
  5. 做一个完整项目,如博客API或电商API

下一步学习

返回首页