朋仔 發表於 2023-11-4 22:42:00

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

<p>Next.js 是一个用于构建 Web 应用程序的框架。Next.js 是一个用于生产环境的 React 框架,是一个 React 服务端渲染应用框架。</p>
<p>NextJS: https://nextjs.org/</p>
<p><br>Prisma 是一个基于 promise 的 Node.js 和 TypeScript 的 ORM,目前支持
Mysql,MariaDB,SQLite,PostgreSQL,AWS Aurora Serverless 和 Aws Aurora ,暂不支持
Microsft SQL Server 。Prisma 通过提供 类型安全、丰富的自动补全、平滑的 API 等特性。<br><br>Prisma: https://www.prisma.io <br>Prisma CN: https://prisma.yoga/<br>Prisma NextJS: https://www.prisma.io/nextjs</p>
<p>&nbsp;</p>
<p>Sequelize 是一个基于 promise 的 Node.js 的 ORM,目前支持 Mysql,Postgres,MariaDB,SQLite 以及 Microsft SQL Server。它具有强大的事务支持,关联关系,预读和延迟加载,读取复制等功能。<br>Sequelize: https://sequelize.org/</p>
<p>
Sequelize CN: https://www.sequelize.cn/</p>
<p><br>Prisma 和 Sequelize 各自支持的功能比较表如下:</p>
<table border="0">
<tbody>
<tr>
<td style="text-align: center">&nbsp;</td>
<td style="text-align: center"><strong>Prisma</strong></td>
<td style="text-align: center"><strong>Sequelize</strong></td>





</tr>
<tr>
<td>原始查询</td>
<td>yes</td>
<td>yes</td>





</tr>
<tr>
<td>事务</td>
<td>yes</td>
<td>yes</td>





</tr>
<tr>
<td>自动生成 Schema</td>
<td>yes</td>
<td>yes</td>





</tr>
<tr>
<td>迁移</td>
<td>yes</td>
<td>yes</td>





</tr>
<tr>
<td>TypeScript</td>
<td>yes</td>
<td>yes</td>





</tr>
<tr>
<td>子查询</td>
<td>yes</td>
<td>no</td>





</tr>
<tr>
<td>读写分离</td>
<td>no</td>
<td>yes</td>





</tr>
<tr>
<td>乐观锁</td>
<td>yes</td>
<td>no</td>





</tr>
<tr>
<td>高级函数</td>
<td>yes</td>
<td>no</td>





</tr>





</tbody>





</table>
<p><br>本文选择使用 Node.js + Next.js + Prisma + MySQL 搭建 JSON API 服务。</p>
<p>&nbsp;</p>
<h3>1. 系统环境</h3>





&nbsp;&nbsp;&nbsp; NodeJS:&nbsp; 16.20.1<br>&nbsp;&nbsp;&nbsp; NPM: 8.19.4<br>&nbsp;&nbsp;&nbsp; NextJS: 13.4.12<br>&nbsp;&nbsp;&nbsp; Prisma:5.5.2<br>
<p>&nbsp;</p>
<h3>2. 创建 NextJS 项目</h3>
<p>&nbsp;&nbsp;&nbsp; 安装 create-next-app 脚手架,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # 使用 -g 参数,表示该命令只需在本机上运行一次<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm install -g create-next-app@13.4.12&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:或直接使用如下命令创建 next 项目 &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npx create-next-app@13.4.12<br><br>&nbsp;&nbsp;&nbsp; 使用 create-next-app 命令创建 NextJS 项目,命令如下:</p>
<div class="cnblogs_code">
<pre>      $ 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.</pre>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:这里选择 App Router<br><br>&nbsp;&nbsp;&nbsp; 进入 furniture-service 项目目录安装依赖,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm install<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp; 运行 furniture-service 项目,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm run start&nbsp;&nbsp;&nbsp;&nbsp; # npm run dev <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp; 浏览器访问 http://localhost:3000,显示内容如下:<br>&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Get started by editing src/app/page.js</p>
<h3><br>3. API 路由</h3>
<p>&nbsp;&nbsp;&nbsp; 路由处理程序 (Route Handlers) 允许用户使用 Web 请求和响应 API 为给定路由创建自定义请求处理程序。它定义在 app 目录及其子目录下的 route.js 或 route.ts 文件中,比如:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/api/route.js<br><br>&nbsp;&nbsp;&nbsp; 路由处理程序类似于 page.js 和 layout.js,但在同一目录下 page.js 和 router.js 不能同时存在。<br><br>&nbsp;&nbsp;&nbsp; 支持以下 HTTP 方法:GET、POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS。如果调用了不支持的方法,Next.js 将返回一个 405 method Not Allowed 响应。<br><br>&nbsp;&nbsp;&nbsp; 示例,创建 app/api/route.js 文件,内容如下:</p>
<div class="cnblogs_code">
<pre>       import { NextResponse } from 'next/server'<span style="color: rgba(0, 0, 0, 1)">;

      export async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> GET(request) {

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">console.log(request.nextUrl.searchParams);</span>
            <span style="color: rgba(0, 0, 255, 1)">return</span> NextResponse.json({ ret: 'GET Success' }, { status: 200<span style="color: rgba(0, 0, 0, 1)"> });
      }

      export async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> POST(request) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">console.log(request);</span>
            <span style="color: rgba(0, 0, 255, 1)">return</span> NextResponse.json({ ret: 'POST Success' }, { status: 200<span style="color: rgba(0, 0, 0, 1)"> });
      }</span></pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp; 运行 furniture-service 项目,浏览器访问 http://localhost:3000/api,显示内容如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"ret":"GET Success"}<br><br>&nbsp;&nbsp;&nbsp; Postman 用 POST 方法访问 http://localhost:3000/api,显示内容如下: <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "ret": "POST Success"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br></p>
<h3><br>4. 安装 MySQL 支持</h3>
<p>&nbsp;&nbsp;&nbsp; 手动创建 MySQL 数据库 testdb 和 user 表,SQL 脚本如下:</p>
<div class="cnblogs_code">
<pre>      <span style="color: rgba(0, 0, 255, 1)">CREATE</span> <span style="color: rgba(0, 0, 255, 1)">TABLE</span> `<span style="color: rgba(255, 0, 255, 1)">user</span><span style="color: rgba(0, 0, 0, 1)">` (
            `id` </span><span style="color: rgba(0, 0, 255, 1)">int</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">11</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)"> AUTO_INCREMENT,
            `username` </span><span style="color: rgba(0, 0, 255, 1)">varchar</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">50</span>) <span style="color: rgba(128, 128, 128, 1)">NOT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
            `password` </span><span style="color: rgba(0, 0, 255, 1)">varchar</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">255</span>) <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
            `age` </span><span style="color: rgba(0, 0, 255, 1)">int</span>(<span style="color: rgba(128, 0, 0, 1); font-weight: bold">11</span>) <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
            `createtime` </span><span style="color: rgba(0, 0, 255, 1)">timestamp</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span> <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> <span style="color: rgba(0, 0, 255, 1)">NULL</span><span style="color: rgba(0, 0, 0, 1)">,
            </span><span style="color: rgba(0, 0, 255, 1)">PRIMARY</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span><span style="color: rgba(0, 0, 0, 1)"> (`id`),
            </span><span style="color: rgba(0, 0, 255, 1)">UNIQUE</span> <span style="color: rgba(0, 0, 255, 1)">KEY</span><span style="color: rgba(0, 0, 0, 1)"> (`username`)
      ) ENGINE</span><span style="color: rgba(128, 128, 128, 1)">=</span>InnoDB <span style="color: rgba(0, 0, 255, 1)">DEFAULT</span> CHARSET<span style="color: rgba(128, 128, 128, 1)">=</span><span style="color: rgba(0, 0, 0, 1)">utf8;

      # 创建一条用户记录
      </span><span style="color: rgba(0, 0, 255, 1)">INSERT</span> <span style="color: rgba(0, 0, 255, 1)">INTO</span> <span style="color: rgba(255, 0, 255, 1)">user</span> (username, password, age, createtime) <span style="color: rgba(0, 0, 255, 1)">VALUES</span> (<span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">admin</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(255, 0, 0, 1)">'</span><span style="color: rgba(255, 0, 0, 1)">123456</span><span style="color: rgba(255, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1); font-weight: bold">18</span>, Now());</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp; 进入 furniture-service 项目目录,安装 MySQL 支持,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm install mysql mysql2 --save<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp; 修改 app/api/route.js 文件,内容如下:</p>
<div class="cnblogs_code">
<pre>      import { NextResponse } from 'next/server'<span style="color: rgba(0, 0, 0, 1)">;
      import mysql from </span>'mysql'<span style="color: rgba(0, 0, 0, 1)">;


      const getData </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {

                </span><span style="color: rgba(0, 0, 255, 1)">var</span> connection =<span style="color: rgba(0, 0, 0, 1)"> mysql.createConnection({   
                  host   : </span>'localhost'<span style="color: rgba(0, 0, 0, 1)">,      
                  user   : </span>'root'<span style="color: rgba(0, 0, 0, 1)">,            
                  password : </span>'123456'<span style="color: rgba(0, 0, 0, 1)">,      
                  port: </span>'3306'<span style="color: rgba(0, 0, 0, 1)">,                  
                  database: </span>'testdb'<span style="color: rgba(0, 0, 0, 1)">
                });
               
                connection.connect();
                connection.query(</span>'SELECT * FROM user', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (err, result) {
                  </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">console.log("getData() -&gt; reject: " + err.message);</span>
                        reject({ ret: 'error'<span style="color: rgba(0, 0, 0, 1)">, msg: err.message })
                  }
               
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">console.log("getData() -&gt; resolve: " + result);</span>
                  resolve({ ret: 'success'<span style="color: rgba(0, 0, 0, 1)">, data: result});
                });
               
                connection.end();
            })
      }

      export async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> GET(request) {

            let data </span>=<span style="color: rgba(0, 0, 0, 1)"> await getData();
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">console.log(data);</span>

            <span style="color: rgba(0, 0, 255, 1)">return</span> NextResponse.json( data, { status: 200<span style="color: rgba(0, 0, 0, 1)"> });
      }

      export async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> POST(request) {
            console.log(request);
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> NextResponse.json({ ret: 'POST Success' }, { status: 200<span style="color: rgba(0, 0, 0, 1)"> });
      }</span></pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp; 运行 furniture-service 项目,浏览器访问 http://localhost:3000/api,显示内容如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"ret":"success","data":[{"id":1,"username":"admin","password":"123456","age":18,"createtime":"2023-11-04T07:24:59.000Z"}]}</p>
<h3><br>5. 安装 ORM 支持</h3>
<p>&nbsp;&nbsp;&nbsp; <strong>1) 安装 Prisma</strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在 furniture-service 项目目录下安装 Prisma,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm install prisma --save<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:可以运行 npx prisma 来查看 prisma 的命令使用方法。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 创建 Prisma 架构文件模板来设置 Prisma 项目,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npx prisma init</p>
<div class="cnblogs_code">
<pre>                ✔ 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</pre>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以上命令会在 furniture-service 项目的根目录下创建一个 .env 文件和一个 prisma 目录,.env 文件用于定义环境变量(例如数据库连接),prisma 目录下生成了一个 schema.prisma 文件,schema.prisma 文件包含带有数据库连接变量和模式模型的 prisma 模式。</p>
<p><br>&nbsp;&nbsp;&nbsp;<strong> 2) 配置 Prisma</strong></p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改 .env 文件,内容如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DATABASE_URL="mysql://root:123456@localhost:3306/testdb?schema=public"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改 prisma/schema.prisma </p>
<div class="cnblogs_code">
<pre>            // 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")
            }</pre>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<strong> 3) 安装 Prisma Client</strong><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Prisma Client 提供常用的数据库 CRUD 方法: create、update、delete、findUnique、findMany 等。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安装 Prisma Client,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npm install @prisma/client<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</p>
<p>&nbsp;</p>
<h3>6. 使用 Prisma Migrate</h3>
<p>&nbsp;&nbsp;&nbsp; Prisma Migrate 是一个声明性数据库模型迁移工具,使您能够:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1) 随着 Prisma 架构的发展,保持数据库架构与 Prisma 数据模型同步<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2) 维护数据库中的现有数据<br><br>&nbsp;&nbsp;&nbsp; Prisma Migrate 生成 .sql 迁移文件的历史记录,并在开发和部署中发挥作用。<br><br>&nbsp;&nbsp;&nbsp; 上文我们是用手动方式在 testdb 数据库创建了 user 表,而且 app/api/route.js 文件里的 GET/POST 函数中没有使用 ORM (Prisma/Sequelize) 数据模型来操作数据库的表。 <br><br>&nbsp;&nbsp;&nbsp; 在我们熟悉 Prisma 数据模型的情况下,我们可以直接在 prisma\schema.prisma 文件里添加表的数据模型,再运行 npx prisma migrate dev --name init 命令把表迁移到数据库。<br><br>&nbsp;&nbsp;&nbsp; <strong>1) 新增 post 表</strong><br>&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改 prisma\schema.prisma 文件,添加 post 表的数据模型,内容如下:</p>
<div class="cnblogs_code">
<pre>            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
            }</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 执行 prisma migrate 命令,创建第 1 个数据库迁移,格式如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npx prisma migrate dev --name init&nbsp;&nbsp;&nbsp; // dev 表示开发环境</p>
<div class="cnblogs_code">
<pre>             ...

                [+] 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</pre>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:testdb 数据库里的 user 表是手动创建的,该表不在 prisma migrate 的版本管理范围之内,会提示是否重置数据库,这里选择了 y 重置数据库。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prisma migrate 命令会自动执行 prisma generate 命令读取 Prisma 架构并生成 Prisma Client 库。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查看 testdb 数据库,可以看到新增了两个表 post 和 _prisma_migrations(记录 migration 历史)。查看项目目录,新增了一个 prisma/migrations/20231106104925_init/migration.sql 文件。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以运行 npx prisma migrate status 命令查看 migration 状态。<br><br>&nbsp;&nbsp;&nbsp; <strong>2) 修改 post 表</strong><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改 prisma\schema.prisma 文件,修改 post 表的数据模型,内容如下:</p>
<div class="cnblogs_code">
<pre>            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
            }</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 执行 prisma migrate 命令,创建第 2 个数据库迁移,格式如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npx prisma migrate dev --name post_add_content<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查看 testdb 数据库,可以看到 post 表结构已更新,_prisma_migrations 里新增了一条记录。查看项目目录,新增了一个 prisma/migrations/20231106130648_post_add_content/migration.sql 文件。<br><br>&nbsp;&nbsp;&nbsp; <strong>3) 反向生成 Prisma 数据模型</strong><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上文运行 npx prisma migrate dev --name init 命令过程中,我们选择了 y 重置了 testdb。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果我们需要保留 user 表结构,可以在运行 prisma migrate 命令之前,反向生成 prisma 数据模型,把 user 表数据模型保存到 prisma\schema.prisma 文件,命令如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ npx prisma db pull</p>
<div class="cnblogs_code">
<pre>                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.</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查看 prisma\schema.prisma 文件,内容如下:</p>
<div class="cnblogs_code">
<pre>            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)
            }</pre>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:prisma\schema.prisma 文件里多了一个 user 表的数据模型。</p>
<h3><br>7. 读写数据库</h3>
<p>&nbsp;&nbsp;&nbsp; 示例,创建 app/api2/route.js 文件,内容如下:</p>
<div class="cnblogs_code">
<pre>      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 });
      }</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp; 运行 furniture-service 项目,在 postman 上用 POST 方法访问 http://localhost:3000/api2,HTTP 请求的 Body 类型为 raw(JSON 格式),内容如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"username":"user","password":"abcdef","age": 99}<br><br>&nbsp;&nbsp;&nbsp; 返回结果为:</p>
<div class="cnblogs_code">
<pre>      {
            "id": 2,
            "username": "user",
            "password": "abcdef",
            "age": 99,
            "createtime": "2023-11-04T12:27:47.000Z"
      }</pre>
</div>
<p><br>&nbsp;&nbsp;&nbsp; 浏览器访问 http://localhost:3000/api2, 显示结果如下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"id":2,"username":"user","password":"abcdef","age":99,"createtime":"2023-11-04T12:27:47.000Z"}<br><br><br></p><br><br>
来源:https://www.cnblogs.com/tkuang/p/17809971.html
頁: [1]
查看完整版本: NodeJS系列(13)- Next.js 框架 (六) | Node.js + Next.js + Prisma/Sequelize (ORM) + MySQL 搭建 JSON API 服务