# 用 Node.js 连接数据库:一次请求是怎么跑起来的?

用 Node.js 连接数据库:一次请求是怎么跑起来的?

这篇主要是把一个简单后端的流程串起来:Node.js 接收请求,PostgreSQL 存数据,Redis 存缓存,最后把结果返回给前端。

先引入要用的东西

js 复制代码
//Web服务器框架  处理http请求,定义路由
const express = require('express')
//PostgreSQL驱动  连接数据库,执行SQL
const { Client } = require('pg')
//环境变量加载器  把.env文件里的密钥读进代码
const dotenv = require('dotenv')
//密码哈希工具  注册是加密密码 登录时比对密码
const bcrypt = require('bcrypt')
//Redis驱动  连接 Redis 缓存、读写缓存数据
const redis = require('redis')

关于 Client,这里用的是解构赋值,意思是只需要 pg 包中的 Client 这一个功能。

引入之后就是初始化配置。

js 复制代码
dotenv.config()

它会读取项目根目录的 .env 文件,把里面的键值对放进 process.env 对象中。

text 复制代码
.env文件
DATABASE_URL='xxxxxxxxxxxxxxxxxxxxxx'

REDIS_URL='xxxxxxxxxxxxxxxxxxxxxxxxx'

接着创建 PostgreSQL 和 Redis 客户端实例。这里只是创建,还没有真正连接。

js 复制代码
const client = new Client(process.env.DATABASE_URL)

const redisClient = redis.createClient({
	url:process.env.REDIS_URL
})

最后创建 Express 应用实例,后面的 app.getapp.post 等都会挂在它身上。

js 复制代码
const app = express()
js 复制代码
app.use(express.json())

这是注册一个中间件。如果前端发来的请求体是 JSON 格式,它会帮我们解析成 JS 对象,放进 req.body

路由:别人访问什么,就做什么

js 复制代码
app.get('/',(req.res)=>{
	res.send('hello world')
})
  • app:Express 实例。
  • get:请求方法。
  • /:请求路径。
  • req:请求对象,也就是客户端传过来的值。
  • res:响应对象,也就是返回给客户端的内容。

简单来说,有人用 GET 方法访问根路径时,就回复 hello world。

注册接口则是把用户信息写进数据库。

js 复制代码
app.post('/register', async (req, res) => {
    try {
        await client.query(`insert into users (name,password,email) values ($1,$2,$3)`, [req.body.name, await bcrypt.hash(req.body.password, 10), req.body.email])
        res.json({ message: '注册成功' })
    } catch (err) {
        console.log(err)
        res.status(400).json({ message: '注册失败' })
    }

})
  • async / await:因为密码哈希和数据库插入都是异步操作,所以回调函数要使用 async。
  • try / catch:保护可能出错的代码。例如邮箱重复,违反 UNIQUE 约束时,插入就会失败。
  • client.query :执行 SQL。值不直接写进 SQL,而是用 1、1、 1、2、$3 传入,这样可以防止 SQL 注入,因为这样数据库会把传进来的值当纯文本处理。
  • bcrypt.hash:把密码变成哈希值,10 可以简单理解成哈希时的计算强度。
  • res.json:给客户端返回 JSON 内容。
  • res.status(400):给客户端返回状态码。

登录时,不是把密码再 hash 一次后直接比较,而是用 bcrypt.compare 比对"用户输入的密码"和"数据库里存的哈希值"。

js 复制代码
app.post('/login', async (req, res) => {
    try {
        const data = await client.query(`select * from users where email = $1`, [req.body.email])
        if(data.rows.length === 0){
            res.status(400).json({message:'用户不存在'})
            return
        }
        if (await bcrypt.compare(req.body.password, data.rows[0].password)) {
            res.json({ message: '登录成功',data:data.rows[0].id })
        }else{
            res.status(400).json({message:'密码错误'})
            return
        }
    } catch (err) {
        console.log(err)
        res.status(400).json({ message: '登录失败' })
    }
})

GET、POST 和 Redis 缓存

GET 请求和 POST 请求里,req 后面跟的内容通常不一样:

  • app.get :常用 req.query,数据在 URL 参数上。
  • app.post :常用 req.body,数据在 JSON 里。

什么是 URL 参数?

text 复制代码
https://xxx.com/details?teamid=0  ?后面的teamid=0 就是参数

下面这个接口是查用户资料。它会先查 Redis,有缓存就直接返回;没有缓存才去查 PostgreSQL,然后把结果写回 Redis。

js 复制代码
app.get('/profile',async (req,res) =>{
    const id = req.query.id
    const cached = await redisClient.get(id)
    try{
        if(cached){
            res.json({message:"用户信息已缓存",data:JSON.parse(cached)})
         }else{
            const userId = await client.query('select id,name,email,created_at from users where id = $1',[id])
            await redisClient.set(id,JSON.stringify(userId.rows[0]))
            res.json({message:"用户信息已缓存成功",data:userId.rows[0]})
         }
    }catch(error){
        res.status(400).json({message:error.message})
    }
})
  • redisClient.get:读取缓存。没有就是 null,有就返回对应的值。
  • redisClient.set:写入缓存,前者是值的名称,后者是具体的值。
  • JSON.stringify:JS 对象变成 JSON 字符串。
  • JSON.parse:JSON 字符串变回 JS 对象。

Redis 只能存字符串,所以对象写进去之前,要先用 JSON.stringify 转成字符串。

这里用户的 id 写在 URL 里,别人修改 id 后就可能查看别人的账号。更关键的还是要判断:当前用户有没有权限查看这份资料。

最后启动服务器

js 复制代码
app.listen(3000, async () => {
    await client.connect()
    await redisClient.connect()
})
  • client.connect:连接 PostgreSQL。
  • redisClient.connect:连接 Redis。

把连接写在这里,想表达的就是:服务启动时,也把 PostgreSQL 和 Redis 连上。这样整个流程就串起来了。

最后收一下

如果只用一句话理解这段代码,那就是:Express 负责接请求,PostgreSQL 负责存用户数据,bcrypt 负责处理密码,Redis 负责缓存用户信息。

整个流程就是:先引入工具和配置,再定义路由,路由里查数据库或缓存,最后启动服务器。

相关推荐
kisshyshy5 小时前
给端侧大模型装上“发动机”:React 合成事件 + 进度条组件全解
前端·react.js·node.js
csdn2015_9 小时前
nodejs安装
node.js·vue
前端双越老师1 天前
如何以前端视角(非0基础)学 Java ?
java·node.js·全栈
Kel1 天前
Node.js 没那么复杂
人工智能·node.js·全栈
星栈独行1 天前
Node 接口该写同步还是异步?
服务器·开发语言·后端·程序人生·node.js
星栈2 天前
Node 接口该写同步还是异步?
后端·node.js
半个落月2 天前
用 LangChain 连接远程 MCP:从工具发现到多轮调用闭环
人工智能·后端·node.js
星栈独行2 天前
Node 框架怎么选?Express、Koa、Egg、NestJS 场景化选型指南
后端·程序人生·node.js
徐小超2 天前
从一句 Hello World 到完整 RAG 系统:一个 AI 知识库的架构选型实录
langchain·node.js·ai编程