10分钟掌握 Koa + Prisma 实现数据库 CRUD
前言
Prisma 是号称下一代的 ORM 工具,同样是基于 TypeScript 实现,但是带来了很强的类型安全。
本文使用 Koa.js 搭建一个简单的 Web 服务,配合 MySQL 数据库,来演示如何通过 Prisma 实现数据的增删改查。
Prisma
Prisma 是号称下一代的 ORM 工具,下面是 Prisma 中文网站,可以说是技术类网站中相当优秀的中文站了:
Prisma 是一个开源的数据库工具链项目,不仅仅是一个简单的 ORM 工具。它支持的数据库有 PostgreSQL、MySQL、MongoDB、SQL Server和SQLite,本文以 MySQL 进行演示。
在使用方式上,对于第一次接触它的人来讲,稍微有些繁琐。我总结了以下几个流程:
- 安装依赖
- 初始化 Prisma 项目
- 设计 Prisma Schema(数据库信息和模型)
- 同步到数据库
- 生成 Prisma Client
- 使用 Prisma Client 完成 CRUD
下面我们先去创建一个开发环境,再回过头来继续介绍 Prisma。
初始化环境
使用 Koa 快速搭建一个 Web 服务,首先创建目录,并安装依赖:
bash
$ mkdir koa-prisma
$ cd koa-prisma
$ pnpm init
# 安装 Koa 依赖
$ pnpm add koa @koa/router koa-bodyparser
- @koa/router:路由中间件,方便集成路由功能
- koa-bodyparser:解析请求体数据,并放到 ctx.request.body 对象上
然后新建一个 index.js
,完成一个简单的 Web 服务:
javascript
const Koa = require('koa')
const Router = require('@koa/router')
const bodyParser = require('koa-bodyparser')
const app = new Koa()
// 实例化路由器,并设置公共路由前缀 /users
const router = new Router({
prefix: '/users'
})
app.use(bodyParser())
// 查询用户列表
router.get('/', async ctx => {
})
// 查询单个用户
router.get('/:id', async ctx => {
})
// 创建用户
router.post('/', async ctx => {
})
// 更新用户
router.patch('/:id', async ctx => {
})
// 删除用户
router.delete('/:id', async ctx => {
})
// 注册路由中间件
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log('服务器运行在 3000 端口')
})
在上面代码中,定义了5个路由方法,分别对应了数据库的查询、插入、更新和删除操作。稍后完成 Prisma 的初始化,将 Prisma Client 引入,就能实现这几个接口了。
然后使用 nodemon
命令来启动服务,最好是在全局安装此模块,方便使用:
bash
$ nodemon src/index.js
Prisma CLI
首先安装下 Prisma 的两个依赖模块:
bash
$ pnpm add -D prisma
$ pnpm add @prisma/client
安装的第一个 prisma,它是一个 CLI 命令,主要通过它来调用 Prisma 的各种功能,包括数据库迁移,创建Prisma Client 等等。
执行下面的命令,看一下 prisma 的使用说明:
bash
$ npx prisma --help
可以看到,prisma 提供了 7 个命令:
命令 | 说明 |
---|---|
init | 在应用中初始化 Prisma |
generate | 主要用来生成 Prisma Client |
db | 管理数据库的模式和生命周期 |
migrate | 迁移数据库 |
studio | 启动一个Web 端的工作台来管理数据 |
validate | 检查 Prisma 的模式文件的语法是否正确 |
format | 格式化Prisma的模式文件,默认就是 prisma/schema.prisma |
并且还给出了示例,我们这里主要使用其中的三个,剩下的大家可以自己去使用看看效果:
初始化 Prisma
执行下面的命令,完成初始化:
bash
$ npx prisma init
这个命令的效果是在命令所在目录,也就是现在的根目录中,创建一个 .env
文件,一个 prisma
目录,并在此目录下创建 schema.prisma
文件,如下:
对于 .env
文件,大家都不会陌生,就是一个放置环境变量的文件,这里面的环境变量通常是一些配置信息。
prisma
目录,用来存放和 Prisma 相关的文件,目前只有一个 schema.prisma
文件,这个文件就是前面提及过的 Prisma 模式文件,我们会在此文件中定义数据库的连接信息和模型。
设计 Prisma Schema
schema.prisma
是使用 Primsa 的主要配置文件,称之为 Prisma schema 文件,它包含三个基本结构:
-
数据源
-
生成器
-
数据模型定义
安装 VSC 插件
在编辑模式文件前,在 VS Code 中安装 Prisma
插件,它针对 .prisma
文件提供了代码高亮、格式化、自动补全、跳转定义和检查的功能。没有这个插件的加持,模式文件就是一个纯文本。
设置生成器
使用 generate
定义生成器,通过 provider 属性声明为 prisma-client-js
(目前也只支持这一种)。当执行 prisma generate
命令时就会生成 Prisma Client,使用它完成数据的增删改查。
typescript
generator client {
provider = "prisma-client-js"
}
设置数据源
使用 datasource
是定义数据源,用来设置 Prisma 连接的数据库所需要的一些信息。provider
是连接到的数据库的类型,默认是 postgresql,我们改为要用到的 mysql。 url
是数据库URL,通常为了保持配置分离,会将其单独定义到一个环境变量文件中,也就是 prisma cli 自动生成的 .env
文件。通过 env()
函数,会去读取此文件中的变量。
typescript
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
看一下 .env
文件,默认连接的是 Postgresql:
bash
DATABASE_URL=postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public
这是一个 数据库连接 URL的组成:
下面都是必填项:
名称 | 占位符 | 描述 |
---|---|---|
Host | HOST |
数据库 IP 或域名, 例如 localhost |
Port | PORT |
数据库端口, 例如 3306 |
User | USER |
数据库用户名, 例如 root |
Password | PASSWORD |
数据库密码 |
Database | DATABASE |
数据库名称,例如 mydb |
根据这个说明,定义我们自己的 MySQL URL:
bash
DATABASE_URL="mysql://root:root123@localhost:3306/prisma"
定义 User 模型
作为 ORM 工具,肯定少不了模型了。Prisma 的模型主要有以下作用:
- 构成了应用领域的 实体
- 映射到数据库的 表 (关系型数据库,例如 PostgreSQL)或 集合 (MongoDB)
- 构成 Prisma Client API 中 查询 的基础
- 在使用 TypeScript 时,Prisma Client 为模型及其 变体 提供 类型定义,保证数据库访问的类型安全
定义模型时会用到形如 @id()
、@default()
这些 Prisma 内置的工具函数。比如 @id()
用来声明主键,@default()
用来设置默认值,命名都非常语义化,基本就是 SQL 中的一些关键字,非常容易理解。
下面是一个 User 模型的定义:
typescript
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
createdTime DateTime @default(now()) @map("created_time")
updatedTime DateTime @updatedAt @map("updated_time")
@@map("user")
}
需要说明的几点信息:
1.模型的名字默认就是创建的数据表的名字,这里是大写的 User,那么数据表名也就是大写的 User,可以使用 @@map()
来设置映射的表名,改为小写的 user。
2.每个模型的记录都是唯一可识别的,也就是要有主键,可以使用 @id 去声明。
3.字段的类型,比如 Int,String,会经过Prisma 转为数据库对应的类型,也就是 int 和 varchar。
4.@unique
是唯一值约束,所以生成的 user 表的 email 字段的值不能重复。
5.像是创建时间和更新时间,为了符合 JS、TS 中的命名规范,使用了小驼峰命名,为了符合数据库命名规范,在后面使用 @map()
重新映射为了下划线命名。
同步数据库
将Prisma 模型同步到数据库,对于我们这样一个新项目,空项目,可以使用下面的命令:
bash
$ npx prisma db push
如果是一个已经有数据的项目,就不能使用这个命令了,转而使用 prisma migrate
迁移。本文先不涉及。
它会创建数据库的schema,并且使用 Prisma schema 同步数据库,还会"偷偷"执行下 prisma generate
命令来生成 Prisma Client。
来到数据库,刷新一下,已经创建好了 prisma
数据库和一张 user
表:
生成 Prisma client
前面同步数据库时,已经执行了 prisma generate
。所以现在不需要再次执行了,但是一旦Prisma Schema 文件发生了变动,比如修改了模型,就需要再来执行下这个命令,重新生成 Prisma Client。
generate
命令执行的流程是:
CRUD 增删改查
初始化 Prisma Client
有了 Prisma Client,可以使用它来执行 CRUD 操作。初始化:
js
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
Prisma Client 的实例 prisma ,具备丰富的类型,使用方式是 prisma.模型.CRUD方法
:
我们先来介绍几个常用的 API:
- findMany:查询多条记录
- findUnique:查询单条记录
- create:创建记录
- update:更新记录
- delete:删除记录
使用这几个 API 在路由方法中操作数据库,完成接口的开发。
查询用户列表
findMany 不传入参数,表示查询整张 user 表的所有记录,它返回的是一个数组,是 User 模型的实例集合:
js
router.get('/', async ctx => {
const users = await prisma.user.findMany()
ctx.body = users
})
查询单个用户
在 findUnique
方法中通过 where
设置查询条件,也就是根据指定的 ID 来查询一条用户记录,该方法只返回一条记录,也就是 User
模型的一个实例:
js
// 查询单个用户
router.get('/:id', async ctx => {
const id = parseInt(ctx.params.id)
const user = await prisma.user.findUnique({
where: { id }
})
ctx.body = user
})
需要注意的一点,从 ctx.params.id
获取的 id
是字符串类型,要转为整数类型再去查询。
创建用户
使用 create
可以插入数据,将从请求体中解析的数据,也就是描述 User
模型 的一个对象赋值给 data 属性即可:
js
router.post('/', async ctx => {
const user = ctx.request.body
const newUser = await prisma.user.create({
data: user
})
ctx.body = newUser
})
更新用户
使用 update
方法,通过 where
设置查询条件,查询到一个用户,再将待更新的数据赋值给 data,
完成更新:
js
router.patch('/:id', async ctx => {
const id = parseInt(ctx.params.id)
const updateUser = ctx.request.body
const user = await prisma.user.update({
where: {
id
},
data: updateUser
})
ctx.body = user
})
删除用户
使用 delete
方法,直接通过 where 设置删除记录的查询条件即可:
js
router.delete('/:id', async ctx => {
const id = parseInt(ctx.params.id)
const user = await prisma.user.delete({
where: {
id
}
})
ctx.body = user
})
注意事项:使用 update 和 delete 时,一定要设置where,否则会更新或者删除数据表的所有记录,非常危险。
测试
创建用户:
查询用户列表:
更新用户:
删除用户:
总结
完整代码在这里。
本文通过一个用户的增删改查的例子,在 Koa 中演示了 Prisma 的基本使用,在此总结一下:
- 安装依赖
- 初始化 Prisma
- 设置 Prisma Schema
- 同步数据库
- 创建Prisma Client
- 使用 Prisma Client 实现 CRUD
Prisma 还有很多优秀的特性,本文抛砖引玉,只介绍了最基本的使用,一个单表的增删改查。推荐大家去阅读中文文档站点了解更多关于 Prisma 的内容。
感谢阅读!