# 用 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 负责缓存用户信息。

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

相关推荐
王八八。12 小时前
nvm 安装与配置完全指南:从零到多版本 Node.js 自由切换
node.js
紫禁玄科13 小时前
Shai-Hulud:npm生态的自我复制蠕虫风暴
前端·npm·node.js
烂蜻蜓14 小时前
Node.js入门教程(十四):async/await
node.js
谢小飞3 天前
大文件上传很难?学会这招,GB文件秒传不是梦
前端·node.js
周小董3 天前
[1367]npm 安装 canvas 报错 node-gyp ERR
前端·npm·node.js
张龙6874 天前
Node.js 内存泄漏排查实战:从内存曲线到堆快照,一条可复用的定位路径
性能优化·node.js
烂蜻蜓4 天前
Node.js入门教程(七):工作机制
node.js
凌云拓界5 天前
NodeVerdict | .ndv 二进制格式:为 WASM 解码器设计的紧凑布局
安全·架构·开源·node.js·编辑器·软件工程·wasm
凌云拓界5 天前
NodeVerdict | 性能门禁:把追踪数据变成 CI 规则
ci/cd·信息可视化·开源·node.js·bug·数据可视化·安全架构
烂蜻蜓5 天前
Node.js入门教程(五):NVM 管理多版本 Node.js
node.js