Akara's Wonderland

Node 实现 MySQL/pgSQL CRUD

2022-03-08 · 5 min read
MySQL pgSQL CRUD

作为 CRUD 工程师,这种基本功自然不会忘记,但是用到的时候直接复制岂不美哉?

pgSQL

pgSQL 这边提供了两个对象来实现数据库连接,分别是 PoolClient,这里我们只谈论 Pool,因为它更符合我的业务逻辑

安装依赖

yarn add pg
yarn add @types/pg # if use TypeScript

创建连接

首先,你需要准备好连接信息,需要地址、端口、用户、密码、数据库,相信这个不用我多BB。
我们再来实现一个最简实现:

import { Pool } from "pg";

const pool = new Pool();

所以,连接信息呢?然而这段代码是完全合法的,因为 pg 库会先找你在 new Pool() 中传的参数,如果找不到就找环境变量,还找不到就用默认值,各个值的对应关系如下表:

项目 环境变量 默认值
地址 PGHOST localhost
用户 PGUSER USER(环境变量)
密码 PGPASSWORD (空)
数据库 PGUSER USER(环境变量)
端口 PGPORT 5432

如果你正在调试一个开源项目,又不想每次调试前都要写一堆 PGHOST=......PGPORT=5432 ,更不想把这么一堆的写进电脑的环境变量里(当然,这也并不安全),也是有法子的,这个叫 dotenv 的包能解决这个问题

yarn add dotenv
import 'dotenv/config'

然后就能在运行 js 脚本的时候自动导入项目目录下的 .env 文件,并设置环境变量。

当然,这一切让你觉得麻烦的话,也可以直接把连接信息写死在 new Pool()

import { Pool } from 'pg'

const pool = new Pool({
  user: 'dbuser',
  host: 'database.server.com',
  database: 'mydb',
  password: 'secretpassword',
  port: 3211,
})

直接查询与 Prepare Statement

直接查询还是非常简单的

pool.query('select now()',(err,result) => {
    if (err) throw err.stack
    console.log(result.rows[0])
})

(err,result)这个回调可以不写,然后就会输出当前的时间,这是直接查询,我们接着看 Prepare Statement。
Prepare Statement,听着高端,其实干的就一件事,检查一下输入有没有不合法内容,然后拼接字符串,仅此而已,但是这确实是刚需,因为你不能相信用户的输入,用户可能会在输入里写各种离谱玩意,直接给你删库了就好玩了,所以还是得老老实实地用这个。

const query = {
    name: "fetch-user",
    text: `INSERT INTO zhutian.web_content (name, link, category, cover) 
    VALUES ($1::varchar, $2::varchar, $3::varchar, $4::varchar)`,
    values: [post.title, post.url, post.category, post.image],
};
pool.query(query);

语法上,直接在 query 对象的 text 值里头直接插入形如$1,$2,$3 这样的东西,然后我们在 values 那里写一个列表,列表对应的值跟$1这些一一对应。
同样的,我们可以在 query 后面加上回调,但是这里没必要。

结束连接

pg 库会自己断开 Pool 的连接,如果实在有刚需,那就 pool.end()

PostgreSQL 使用自签 SSL 证书导致连接失败

heroku 提供的免费 Postgres 数据库限制一定要加密访问,但是证书偏偏是自签的,Node 那边自检过不去,虽然 pg 库提供了验证自签证书的参数,但是我没学过相关知识,所以就禁用咯。

// 直接写在 .env 里倒是也行,但是有时候 ci 工具会因此抽风,比如 Vercel,所以还是写文件里好
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" 
const pool = new Pool({ ssl:true })

MySQL

连接

非常简单,一点坑都没

var mysql = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : '123456',
  database : 'test'
});
 
connection.connect();
 
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});

connection.end()

用作 Serverless 用途

MySQL 没坑,但是 Serverless 有坑,如果像之前那么写的话,Serverless 函数就会出问题,第一次卡住不输出,第二次函数崩溃,当时的 log 实在找不到了,就简单在这记录下咋解决的吧。

首先,每次 Serverless 请求都要独占一个 connection,而且用完立刻销毁,这样 Serverless 函数才正常,简单上个代码。

var mysql = require('mysql');

// 写个函数,执行一次返回一个新连接
function getConnection() {
    var connection = mysql.createConnection({
        host     : 'localhost',
        user     : 'root',
        password : '123456',
        database : 'test'
    })
    return connection
}

// 示例函数
export default async function () {
    let connection = getConnection()
    // Some Logic
    connection.end()
    return;
}