Next.js 是一个用于构建 Web 应用程序的框架。Next.js 是一个用于生产环境的 React 框架,是一个 React 服务端渲染应用框架。
NextJS: https://nextjs.org/
Prisma 是一个基于 promise 的 Node.js 和 TypeScript 的 ORM,目前支持
Mysql,MariaDB,SQLite,PostgreSQL,AWS Aurora Serverless 和 Aws Aurora ,暂不支持
Microsft SQL Server 。Prisma 通过提供 类型安全、丰富的自动补全、平滑的 API 等特性。
Prisma: https://www.prisma.io
Prisma CN: https://prisma.yoga/
Prisma NextJS: https://www.prisma.io/nextjs
Sequelize 是一个基于 promise 的 Node.js 的 ORM,目前支持 Mysql,Postgres,MariaDB,SQLite 以及 Microsft SQL Server。它具有强大的事务支持,关联关系,预读和延迟加载,读取复制等功能。
Sequelize: https://sequelize.org/
Sequelize CN: https://www.sequelize.cn/
Prisma 和 Sequelize 各自支持的功能比较表如下:
Prisma | Sequelize | |
原始查询 | yes | yes |
事务 | yes | yes |
自动生成 Schema | yes | yes |
迁移 | yes | yes |
TypeScript | yes | yes |
子查询 | yes | no |
读写分离 | no | yes |
乐观锁 | yes | no |
高级函数 | yes | no |
本文选择使用 Node.js + Next.js + Prisma + MySQL 搭建 JSON API 服务。
1. 系统环境
NodeJS: 16.20.1NPM: 8.19.4
NextJS: 13.4.12
Prisma:5.5.2
2. 创建 NextJS 项目
安装 create-next-app 脚手架,命令如下:
# 使用 -g 参数,表示该命令只需在本机上运行一次
$ npm install -g [email protected]
...
注:或直接使用如下命令创建 next 项目
$ npx [email protected]
使用 create-next-app 命令创建 NextJS 项目,命令如下:
$ create-next-app furniture-service √ What is your project named? ... furniture-service √ Would you like to use TypeScript? ... No / Yes √ Would you like to use ESLint? ... No / Yes √ Would you like to use Tailwind CSS? ... No / Yes √ Would you like to use `src/` directory? ... No / Yes √ Would you like to use App Router? (recommended) ... No / Yes √ Would you like to customize the default import alias? ... No / Yes Creating a new Next.js app in ..\furniture-service.
注:这里选择 App Router
进入 furniture-service 项目目录安装依赖,命令如下:
$ npm install
...
运行 furniture-service 项目,命令如下:
$ npm run start # npm run dev
...
浏览器访问 http://localhost:3000,显示内容如下:
Get started by editing src/app/page.js
3. API 路由
路由处理程序 (Route Handlers) 允许用户使用 Web 请求和响应 API 为给定路由创建自定义请求处理程序。它定义在 app 目录及其子目录下的 route.js 或 route.ts 文件中,比如:
app/api/route.js
路由处理程序类似于 page.js 和 layout.js,但在同一目录下 page.js 和 router.js 不能同时存在。
支持以下 HTTP 方法:GET、POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS。如果调用了不支持的方法,Next.js 将返回一个 405 method Not Allowed 响应。
示例,创建 app/api/route.js 文件,内容如下:
import { NextResponse } from 'next/server'; export async function GET(request) { //console.log(request.nextUrl.searchParams); return NextResponse.json({ ret: 'GET Success' }, { status: 200 }); } export async function POST(request) { //console.log(request); return NextResponse.json({ ret: 'POST Success' }, { status: 200 }); }
运行 furniture-service 项目,浏览器访问 http://localhost:3000/api,显示内容如下:
{"ret":"GET Success"}
Postman 用 POST 方法访问 http://localhost:3000/api,显示内容如下:
{
"ret": "POST Success"
}
4. 安装 MySQL 支持
手动创建 MySQL 数据库 testdb 和 user 表,SQL 脚本如下:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, `createtime` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # 创建一条用户记录 INSERT INTO user (username, password, age, createtime) VALUES ('admin', '123456', 18, Now());
进入 furniture-service 项目目录,安装 MySQL 支持,命令如下:
$ npm install mysql mysql2 --save
...
修改 app/api/route.js 文件,内容如下:
import { NextResponse } from 'next/server'; import mysql from 'mysql'; const getData = () => { return new Promise((resolve, reject) => { var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '123456', port: '3306', database: 'testdb' }); connection.connect(); connection.query('SELECT * FROM user', function (err, result) { if (err) { //console.log("getData() -> reject: " + err.message); reject({ ret: 'error', msg: err.message }) } //console.log("getData() -> resolve: " + result); resolve({ ret: 'success', data: result}); }); connection.end(); }) } export async function GET(request) { let data = await getData(); //console.log(data); return NextResponse.json( data, { status: 200 }); } export async function POST(request) { console.log(request); return NextResponse.json({ ret: 'POST Success' }, { status: 200 }); }
运行 furniture-service 项目,浏览器访问 http://localhost:3000/api,显示内容如下:
{"ret":"success","data":[{"id":1,"username":"admin","password":"123456","age":18,"createtime":"2023-11-04T07:24:59.000Z"}]}
5. 安装 ORM 支持
1) 安装 Prisma
在 furniture-service 项目目录下安装 Prisma,命令如下:
$ npm install prisma --save
...
注:可以运行 npx prisma 来查看 prisma 的命令使用方法。
创建 Prisma 架构文件模板来设置 Prisma 项目,命令如下:
$ npx prisma init
✔ Your Prisma schema was created at prisma/schema.prisma warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information. Next steps: 1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started 2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb. 3. Run prisma db pull to turn your database schema into a Prisma schema. 4. Run prisma generate to generate the Prisma Client. You can then start querying your database. More information in our documentation: https://pris.ly/d/getting-started
以上命令会在 furniture-service 项目的根目录下创建一个 .env 文件和一个 prisma 目录,.env 文件用于定义环境变量(例如数据库连接),prisma 目录下生成了一个 schema.prisma 文件,schema.prisma 文件包含带有数据库连接变量和模式模型的 prisma 模式。
2) 配置 prisma
修改 .env 文件,内容如下:
DATABASE_URL="mysql://root:123456@localhost:3306/testdb?schema=public"
修改 prisma/schema.prisma
// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") }
3) 反向生成 prisma 数据模型
上文的示例中,我们手动创建了 MySQL 数据库 testdb 和 user 表,这里在 .env 文件里配置了连接到 testdb 数据库,可以反向生成 user 表的数据模型,保存到 prisma\schema.prisma 文件,命令如下:
$ npx prisma db pull
Prisma schema loaded from prisma\schema.prisma Environment variables loaded from .env Datasource "db": MySQL database "testdb" at "localhost:3306" ✔ Introspected 1 model and wrote it into prisma\schema.prisma in 79ms Run prisma generate to generate Prisma Client.
查看 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 @db.VarChar(50) password String? @db.VarChar(255) age Int? createtime DateTime? @db.Timestamp(0) }
可以看到 prisma\schema.prisma 文件里生成了 user 表的数据模型。
4) 生成数据库基线 (Baseline)
基线 (Baseline)是指初始化一个数据库的迁移记录。数据库的表结构可能因为业务需要而多次修改,可以使用 Baseline 技术,记录下表结构多次修改之间的差别。
创建一个迁移目录 prisma/migrations/0_init,我们将使用 0_init 作为首次迁移的名称,命令如下:
$ mkdir -p prisma/migrations/0_init
...
注:-p 将在自动创建路径中不存在的中间路径。
使用 prisma migrate diff 命令生成迁移文件,格式如下:
$ npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
...
参数说明:
--from-empty:假设从中迁移的数据模型为空
--to-schema-datamodel:使用数据源块中 URL 的当前数据库状态
--script:输出 SQL 脚本
6. 读写数据库
在读写数据库的一个表之前,需要确保已经创建了这个表的 prisma 数据模型,并且要安装好 Prisma Client,Prisma Client 提供常用的数据库 CRUD 方法: create、update、delete、findUnique、findMany 等。
安装 Prisma Client,命令如下:
$ npm install @prisma/client
...
读取 Prisma 架构并生成 Prisma Client 库,命令如下:
$ npx prisma generate
Environment variables loaded from .env Prisma schema loaded from prisma\schema.prisma ✔ Generated Prisma Client (v5.5.2) to .\node_modules\@prisma\client in 55ms Start using Prisma Client in Node.js (See: https://pris.ly/d/client) ``` import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() ``` or start using Prisma Client at the edge (See: https://pris.ly/d/accelerate) ``` import { PrismaClient } from '@prisma/client/edge' const prisma = new PrismaClient() ``` See other ways of importing Prisma Client: http://pris.ly/d/importing-client
示例,创建 app/api2/route.js 文件,内容如下:
import { NextResponse } from 'next/server'; import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient(); export async function GET(request) { // By unique identifier let user = await prisma.user.findUnique({ where: { id: 2, }, }) //console.log(user); return NextResponse.json(user, { status: 200 }); } export async function POST(request) { let user = await prisma.user.create({ data: { username: request.get('username'), password: request.get('password'), age: request.get('age'), createtime: time() }, }) //console.log(user); return NextResponse.json(user, { status: 200 }); }
运行 furniture-service 项目,在 postman 上用 POST 方法访问 http://localhost:3000/api2,HTTP 请求的 Body 类型为 raw(JSON 格式),内容如下:
{"username":"user","password":"abcdef","age": 99}
返回结果为:
{ "id": 2, "username": "user", "password": "abcdef", "age": 99, "createtime": "2023-11-04T12:27:47.000Z" }
浏览器访问 http://localhost:3000/api2, 显示结果如下:
{"id":2,"username":"user","password":"abcdef","age":99,"createtime":"2023-11-04T12:27:47.000Z"}