NodeJS系列(13)- Next.js 框架 (六) | Node.js + Next.js + Prisma/Sequelize (ORM) + MySQL 搭建 JSON API 服务

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.1
    NPM: 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 Client

        Prisma Client 提供常用的数据库 CRUD 方法: create、update、delete、findUnique、findMany 等。
        
        安装 Prisma Client,命令如下:

            $ npm install @prisma/client

                ...

 

6. 使用 Prisma Migrate

    Prisma Migrate 是一个声明性数据库模型迁移工具,使您能够:

        (1) 随着 Prisma 架构的发展,保持数据库架构与 Prisma 数据模型同步
        (2) 维护数据库中的现有数据

    Prisma Migrate 生成 .sql 迁移文件的历史记录,并在开发和部署中发挥作用。

    上文我们是用手动方式在 testdb 数据库创建了 user 表,而且 app/api/route.js 文件里的 GET/POST 函数中没有使用 ORM (Prisma/Sequelize) 数据模型来操作数据库的表。

    在我们熟悉 Prisma 数据模型的情况下,我们可以直接在 prisma\schema.prisma 文件里添加表的数据模型,再运行 npx prisma migrate dev --name init 命令把表迁移到数据库。

    1) 新增 post 表
    
        修改 prisma\schema.prisma 文件,添加 post 表的数据模型,内容如下:

            generator client {
                provider = "prisma-client-js"
            }

            datasource db {
                provider = "mysql"
                url      = env("DATABASE_URL")
            }

            model post {
                id        Int      @id @default(autoincrement())
                title     String   @db.VarChar(255)
                createdAt DateTime @default(now())
                updatedAt DateTime @updatedAt
            }


        执行 prisma migrate 命令,创建第 1 个数据库迁移,格式如下:

            $ npx prisma migrate dev --name init    // dev 表示开发环境

             ...

                [+] Added tables
                - user

                ? We need to reset the MySQL database "testdb" at "localhost:3306"
                Do you want to continue? All data will be lost. » (y/N) y

                Applying migration `20231106104925_init`

                The following migration(s) have been created and applied from new schema changes:

                migrations/
                └─ 20231106104925_init/
                    └─ migration.sql

                Your database is now in sync with your schema.

                ✔ Generated Prisma Client (v5.5.2) to .\node_modules\@prisma\client in 56ms

            注:testdb 数据库里的 user 表是手动创建的,该表不在 prisma migrate 的版本管理范围之内,会提示是否重置数据库,这里选择了 y 重置数据库。

                    prisma migrate 命令会自动执行 prisma generate 命令读取 Prisma 架构并生成 Prisma Client 库。

        查看 testdb 数据库,可以看到新增了两个表 post 和 _prisma_migrations(记录 migration 历史)。查看项目目录,新增了一个 prisma/migrations/20231106104925_init/migration.sql 文件。

        可以运行 npx prisma migrate status 命令查看 migration 状态。

    2) 修改 post 表

        修改 prisma\schema.prisma 文件,修改 post 表的数据模型,内容如下:

            generator client {
                provider = "prisma-client-js"
            }

            datasource db {
                provider = "mysql"
                url      = env("DATABASE_URL")
            }

            model post {
                id        Int      @id @default(autoincrement())
                title     String   @db.VarChar(255)
                content   String?
                createdAt DateTime @default(now())
                updatedAt DateTime @updatedAt
            }


        执行 prisma migrate 命令,创建第 2 个数据库迁移,格式如下:

            $ npx prisma migrate dev --name post_add_content

                ...

        查看 testdb 数据库,可以看到 post 表结构已更新,_prisma_migrations 里新增了一条记录。查看项目目录,新增了一个 prisma/migrations/20231106130648_post_add_content/migration.sql 文件。

    3) 反向生成 Prisma 数据模型

        上文运行 npx prisma migrate dev --name init 命令过程中,我们选择了 y 重置了 testdb。

        如果我们需要保留 user 表结构,可以在运行 prisma migrate 命令之前,反向生成 prisma 数据模型,把 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 post {
                id        Int      @id @default(autoincrement())
                title     String   @db.VarChar(255)
                content   String?
                createdAt DateTime @default(now())
                updatedAt DateTime @updatedAt
            }

            model user {
                id         Int       @id @default(autoincrement())
                username   String    @unique(map: "username") @db.VarChar(50)
                password   String?   @db.VarChar(255)
                age        Int?
                createtime DateTime? @db.Timestamp(0)
            }

            注:prisma\schema.prisma 文件里多了一个 user 表的数据模型。


7. 读写数据库

    示例,创建 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"}


posted @ 2023-11-04 22:42  垄山小站  阅读(1649)  评论(0编辑  收藏  举报