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 create-next-app@13.4.12
...
注:或直接使用如下命令创建 next 项目
$ npx create-next-app@13.4.12
使用 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,raw 参数为:
{"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"}