高含有高分子成分的 Prisma 2 回归了

首先

因为前不久Prisma 2的预览版被宣布出来,所以我闲来无事就调查了一下。

由于仍处于预览阶段,实施和API可能会发生重大变更的可能性。

Prisma 1 对比 Prisma 2

Prisma 1 是什么来着?

直接使用graphcool的后端部分切割出来的框架是Prisma,简单来说,Prisma就是GraphQL as a Service。

从此篇文章(https://www.prisma.io/blog/prisma-raises-4-5m-to-build-the-graphql-data-layer-for-all-databases-663484df0f60)可以看出,Prisma已经筹集到450万美元的资金,旨在构建适用于所有数据库的GraphQL数据层。

而且,正如prisma-最快的GraphQL服务器实现所述,

具有GraphQL形式的ORM
带有MySQL/Postgre迁移助手
从模型定义自动生成索引
自动生成CRUD操作

具有这样的特征。

在Prisma中,我们使用GraphQL SDL来定义数据模型,

type User {
  id: ID! @id
  email: String @unique
  name: String!
  posts: [Post!]!
}

type Post {
  id: ID! @id
  title: String!
  published: Boolean! @default(value: false)
  author: User @relation(link: INLINE)
}
$ prisma deploy

当您执行此命令时,将自动生成上述模型的CRUD,并可以作为GraphQL API执行。

Prisma 2带来了很大的改变。

下图(引用自Preview的公告博客)简明地展示了1.x和2.x之间的差异。

在Prisma 1中,需要准备一个名为Prisma Server的服务器,其中集成了执行GraphQL <-> RDB转换的ORM部分和迁移功能。

正如图上所示,Prisma 2中已经取消了Prisma Server,而是直接由应用程序的API服务器与数据库进行交互。

顺便说一下,这个”API服务器”并不一定需要是GraphQL API。它可以是REST API,也可以是gRPC或者Thrift。此外,后面会提到,使用Prisma 2与数据库交互时也不一定需要使用GraphQL。

总之,Prisma 2已经完全与GraphQL分离,成为了一个ORM。

光子和电梯

刚才的图中也已经出现了,Prisma 2由两个重要组件构成。它们是Photon和Lift。

    • Photon: ORM本体。現状はMySQL/PostgreSQL/SQLiteのみに対応していますが、MongoDBへの対応も計画されているとのとこと

 

    Lift: マイグレーションエンジン。いわゆるRailsのマイグレーションとかと同じような位置づけ

在Prisma 2中,基于SDL的模型定义仍然存在。我们将创建类似GraphQL SDL的定义体,如下所示。这将成为所有内容的基础。

datasource db {
  provider = "sqlite"
  url      = "file:dev.db"
  default  = true
}

model User {
  id    String  @default(cuid()) @id @unique
  email String  @unique
  name  String?
  posts Post[]
}

Photon的CLI根据这个定义体,可以生成可以从应用程序源代码中导入的DAO代码。目前只实现了JavaScript/TypeScript的代码输出,但是Go语言似乎也包含在里程碑计划中。对于TypeScript来说,还包含模型的类型信息,这样在编写代码时可以进行补全和错误检查,非常方便。

Lift根据Prisma文件生成迁移。当执行Lift的CLI时,会在migrations目录下创建带有时间戳前缀的目录,并逐步应用迁移到数据库中,可以顺利应用迁移。如果你有接触过Rails的迁移,应该很容易理解。

与GraphQL兼容性

正如上述所提到的,Prisma 2专注于ORM层。与Prisma 1相比,它与模型定义到GraphQL的CRUD操作之间的无缝创建具有显著差异。

关于这个问题,在官方的常见问题解答中明确提到,如下所示进行了回答。

随着Prisma 2,Prisma的查询引擎不再公开符合规范的GraphQL端点,因此不再官方支持在Prisma 2中使用模式委派和GraphQL绑定。要使用Prisma 2构建GraphQL服务器,请务必查看GraphQL Nexus及其nexus-prisma集成。GraphQL Nexus提供了一种基于代码和类型安全的方式来构建可扩展的GraphQL服务器。

「虽然 Prisma 2 不支持 GraphQL binding,但是我认为如果使用 GraphQL Nexus(也是 Prisma 组织下的工具),就可以相对容易地实现了。所以可能更多是关于这些其他工具链的讨论。」

项目的流程

由于翻译角度的原因,我进行了一些笔记,但只是简单地抄写了官方示例加上一点补充,所以请无视它们也没有关系。

安装Prisma 2 CLI。

$ npm i -g prisma2

创建项目

$ prisma2 init prisma2-nexus-example
asciicast
    • sqlite

 

    GraphQL APIのboilerplateを利用

完成无事项目后,您可以使用锅炉板设置的模型定义文件进行查看。

datasource db {
  provider = "sqlite"
  url      = "file:dev.db"
  default  = true
}

generator photon {
  provider = "photonjs"
}

generator nexus_prisma {
  provider = "nexus-prisma"
}

model User {
  id    String  @default(cuid()) @id @unique
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        String   @default(cuid()) @id @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean
  title     String
  content   String?
  author    User?
}

数据库的设置

根据上述的模型定义,在Lift中执行迁移,并准备数据库。

首先,需要从当前模型定义中创建一个包括CREATE TABLE等操作的迁移。可以使用以下命令执行该操作。

$ prisma2 lift save

当您输入一个适当的名称(例如init),将会以时间戳-名称的形式创建在下图所示的migrations目录下。

- prisma/                             
 |- migrations/                       
  |- 20190808024227-init/             
   |  datamodel.prisma                
   |  README.md                       
   |  steps.json                      
  |  lift.lock                        
 |  schema.prisma                     

您可以从README.md中查看此迁移将执行的TDL以及原始模型定义的差异信息。

Image from Gyazo

请使用以下命令来应用已创建的迁移。

$ prisma2 lift up

顺便提一下,回滚操作可以使用 prisma2 lift down。

光子的准备

接下来是Photon端。通过执行以下命令,将生成Photon的TypeScript客户端。

$ prisma2 generate

话虽这么说,Photon的客户端代码会在node_modules/@generated/photon目录下生成。
一开始看到时我还纳闷,为什么要放在node_modules下面呢…但是如果放在src目录下,生成的代码会在tsserver中展开,这样在更新和重新生成的时候,tsserver中的文件还是旧的状态,不方便进行补全和错误检查等操作。
因为这个原因,生成命令被指定为package.json中的postinstall操作。

光子的运行

既然已经准备好了数据库和Photon客户端,让我们来执行Photon吧。

因为我们选择的这个模版包含了在seed.ts中创建用户的示例,所以我们将执行该示例。

import Photon from '@generated/photon'
const photon = new Photon()

async function main() {

  const user1 = await photon.users.create({
    data: {
      email: 'alice@prisma.io',
      name: 'Alice',
      posts: {
        create: {
          title: 'Join us for Prisma Day 2019 in Berlin',
          content: 'https://www.prisma.io/day/',
          published: true,
        },
      },
    },
  })
  const user2 = await photon.users.create({
    // 中略
  })

  console.log({ user1, user2 })
}

main()
  .catch(e => console.error(e))
  .finally(async () => {
    await photon.disconnect()
  })

Photon.users.create(…)这部分是Photon的执行,使用TypeScript补全非常高兴。

$ yarn run ts-node prisma/seed.ts

当查看数据库时,应该能够确认通过 Photon 创建了记录并存入了 User 表中。

$ sqlite3 prisma/dev.db
sqlite> select name from User;
Alice
Bob

应用程序执行

也许与Prisma 2无关,但由于选择了作为GraphQL API服务器的基本模板,因此只需要在当前状态下运行yarn start即可执行。

Image from Gyazo

顺便提一句,与GraphQL相关的技术栈如下所示。

    • APIサーバー層: graphql-yoga

Resolverフレームワーク層:GraphQL Nexus

总之,GraphQL相关部分只是最初准备好的代码在运行而已。如果非要说的话,只能说resolver在引用Photon的客户端代码而已。

模型的编辑与应用程序的实现更改

我尝试实施了“在表上添加列并实现用于设置该列的GraphQL变更”这个任务。

在Post模型中添加一个名为category的字段。

model Post {
  # ...略
  category  String?
}

使用 Lift 创建和执行迁移。

$ prisma2 lift save
$ prisma2 lift up

光子客户端也会重新生成。

$ prisma2 generate

我們需要修改GraphQL Nexus的程式碼,並更改Post類型的定義。

export const Post = objectType({
  name: 'Post',
  definition(t) {
    t.model.id()
    t.model.createdAt()
    t.model.updatedAt()
    t.model.title()
    t.model.content()
    t.model.published()
    t.model.author()
    t.model.category()  // これが追加する部分
  },
})

关于上述的 t.model.category() 这部分,通过 nexus-prisma 插件,Prisma 2 生成了用于将 Photon 模型信息作为 GraphQL 的类型定义进行使用的接口,并且可以通过 Nexus 方面来引用该类型,从而实现一些便捷的功能。说实话,我还不太了解 Prisma 2 和 GraphQL Nexus 之间的关系。

解释mutation的resolver只需将其作为Photon Client编写即可,这样更易理解。

    t.field('setCategory', {
      type: 'Post',
      args: {
        id: idArg(),
        category: stringArg(),
      },
      resolve: async (_, { id, category }, ctx) => {
        return await ctx.photon.posts.update({
          where: { id },
          data: {
            category,
          },
        })
      },
    })

另外,如果你有兴趣,可以在GitHub的https://github.com/Quramy/prisma2-nexus-example中找到在示例中的工作,并进行推送。这个提交对应于“模型更改+实现修正”的工作。

个人感受

作为ORM

我之前已经多次提到过,Prisma 2基本上只是一个ORM。

在JavaScript/TypeScript的ORM上下文中,我们在比较TypeORM和其他类似工具时,讨论的重点就是它们之间的区别。

我更喜欢Prisma 2,它只使用了简单的对象,相较于倾向于Hibernate/ Active Record风格的TypeORM。

对于Prisma 2的核心由Rust实现这一事实,一方面,我也有一些担忧,例如,当出现问题时,我是否能够自行追踪代码?另外,作为开源软件,它是否能够得到有效的持续维护?

作为GraphQL的工具

最初にPrisma 2のプレビューを読もうと思ったのは、GraphQLの文脈であるからですが、GraphQL APIサーバーのバックエンドとして考えると、使い方があまり明確ではなく、どうなのかよくわかりません。

通过松散耦合化努力,可以肯定地说相较于Prisma 1.x时期,开发工作变得更加繁琐了,同时虽然Nexus被推荐使用且属于prisma组织的存储库,但我还是有些担心核心贡献者并非来自Prisma的这一点。

另外,当我同时接触Nexus和Prisma 2时,下面列举的哲学差异引起了我的一些关注。

    • Prisma 2: PSL使った定義体ドリブンなフレームワーク

 

    Nexus: GraphQL type定義をコードベースで記述し、最後にGraphQL SDLが自動で出力されるフレームワーク

如果是同样的prisma系工具,结合类似graphqlgen的SDL优先库可能更加符合需求吧?我认为对于简单的CRUD操作来说,使用这种方式定义会变得相当冗长(也有个人不太喜欢在实现中逐渐添加注解的框架的原因)。

你可以自己搭建服务器,也可以使用名为Prisma cloud的托管服务。

准确来说,它被称为PSL(Prisma Schema Language)。

参考链接:https://github.com/prisma/prisma2/blob/master/docs/faq.md#does-photon-support-graphql-schema-delegation-and-graphql-binding

参考链接:https://github.com/prisma/photonjs/issues/77#issuecomment-508162695 大体是这样的,如果想要tsserver能够正确支持这个,就需要创建插件,向DocumentRegistory适当通知更新并且清空ScriptVersionCache,等等…

广告
将在 10 秒后关闭
bannerAds