cisen / blog

Time waits for no one.
133 stars 20 forks source link

prisma 一个能够快速构建Graphql API服务的后端框架 #536

Open cisen opened 5 years ago

cisen commented 5 years ago

简介

https://github.com/prisma/prisma https://prisma.1wire.com/ https://github.com/prisma/graphql-prisma-typescript Prisma 是现代应用程序的数据层。 它通过 Prisma client使用通用数据库抽象替换传统的 ORM 和数据访问层。 Prisma 用于构建 GraphQL 服务器,REST API 等。

作用

连接数据库和任何前端应用

可看作类似Sequelize和Mongoose的ORM层和Graphql技术的结合,能通过Prisma高性能引擎处理数据库的繁杂操作并提供数据给前端应用。

自动生成代码

只需定义字段就能生成所有操作数据的代码,5分钟即可搭建出生产环境级别的后端服务,目前已全面支持JavaScript、TypeScript、Go语言。

完全兼容Express框架,可选择任何数据库 不仅适合零基础开发者,Node.js开发者迁移成本也为零。目前成熟支持的数据库包括MySQL、PostgreSQL和MongoDB。

为什么使用Prisma

Prisma能让你在极短时间内搭建好成熟完善的后端服务

特点

零基础

不需要任何Graphql基础,甚至Node.js也无需熟练,阅读文档和教程操作即可构建成熟可靠的API后端服务。

面向未来

RESTful将死,Graphql长存。简单易用且性能强劲,更适应移动端、小程序及React、Vue等现代Web框架

生产环境成熟

Facebook、Github等公司的后端API大部分使用Graphql,Prisma让你的团队零负担拥有最前沿架构

demo

定义数据段type

Prisma仅需字段和格式就可定义所有属性 新:

type User {
  id: ID! @id
  createdAt: DateTime! @createdAt
  name: String!
  admin: Boolean! @default(value: "true")
}

旧:

import {
    GraphQLObjectType,
    GraphQLString,
    GraphQLID,
    GraphQLBoolean
} from 'graphql';

import models from '../../models/index.js';

export default new GraphQLObjectType({
    name: 'user',
    description: 'user of some quote',
    fields () {
        return {
            id: {
                type: GraphQLID,
                description: "user's ID",
                resolve (user) {
                    return user.id;
                }
            },
            name: {
                type: GraphQLString,
                description: "user's name",
                resolve (user) {
                    return user.name;
                }
            },
            createdAt: {
                type: GraphQLString,
                description: "user's createdAt",
                resolve (user) {
                    return user.createdAt;
                }
            },
            admin: {
                type: GraphQLBoolean,
                description: "user's status",
                resolve (user) {
                    return user.admin;
                }
            }
        };
    }
});

数据库定义

CREATE TABLE User(
    id CHAR(25),
    createdAt DATETIME NOT NULL DEFAULT CURRENT
    updatedAt DATETIME NOT NULL DEFAULT CURRENT
    name MEDIUMTEXT NOT NULL,
    admin BOOLEAN NOT NULL DEFAULT TRUE,
    PRIMARY KEY (id),
    UNIQUE INDEX id_UNIQUE (id ASC)
)

查询query

Prisma实现查询所需要的解析极为简单

另外排序、过滤、全文查询、嵌套查询详见文档 新

//Query
{
  userList {
    name
    isAdmin
  }
}
//Resolvers
const Query = {
  userList: (_, args, context, info) => {
    return context.prisma.query.users({}, info)
  }
}

sql查询

//Query
  {
    userList {
      name
      isAdmin
    }
  }
//Resolvers
  const Query = {
    userList: (_, args, context, info) => {
      return mysql.query(
        SELECT
            "user"."id",
            "user"."name",
            "user"."isAdmin"
          FROM tblUsers as "user"
      )
    }
  }

返回

{
    "data": {
      "userList": [
        {
          "name": "Jane Dixie",
          "isAdmin": true
        },
        {
          "name": "Arnold Anderson",
          "isAdmin": false
        }
      ]
    }
  }

突变mutation

//Mutation
mutation {
  createPost(data: {
    title: "Join us for GraphQL Europe"
    authorEmail: "hello@graphql-europe.org"
  }) {
    id
  }
}
//Resolvers
const Mutation = {
  createPost: (_, args, context, info) => {
    return context.prisma.mutation.createPost(
      {
        data: {
          title: args.title,
          author: {
            connect: { email: args.authorEmail },
          },
        },
      },
      info
    )
  },
}

sql读写数据

//Mutation
mutation {
  createPost(data: {
    title: "Join us for GraphQL Europe"
    authorEmail: "hello@graphql-europe.org"
  }) {
    id
  }
}
//Resolvers
const Query = {
  searchAdminPosts: (_, args, context, info) => {
    const postId = mysql.query(
      INSERT INTO "tblPosts" ("title", "authorId")
      VALUES (
        '${args.title}',
        (
          SELECT TOP(1) "id"
          FROM tblUsers
          WHERE "tblUsers"."email" = '${args.authorEmail}'
        )
      )
    )

    return mysql.query(
      SELECT
          "post"."id",
          "post"."createdAt",
          "post"."title",
          "user"."id",
          "user"."name",
          "user"."isAdmin"
        FROM tblPosts AS "post"
        LEFT JOIN tblUsers AS "user" ON "user"."id" = "post"."authorId"
        WHERE "post"."id" = '${postId}'
    )
  }
}

返回

{
    "data": {
      "createPost": [
        {
          "id": "cjh0puo0b0000dvpi7b2e87c7"
        }
      ]
    }
  }

开始使用

  1. 安装 npm 或 yarn 安装,国内推荐使用 cnpm:

没有cnpm请先安装:npm i -g cnpm

cnpm i -g prisma

or

yarn global add prisma mac 用户也可选择使用 brew 安装:

brew tap prisma/prisma brew install prisma

  1. 连接数据库和 Prisma 有两种方式启动数据库和 Prisma,熟悉 docker 的用户选择第一种,不会 docker 的用户可以选择先部署在 Prisma 提供的云上,选第二种方式:

  2. Docker 用户

prisma init hello-world 在终端中可以看到各种选项,按如下选择:

选择 Create new database (你也可以选择 existing database ) 选择数据库类型: MySQL 或 PostgreSQL 选择生成 Prisma Client的编程语言: TypeScript, Flow, JavaScript 或 Go 向导终止后,运行以下命令来启动 Prisma:

cd hello-world docker-compose up -d 部署到已有数据库?修改 docker-compose.yml 中的参数为你私有数据库参数即可(目前只支持 PostgreSQL)。

  1. Prisma 提供测试服务器

prisma init hello-world 在终端中可以看到各种选项,按上下键选择(windows 用户建议使用 PowerShell):

选择 Demo server 此时会打开 app.prisma.io 网页,可以注册登录或使用 github 账户登录,登录后点击授权即可。 注意此处千万不能用数字开头的邮箱注册(尤其 qq 邮箱),包括注册 github 的邮箱,如果你的 github 账户邮箱由数字开头,建议用字母开头邮箱注册 app.prisma.io 终端显示 Authenticating 后选择你想部署在哪个区域,有两个选择,欧洲和美国,后面有延迟状态,选延迟低的回车确认。如:账户名/demo-us1 即美国地区。 填入你想使用的名字,这里我们直接回车,选择 stage 分支,默认是 dev,再次直接回车就行。 选择生成 Prisma Client的编程语言,这里我们选择 Prisma JavaScript Client,如选择 TS,注意后面安装依赖会多个 graphql-tools。 初始文件已生成,按照提示进入目录部署: cd hello-world && prisma deploy 此时你的后端 API 已经可以访问,打开终端显示的 http 地址即可看到 Prisma playground prisma.yml是 Prisma 的根配置文件。 datamodel.prisma定义应用程序的数据库的数据模型(它基本上定义了数据库模式)。 如果报错,请 prisma account 查看名称开头是否为数字。

  1. 定义数据模型 打开编辑器编辑 datamodel.prisma 使用 SDL 语言风格 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 }

  1. 部署 Prisma API 在终端输入以下命令变更数据库表并生成新 client:

prisma deploy prisma generate Prisma API 基于数据模型进行部署,并为该文件中的每个模型公开 CRUD 和实时操作。

  1. 使用 Prisma client (JavaScript) Prisma 客户端连接到 Prisma API,允许你对数据库执行读写操作。 本节介绍如何使用 JavaScript 中的 Prisma client。

首先,在hello-world中初始化并安装依赖:

cnpm init -y cnpm install --save prisma-client-lib prisma generate 在hello-world目录中创建一个新的 Node 脚本:

touch index.js

or 在编辑器中新建index.js

编辑如下代码:

const { prisma } = require('./generated/prisma-client') // 一个main函数,以便我们可以使用async/await async function main() { // 新建一个user,并新建一个post文章 const newUser = await prisma .createUser({ name: "Alice", posts: { create: { title: "The data layer for modern apps", } }, }) console.log("Created new user: ${newUser.name} (ID: ${newUser.id})") // 从数据库中读取所有用户user并将其打印到控制台 const allUsers = await prisma.users() console.log(allUsers) // 从数据库中读取所有文章post并将其打印到控制台 const allPosts = await prisma.posts() console.log(allPosts) } main().catch(e => console.error(e)) 最后,在终端启动这个文件:

node index.js 此时终端显示用户已被存到数据库:

Created new user: Alice (ID: cjn70mkjlgnv00b77iyk9zx0e) [ { id: 'cjn70mkjlgnv00b77iyk9zx0e', email: null, name: 'Alice' } ] [ { id: 'cjn70mkjrgnv10b77nhqgg2zd', title: 'The data layer for modern apps', published: false } ] 查看更多操作

  1. 下一步 以上操作仅展示基本能力,数据也是写死的,学习真实环境代码,移步文档和教程
cisen commented 5 years ago

Get Started

目标:成功构建一个 Graphql API 服务器!Go->

Prisma 1.17 以前的版本改动较大,历史上也出现很多破坏性更新,请确保你的 prisma 版本为 1.19 及以上,本文档同步官方保持最新。 本文档采用 1.32 版本语法,即 datamodel SDL 1.1 。在 id、时间、关系上有所变动,如要使用以前版本,请至官网查看。

安装及部署 本节内容与首页相似,但是加了更多手动操作的内容。

如果你已使用 Prisma Demo Server 成功部署,可直接跳过到构建应用,另外有学习路径可参考。

如果你想在本地用 docker 新起一个 Prisma 服务器,参考这里。

如果你想用 Prisma 连接已运行的数据库,参考这里。

  1. 安装 npm 或 yarn 安装,国内推荐使用 cnpm:

没有cnpm请先安装:npm i -g cnpm

cnpm i -g prisma

or

yarn global add prisma

然后查看版本,出现即安装成功

prisma -v mac 用户也可选择使用 brew 安装:

brew tap prisma/prisma brew install prisma

  1. 连接数据库和 Prisma

cd到你常用的目录

prisma init hello-world 在终端中可以看到各种选项,按上下键选择(windows 用户建议使用 PowerShell):

想在本地用 docker 跑的用户选择 new database,参考这里

选择 Demo server 此时会打开 app.prisma.io 网页,可以注册登录或使用 github 账户登录,登录后点击授权即可。 注意此处千万不能用数字开头的邮箱注册(尤其 qq 邮箱),包括注册 github 的邮箱,如果你的 github 账户邮箱由数字开头,建议用字母开头邮箱注册 app.prisma.io

终端显示 Authenticating 后选择你想部署在哪个区域,有两个选择,欧洲和美国,后面有延迟状态,选延迟低的回车确认。如:账户名/demo-us1 即美国地区。 填入你想使用的名字,这里我们直接回车,选择 stage 分支,默认是 dev,再次直接回车就行。 选择生成 Prisma Client的编程语言,这里我们选择 Prisma JavaScript Client,如选择 TS,注意后面安装依赖时会多个 graphql-tools。 初始文件已生成,按照提示进入目录部署: cd hello-world && prisma deploy 此时你的后端 API 已经可以访问,打开终端显示的 http 地址即可看到 Prisma playground prisma.yml是 Prisma 的根配置文件。 datamodel.prisma定义应用程序的数据库的数据模型(它基本上定义了数据库模式)。 generated/prisma-client/包含了自动生成的 prisma client 代码。 如果报错,请prisma account 查看名称开头是否为数字。

  1. 定义数据模型 打开编辑器编辑 datamodel.prisma 使用 SDL 语言风格 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 }

  1. 部署 Prisma API 在终端输入以下命令变更数据库表并生成新 client:

prisma deploy prisma generate Prisma API 基于数据模型进行部署,并为该文件中的每个模型公开 CRUD 和实时操作。

  1. 使用 Prisma client (JavaScript) Prisma client 连接到 Prisma API,允许你对数据库执行读写操作。 本节介绍如何使用 JavaScript 中的 Prisma client。

在hello-world目录中创建一个新的 Node 脚本:

touch index.js

or 在编辑器中新建index.js

编辑如下代码:

const { prisma } = require('./generated/prisma-client') // 一个main函数,以便我们可以使用async/await async function main() { // 新建一个user,并新建一个post文章 const newUser = await prisma .createUser({ name: "Alice", posts: { create: { title: "The data layer for modern apps", } }, }) console.log("Created new user: ${newUser.name} (ID: ${newUser.id})") // 从数据库中读取所有用户user并将其打印到控制台 const allUsers = await prisma.users() console.log(allUsers) // 从数据库中读取所有文章post并将其打印到控制台 const allPosts = await prisma.posts() console.log(allPosts) } main().catch(e => console.error(e)) 最后,在终端用初始化项目并启动这个文件:

cnpm init -y cnpm install --save prisma-client-lib graphql@0.13 node index.js 此时终端显示用户已被存到数据库:

Created new user: Alice (ID: cjn70mkjlgnv00b77iyk9zx0e) [ { id: 'cjn70mkjlgnv00b77iyk9zx0e', email: null, name: 'Alice' } ] [ { id: 'cjn70mkjrgnv10b77nhqgg2zd',title: 'The data layer for modern apps',published: false } ] 查看更多操作 构建 App 以下内容为构建 Graphql API 应用,如需使用 RESTful API,请参考RESTful API APP

配置项目 grahpql-yoga 是基于 Express.js的 Graphql 服务器. 先安装它:

npm install --save graphql-yoga 每个 GraphQL API 都基于GraphQL Schema,它指定了所有 API 操作和数据结构。可以理解为 schema 是前端和服务器之间的约定和连接器,schema 指定了前端能取到什么样的后端数据。

创建一个名为schema.graphql的新文件:

touch schema.graphql 要定义 API 操作,需要在 GraphQL schema 中指定Query和Mutation类型 - 以下操作是简单博客应用程序的示例,输入到 schema.graphql 中:

type Query { publishedPosts: [Post!]! post(postId: ID!): Post postsByUser(userId: ID!): [Post!]! } type Mutation { createUser(name: String!): User createDraft(title: String!, userId: ID!): Post publish(postId: ID!): Post } type User { id: ID! email: String name: String! posts: [Post!]! } type Post { id: ID! title: String! published: Boolean! author: User } post和User类型是对datamodel.prisma中指定的模型的直接重新定义。 此时的定义是用于前端应用,前面说了,schema 指定了前端能取到什么样的后端数据,所以要再声明一遍,除了已删除特定于 Prisma 对于数据库的特殊指令。 教程里我们会学习到,schema 里是要去掉敏感数据如密码的,因为 schema 里定义的模型,前端用户都能够取到,登录教程我们后面再说。

实现解析器功能 以下是如何为 GraphQL schema 中定义的六个 API 操作实现解析器(resolvers)函数。 用以下代码片段完全替换index.js的当前内容:

const { prisma } = require('./generated/prisma-client') const { GraphQLServer } = require('graphql-yoga') const resolvers = { Query: { publishedPosts(root, args, context) { return context.prisma.posts({ where: { published: true } }) }, post(root, args, context) { return context.prisma.post({ id: args.postId }) }, postsByUser(root, args, context) { return context.prisma.user({ id: args.userId }).posts() } }, Mutation: { createDraft(root, args, context) { return context.prisma.createPost( { title: args.title, author: { connect: { id: args.userId } } }, ) }, publish(root, args, context) { return context.prisma.updatePost( { where: { id: args.postId }, data: { published: true }, }, ) }, createUser(root, args, context) { return context.prisma.createUser( { name: args.name }, ) } }, User: { posts(root, args, context) { return context.prisma.user({ id: root.id }).posts() } }, Post: { author(root, args, context) { return context.prisma.post({ id: root.id }).author() } } } 每个解析器都在 Prisma client 实例上调用一个名为prisma的方法,并附加到context对象。

配置 GraphQL 服务器 现在你需要从graphql-yoga库中实例化GraphQLServer并传递 GraphQL schema 及其解析器函数。导入的prisma client 实例附加到context,以便解析器可以访问它。

将以下代码段添加到index.js的底部:

const server = new GraphQLServer({ typeDefs: './schema.graphql', resolvers, context: { prisma }, }) server.start(() => console.log('Server is running on http://localhost:4000')) 启动 GraphQL 服务器 要启动 GraphQL 服务器,请运行以下命令:

node index.js 在 Playground 中查看应用程序层的 GraphQL API 应用程序层的 GraphQL API 现在公开了schema.graphql中定义的六个操作。

要测试这些操作,请将打开浏览器到GraphQL Playground的http://localhost:4000。

GraphQL Playground 是一个交互式 GraphQL IDE,可让你探索 GraphQL API 的操作。 你可以单击 Playground 窗口右边缘的绿色 SCHEMA - 按钮,查看 GraphQL API 的自动生成文档。 你可以把它看做是 Graphql 的 POST man,但是它可以直接查看 API 文档和自动补全输入,用起来你会扔掉别的后端的。

我们在 playground 里随便玩几个操作,写一段然后 run 一下看右边的输出,建议不要复制下面的代码,多用 tab 自动补全写一下:

mutation { createUser(name: "Bob") { id } } 拿到返回的 id 替换下面的 USER_ID 占位符

mutation { createDraft( title: "GraphQL is great" userId: "__USER_ID__" ) { id published author { id } } }

mutation { publish(postId: "__POST_ID__") { id title published } }

query { publishedPosts { id title author { id name } } } 在某些代码段中,你需要将USERIDPOST_ID__占位符替换为前面查到的实际用户的 ID。

学习路径 我在偶然的机会下接触到了 Prisma,然后对 Graphql 这项技术茅塞顿开。我从零开始学习它到现在已用于生产环境,这中间也踩过一些坑,而官方其实没有太多资料的,所以我有必要为后来者提供一些便利,从而使 Prisma 能够更好更快地被广大中国开发者掌握和使用。

到目前为止,国内其实没有一篇资料关于 Prisma,但我被它的技术和架构深深感染,Prisma 呈倍数地提升了整个团队的开发效率,它有必要被更多人认可。所以我联系到 Prisma 团队,在他们的支持下(尤其是 Prisma 的联合创始人 Schickling ),搭建了 Prisma 的中文文档页面,并初步翻译了文档。为了配合我的翻译工作,Prisma 团队加班完善了文档最新的版本,非常感谢。

在官方的描述中,Prisma 像是一个 ORM 层,类似 Sequelize 或 mongoose,但我在实际使用和深入理解后,发现并不是,Prisma 不仅仅是 ORM,它整个生态系统和技术栈就是一个框架,一整套完善的技术,一个新的后端,集成了更加强大的 ORM 层和数据库,可以说只要掌握 Prisma,就完全能独立开发出一个应用的后端。

Graphql 一直被吵的沸沸扬扬,无数前端工程师翘首以待,但直到今天,依然没有普及开,我觉得其中最重要的一点就是,它需要后端做更大量繁琐的工作,这在很多团队是不可能实现的,前后端团队互相推脱,甚至后端自己都无力推动。所以尽管 Graphql 能极大提升前端的效率和 APP 的速度,但后端复杂的问题不解决,就没有可能普及。

所以 Prisma 应需而生,它完全把复杂的东西抽离,后端仅仅定义好数据库字段 type 和解析器 resolver,前端就已经可以取到数据了,甚至还有自动生成的标准的 API 文档,没有数十个甚至上百个路由,没有各种判断和联调,也不需要考虑数据库连接,不仅解决原先 Graphql 后端的复杂度,甚至比以往的 Node 后端更加简单,而且由于强类型系统,还更加可靠,性能更强。

使用 Prisma 不用担心灵活性,Prisma 技术栈里的 Graphql Yoga 完全兼容 Express,比如说我业余时间开发的一款小程序的后端就完全是 Prisma(仅用一天就开发完成),但其中的微信登录我加了一条 POST 路由来完成微信登录获取 unionId(其实还是可以用 Prisma 的 resolvers 解决,但我复制粘贴图方便),和传统开发没有任何区别,所以不管你是要迁移还是在原先基础上升级,都没有任何问题。

不管是前端还是后端,未来不掌握 Prisma 技术栈,会在效率和先进性上吃很大的亏,越深入越觉得如此。

OK,说完上面一大堆,接下来我试图描述一下学习路径,简化学习成本。

在完成 Get Started 后,你就已经有了一个基础的后端,这时候我们就会明白,开发 Prisma 最重要的两个部分,一是 datamodel,二是 resolver。定义 datamodel 是所有后续操作的第一步,有了数据才能操作嘛,type 的定义很简单,很短的一节数据模型定义就可以明白。所以建议先学会,然后就可以自己定义一下了。至于和现有数据库的合并操作,有需求的可以看。

定义好数据模型后用prisma generate 自动生成操作数据的代码,然后复制 datamodel.prisma 的模型到 schema.graphql 中,去除数据库特定字符和不需要前端得到的字段,如密码和手机号等,然后复制 generated 中你实际需要的 Query、Mutation 和 Subscription 到 schema.graphql 中,当然也可以自定义创建查询 Query 和突变 Mutation。然后就可以去写 resolver 了。

resolver 翻译过来就是解析器的意思,解析什么呢,解析当后端接收到前端发来的请求后如何执行并返回什么东西。比如说前面这个新建文章:

这是前端的操作,可以在playground中写,也可以用ajax、fetch、graphql apollo、甚至wx.request发送过来。

mutation即操作,这个操作就是createDraft,新建草稿,发给后端标题title“GraphQl is great”和用户的id,后面{}中的就是你想后端返回给你的字段。

//playground中 mutation { createDraft( title: "GraphQL is great" userId: "__USER_ID__" ) { id published author { id } } }

这时候我们来看后端干了些什么

//index.js中 const resolvers = { …… Mutation: { createDraft(root, args, context) { return context.prisma.createPost( { title: args.title, author: { connect: { id: args.userId } } }, ) }, …… 这是 resolvers 中的 Mutation 中定义 createDraft 的操作,createDraft 接收了三个参数,这个我们后面细讲,这里我们只有知道,args 包含了你前端传来的所有参数,比如 title 为 args.title,userId 同理。

而 context 就相当于数据库,context.prisma.createPost就类似sequelize.model.Post.create或mongoose.model('Post', schema).create。

而且数据库的包含关系和外键等已经被抽象,这里在 Post 的 author 中,直接 connect 到 author 的唯一字段即 id 就好了。传统数据库的关系操作还是比较繁琐的,Prisma 的 connect 更直观明确。

也不用处理路由,这里定义好 resolvers 后前端就已经可以 createDraft 了,API 接口就一个,比如此处是localhost:4000。前端所有请求 url 都是这里。先在 playground 中试试吧。

在 resolvers 中还可以做更多复杂的处理,你可以集成消息队列、grpc 等各种,甚至也可以用 sequelize 和 mongoose 来返回数据到这里从而实现 Graphql 接口。

比如说此处我们给 Post 添加点赞的功能,在 datamodel 和 schema 中给 Post 加入 liked 字段并设默认值为 0(设默认值参考数据模型定义章节),deploy 一下,然后在 schema 中加入 addPostLike 的 Mutation,再写 resolvers:

// 在resolvers函数中 …… async addPostLike(parent, args, context) { const l = await context.prisma.post({id: args.where.id}) return context.prisma.updatePost({ where:{id: args.postId }, data: { liked: l.liked + 1 }, }) } …… 通过以上例子,你应该大概明白 Prisma 的流程了,然后这里的重点就是 datamodel 和 resolvers,schema 算半个重点吧。

所以后面你先完成 datamodel 和 resolvers 中的Prisma client也就是上述的context.prisma.···的各种方法,最后去深入理解 Prisma和Prisma Graphql API

最后把它部署到服务器则需要了解Prisma 的 CLI 和 Configuration 配置文件以及Prisma Server 部署。

剩下一些更新的操作,登录功能,上传文件,Prisma 中间件等等我会放在最后或是专栏那块。

看完所有文档和专栏后,你就可以去看实际案例,底部有链接,仿 AirBnb 的功能展示 Prisma 所有操作,拿来改一改就能成为自己的后端。

如果在学习和使用中有任何疑问和建议,请加入Prisma 中国社区讨论。也欢迎广大开发者在 github 提交 pr。

ok,下面我们继续下一节。

cisen commented 5 years ago

数据模型定义 datamodel定义概述 datamodel有两个主要功能:

定义底层数据库模式和表(在无模式数据库(如MongoDB)的情况下,它们映射到数据库表或等效结构,如documents)。 它是Prisma API的自动生成的CRUD和实时操作代码的基础。 了解更多。 数据模型使用GraphQL的 schema定义语言SDL(SDL)编写,并存储在一个或多个.graphql后缀的文件中。这些.graphql文件需要在prisma.yml的datamodel属性中引用。例如:

endpoint: YOUR_PRISMA_ENDPOINT datamodel: datamodel.prisma 构建数据模型的块 有几个可用的构建块来塑造你的数据模型。

Types由多个fields字段组成,通常表示应用程序中的实体(例如User,Car,Order)。数据模型中的每种类型都映射到数据库表(或无模式数据库的等效结构),并且将CRUD操作添加到GraphQL schema中。 Relations描述类型之间的relationship关系。 Directives指令涵盖不同的用例,例如类型约束或级联删除行为。 Interfaces是抽象类型,包括一组字段,类型必须包含在implement接口中。接口目前不适用于Prisma中的数据建模,但将来会支持待实现功能。 为什么选择SDL? SDL用于数据建模的主要原因有两个:

SDL是一种 直观,简洁,简洁的表达类型定义的方式,因此有助于提供良好的开发人员体验。 使用GraphQL SDL定义用作GraphQL API基础的模型是一种 惯用的方法。 没有严格的技术要求必须使用SDL,在未来Prisma可能允许其他方法提供模型定义。

要了解有关SDL的更多信息,你可以查看官方GraphQL文档或阅读实际规范。

datamodel例子 一个简单的datamodel.prisma文件例子:

type Tweet { id: ID! @id createdAt: DateTime! @createdAt text: String! owner: User! @relation(link: INLINE) location: Location! } type User { id: ID! @id createdAt: DateTime! @createdAt updatedAt: DateTime! @updatedAt handle: String! @unique name: String tweets: [Tweet!]! } type Location { latitude: Float! longitude: Float! } 此示例说明了使用数据模型时的一些重要概念:

三种类型Tweet,User和Location被映射到数据库表(或无模式数据库的等效结构)。 "User"和"Tweet"之间存在双向关系(通过owner和tweets字段)。@relation(link: INLINE)表示这个关系表现为在Tweet表上的外键。 从Tweet到Location(通过location字段)有一个单向关系。他们的关系通过中间表来实现。 除了User上的name字段外,所有字段在数据模型中都是必填字段(由类型后面的!表示)。 id,createdAt和updatedAt字段由Prisma管理,在暴露的Prisma API中是只读的(意味着它们不能通过Mutation改变)。 @unique指令表示唯一约束,这意味着Prisma会自动确保永远不会有两条数据具有相同的值,比如说id或手机号不会相同从而造成重复注册。 创建和更新数据模型就像编写和保存datamodel文件一样简单。一旦对数据模型感到满意,就可以通过运行prisma deploy来保存文件并将更改应用到Prisma service:

$ prisma deploy

以下是终端输出

Changes: Tweet (Type)

我们看在Prisma项目中,有三个相关的.graphql后缀的文件:

datamodel包含服务API的数据模型定义,通常称为datamodel.prisma。 Prisma GraphQL schema定义服务API(也就是前端能操作和请求)的实际CRUD和实时操作(realtime operations),通常称为prisma.graphql。 应用程序schema定义通过http请求能够执行什么样的操作,不像上面的prisma.graphql包含所有操作,应用程序schema只需要写你能用到的,或去掉危险操作比如删除所有用户这种。所有在这里定义的Query、Mutation和Subscription都要一一对应写resolvers来决定返回的数据。通常为schema.graphql或app.graphql. Prisma数据库的表基于datamodel生成:

该图展示了生成的Prisma GraphQL schema的简化版本,你可以找到完整的schema此处。

GraphQL schema定义了GraphQL API的操作。它实际上是用SDL编写的types集合(SDL还支持接口,枚举,联合类型等原语,你可以学习有关GraphQL类型系统的所有内容这里)。

GraphQL schema有三个特殊的根类型:root types:Query,Mutation和Subscription。这些类型定义API的入口点(类似路由的url):entry points定义API将接受的操作。要了解有关GraphQL schema的更多信息,请查看此文章。

数据模型 数据模型由Prisma service的开发者手动编写。它定义了开发人员想要在其API和数据库中使用的模型和结构。

严格来说,datamodel不是有效的GraphQL schema,因为它不包含任何GraphQL的根类型(Query,Mutation,Subscription),因此不定义任何API操作。

数据模型仅是生成Prisma GraphQL API的实际GraphQL schema的基础。

Prisma GraphQL schema 可以使用Prisma.yml中的graphql-schema generator从Prisma service下载Prisma GraphQL schema:

generate:

使用graphql-config和post-deployment hook生成Prisma GraphQL schema 在实际开发设置中,你希望保持本地Prisma GraphQL schema与服务的API同步,并确保在每次部署Prisma service时更新它。你可以使用post-deployment 来实现。

.graphqlconfig.yml和prisma.yml在设置好的样子:

prisma.yml

datamodel: datamodel.prisma endpoint: http://localhost:4466/myservice/dev secret: mysecret hooks: post-deploy:

这是一个有效的GraphQL schema(意味着它包含Query,Mutation和Subscription根类型),它定义了应用程序层的API。与Prisma的_通用CRUD API相比,应用程序层的API应该公开针对前端端应用程序需求而定制的特定的操作。

应用程序层使用Prisma作为data访问层并将传入的请求委托给服务端的Prisma API,它们实际上是针对数据库解析的。

你通常不会在应用程序schema中重新定义模型定义。相反,你使用graphql-importimport他们:

schema.graphql中如下,在这里#符号不是注释,而是特定导入语法。

import Post from "prisma.graphql"

type Query { feed: [Post!]! } graphql-import目前使用SDL注释来导入类型。 将来,SDL可能会有一个正式的导入语法已经讨论过。

阅读 深入理解Prisma了解有关应用程序schema的更多信息。

文件 你可以将你的数据模型编写在一个.graphql文件中,或者将其分成多个。

包含datamodel的.graphql文件需要在prisma.yml下的属性datamodel中指定。 例如:

datamodel:

datamodel: datamodel.prisma 对象类型type object type(简称 type)定义数据模型中一个model的结构。 它用于表示应用领域中的实体。

每个对象类型都映射到数据库。 对于关系数据库,每种类型创建一个table。 对于无模式数据库,使用等效结构(例如document)。 请注意,即使对于无模式数据库,Prisma也会强制执行模式!

类型具有name和一个或多个fields字段。 类型名称只能包含 字母、数字、字符,需要以大写字母开头。 它们可以包含 最多64个字符。

类型的实例化称为node。 该术语指的是data graph中的节点。

你在数据模型中定义的每种类型都将在生成的Prisma GraphQL schema中作为类似类型提供。

定义对象类型 在datamodel中使用关键字"type"定义对象类型:

type Article { id: ID! @id title: String! text: String isPublished: Boolean! @default(value: "false") } 上面定义的类型具有以下属性:

Name: Article Fields: id, title, text , isPublished (默认为 false) id , title , isPublished 是必填字段 (被后面的 ! 符号定义), text 可选填.

所有ID 都必须是@unique。

生成type的API操作 数据模型中的类型会影响Prisma API中的可用操作。以下是Prisma API中每种类型生成的CRUD和实时操作的概述:

Query允许你获取该类型的一个或多个节点 Mutations允许你创建,更新或删除该类型的节点 Subscriptions可以让你收到有关该类型节点更改的实时通知(即新节点是created或现有节点是updated或deleted) 字段 Fields是[type]的构建块,为节点提供shape。每个字段都由其名称引用,并且是标量或关系字段。

字段名称只能包含 字母数字字符,需要以小写字母开头。它们可以包含 最多64个字符。

通俗来说就是数据库表的列column。

标量字段 String String保存文本。用于用户名的类型,博客文章的内容或能表示为文本的任何其他内容。

在Demo服务器上,字符串值当前限制为256KB。使用cluster配置可以在其他cluster上增加此限制。

这是一个String标量定义的例子:

type User { name: String } 当在操作中用作参数时,必须使用用双引号括起来指定String字段:

query { user(name: "Sarah") { id } } 整数 一个Int是一个整数,不能有小数。 使用此选项可存储值,例如年龄。

Int值的范围是-2147483648到2147483647。

这是一个Int标量定义的例子:

type User { age: Int } 当在操作中用作参数时,写Int字段不能包含任何封闭字符:

query { user(age: 42) { id } } Float Float是一个可以有小数的数字。 使用此选项可存储值,例如商品的价格或复杂计算的结果。

在查询或Mutation中,写Float字段,不带任何封闭字符,小数点为可选:float:42,float:4.2。

以下是"Float"标量定义的示例:

type Item { price: Float } 当在操作中用作参数时,写Float字段,不带任何封闭字符,小数点可选:

query { item(priceLargerThan: 42.2) { id } } Boolean 一个Boolean的值可以是true或FALSE。 在判断场景比较常见,例如用户是否想要接收电子邮件或者用户是否是会员。

以下是Boolean标量定义的示例:

type User { overEighteen: Boolean } 同样不能有封闭字符

query { user(overEighteen: true) { id } } DateTime DateTime类型可用于存储日期和/或时间值。可能是一个人的出生日期或特定事件发生时的时间/数据。

这是一个DateTime标量定义的例子:

type User { birthday: DateTime } 当在操作中用作参数时,必须在ISO 8601格式中指定DateTime字段,并带有双引号:

query { user(birthday: "2015-11-22"){ id } } ISO 8601还接受以下格式:

datetime:"2015" datetime:"2015-11" datetime:"2015-11-22" datetime:"2015-11-22T13:57:31.123Z"。 Enum 像"Boolean"一样,枚举可以有一组预定义的值。 区别在于你可以定义可能的值(而对于Boolean,选项被限制为true和false)。 例如,你可以通过创建具有可能值"COMPACT","WIDE"和"COVER"的枚举来指定文章的格式。

枚举值只能包含 字母数字字符和下划线,并且需要以大写字母开头。 枚举值的名称可用于查询过滤器和Mutation。 它们可以包含 最多191个字符。

以下是枚举定义datamodel的示例:

enum ArticleFormat { COMPACT, WIDE, COVER } type Article { format: ArticleFormat } 当在操作中用作参数时,必须在不包含双引号的情况下指定枚举字段:

query { article(format: COMPACT) { id } } Json 有时你可能需要为松散结构化的数据存储任意JSON值。 Json类型确保它实际上是有效的JSON并将值作为解析的JSON对象/数组而不是字符串返回。

Json值目前限制为256KB。

这是一个Json定义的例子:

type Item { data: Json } 当在操作中用作参数时,必须使用双引号括起来指定的Json字段。 特殊字符必须被转义: json: "{"int": 1, "string": "value"}".

mutation { createItem(data: "{"int": 1, "string": "value"}") { data } } ID "ID"值是基于cuid生成的唯一25个字符的字符串。 具有"ID"值的字段是系统字段并且仅在内部使用,因此无法使用"ID"类型创建新字段。

ID字段只能在一个类型上使用一次,并且必须使用@unique指令进行注释:

类型修饰符 在字段定义中,可以使用类型修饰符注释类型。 GraphQL支持两种类型修改:

必填字段:使用!注释类型,例如 name:String! List:使用一对封闭的[]来注释类型,例如: friends: [User] List 一般在一对多关系中使用List。

可以使用列表字段类型标记标量字段。 具有多重性的关系字段也将被标记为列表。

经常会发现列表定义与此类似:

type Article { tags: [String!]! @scalarList(strategy: RELATION) } 注意两个!类型修饰符,这是它们表达的内容:

第一个!类型修饰符(在String之后)意味着列表中的任何项都不能为"null",例如 tags的这个值无效:["Software",null,"GraphQL"] 第二个!类型修饰符(在结束方括号之后)意味着列表本身永远不能为"null",但它可能是empty。 因此,null不是tags字段的有效值,而[]是。 记住,所有一对多关系,必须有两个!,不然prisma deploy时会报错,因为返回值不能为null,用!后返回值哪怕为空也是[]

必填 字段可以标记为必需(有时也称为"非空")。 创建新节点时,需要为所需的字段提供值,并且没有默认值。

在字段类型后面使用!标记必填字段:

type User { name: String! } 字段限制 可以使用字段约束配置字段以添加进一步的语义并在数据模型中强制执行某些规则。

唯一 设置unique约束可确保所讨论类型的两个节点对于某个字段不能具有相同的值。 唯一的例外是null值,这意味着多个节点可以具有值null而不违反约束。 唯一字段在底层数据库中应用了唯一的索引。

一个典型的例子是User类型的email字段,其中假设每个User应该具有全球唯一的电子邮件地址。

只有String字段中的前191个字符被认为是唯一性,唯一的检查是不区分大小写的。 如果前191个字符相同或者它们仅在大小写上不同,则无法存储两个不同的字符串。

要将字段标记为唯一,只需将@unique指令附加到其定义:

type User { id: ID! @id email: String! @unique name: String! } 对于使用@unique注释的每个字段,你都可以通过为该字段提供值作为查询参数来查询相应的数据。

例如,考虑到上面的数据模型,你现在可以通过其email地址检索特定的User节点:

query { user(where: { email: "kwc@1wire.com" }) { name } } 更多限制 将很快添加更多数据库约束。 如果你希望在Prisma中看到某些约束,请加入此功能请求中的讨论。

默认值 你可以为非列表标量字段设置default值。 当在create-mutation期间没有提供任何值时,该值将应用于新创建的节点。

要指定字段的默认值,可以使用@default指令:

type Story { isPublished: Boolean @default(value: false) someNumber: Int! @default(value: 42) title: String! @default(value: "My New Post") publishDate: DateTime! @default(value: "2018-01-26") status: Status! @default(value: PUBLIC) } enum Status { PRIVATE PUBLIC } 系统字段 三个字段id,createdAt和updatedAt在Prisma中有特殊的语义。它们在你的数据模型中是可选的,但始终在基础数据库中存在。这样,你可以随时将字段添加到数据模型中,并且数据仍可用于现有节点。

这些字段的值当前在Prisma API中是只读的(导入数据时除外),但将来可以配置。有关详细信息,请参阅此提案。

请注意,你不能拥有名为id,createdAt和updatedAt的自定义字段,因为这些字段名称是为系统字段保留的。以下是这三个字段唯一支持的声明:

id:ID! @id createdAt:DateTime! @createdAt updatedAt: DateTime! @updatedAt 所有系统字段都需要标记为必填,并且id字段还需要使用@unique指令进行注释。

系统字段:id 节点在创建时将自动分配一个全局唯一标识符,该标识符存储在id字段中。

每当你将id字段添加到类型定义中以在GraphQL API中公开它时,你必须使用@unique指令对其进行注释。

id具有以下属性:

由25个字母数字字符组成(字母总是小写) 始终以(小写)字母开头,例如c *遵循cuid(collision resistant unique identifiers)方案

请注意,Prisma GraphQL schema中的所有模型类型都将生成Node接口。这就是Node接口的样子:

interface Node { id: ID! @id } id 的类型可以为:

ID Int 比如你要设置id为自增字段,则设置为Int然后去数据库手动添加序列。

系统字段:createdAt和updatedAt 数据模型还提供了两个可以添加到类型中的特殊字段:

createdAt:DateTime! @createdAt:存储此对象类型的节点为created的确切日期和时间。 updatedAt: DateTime! @updatedAt:存储此对象类型的节点last updated的确切日期和时间。 如果希望类型公开这些字段,只需将它们添加到类型定义中,例如:

type User { id: ID! @id createdAt: DateTime! @createdAt updatedAt: DateTime! @updatedAt } 不要忘记加!

@db @db 字段定义底层数据库表名:

type User @db(name: "user") { id: ID! @id name: String! @db(name: "full_name") } 这样子表 User 会实际生成 user 表并且列 name 会变为 full_name.

@scalarList @scalarList(strategy: STRATEGY!) 目前仅在String列表有用.

type Post { tags: [String!]! @scalarList(strategy: RELATION) } 生成字段的API操作 数据模型中的字段会影响可用的查询参数。

更改多个标量字段的值 你可以使用updateManyXXXMutation来更改所有节点的标量字段值,或仅更改特定子集。

mutation {

将没有电子邮件地址的所有用户的电子邮件更新为空字符串

updateManyUsers( where: { email: null } data: { email: "" } ) } 向数据模型添加必填字段 向必须包含节点的模型添加必填字段时,会收到以下错误消息:"You are creating a required field but there are already nodes present that would violate that constraint.你正在创建必填字段,但已存在违反该约束的节点。"

这是因为所有现有节点都会收到该字段的"null"值。这将违反该字段的约束必填字段(或non-nullable)。

以下是添加必填字段所需的步骤:

把它变为可选字段

使用updateManyXXX将所有节点的字段从null迁移到非null值

现在你可以将该字段标记为必填字段并按预期进行部署

关系 relation定义两个类型之间连接的语义。关系中的两种类型通过关系字段连接。当关系不明确时,需要使用@relation指令对关系字段进行注释以消除歧义。

关系在底层数据库有两种处理方式:

关系表:关系之间会有关系表定位两者关系。该方法为默认关系设置。 内联关系:关系通过一个表的外键定位。该方法不能适用多对多关系。因为外键对性能的影响,请谨慎设置。 以下是简单双向关系的示例:

type User { id: ID! @id profile: Profile! @relation(link: INLINE) articles: [Article!]! } type Article { id: ID! @id author: User! } type Profile { id: ID! @id user: User! } 关系也可以将类型与自身连接起来。 然后将其称为self-relation:

type User { id: ID! @id friends: [User!]! } 自我关系也可以是双向的:

type User { id: ID! @id following: [User!]! @relation(name: "Following") followers: [User!]! @relation(name: "Followers") } 请注意,在这种情况下,关系需要使用@relation指令进行注释。

必需的关系 对于单向关系字段,你可以配置它是必填字段还是可选的。 !类型修饰符在GraphQL中充当契约,该字段永远不能为"null"。 因此,用户地址的字段将是"Address"或"Address!"类型。

包含所需单向关系字段的类型的节点只能使用[嵌套Mutation]创建,以确保相应字段不为"null"。

再来看以下关系:

type User { id: ID! @id car: Car! } type Car { id: ID! @id owner: User! color: String! } 如果没有"用户",就永远不能创建"Car",反之亦然,因为这会违反所需的约束。 因此,你需要使用嵌套Mutation同时创建两者:

mutation { createUser(data: { car: { create: { color: "Yellow" } } }) { id car { id } } } 再次提醒,一对多关系字段始终设置为required。例如,包含许多用户地址的字段总是使用类型[Address!]!并且永远不能是[Address!],[Address]!或[Address]。

@relation指令 在定义类型之间的关系时,可以使用@relation指令,该指令提供关于关系的元信息。如果关系不明确,那么你必须使用@relation指令来消除它的歧义。

它可以有两个参数:

name(必需):此关系的标识符,以字符串形式提供。 link: 定义底层数据库如何处理关系表(默认为中间表TABLE):

INLINE: 关系在数据库表中为外键. TABLE: 关系在数据库表中为中间表. onDelete:指定删除行为deletion behaviour并启用级联删除cascading deletes。在具有相关节点的节点被删除的情况下,删除行为确定相关节点应该发生什么。此参数的输入值定义为具有以下可能值的枚举: - SET_NULL(默认值):将相关节点设置为"null"。 - CASCADE:删除相关节点。请注意,无法将双向关系的两端都设置为"CASCADE"。 以下是使用@relation指令的数据模型示例:

type User { id: ID! @id stories: [Story!]! @relation(name: "StoriesByUser" onDelete: CASCADE) } type Story { id: ID! @id text: String! author: User @relation(name: "StoriesByUser") } 该关系被命名为"StoriesByUser",删除行为如下:

删除"User"节点时,其所有相关的"Story"节点也将被删除。 当一个Story节点被删除时,它将被简单地从相关的User节点上的stories列表中删除。 省略@relation指令 在最简单的情况下,两个类型之间的关系是明确的并且应该应用默认删除行为(SET_NULL),相应的关系字段不必用@relation指令注释。

这里我们定义了User和Story类型之间的双向one-to-many关系。 由于尚未提供onDelete,因此使用默认删除行为:SET_NULL:

type User { id: ID! @id stories: [Story!]! } type Story { id: ID! @id text: String! author: User } 此示例中的删除行为如下:

当User节点被删除时,其所有相关Story节点上的author字段将被设置为null。 请注意,如果author字段被标记为必填,则操作将导致错误。 当一个Story节点被删除时,它将被简单地从相关的User节点上的stories列表中删除。 使用@relation指令的name参数 在某些情况下,你的数据模型可能包含不明确的关系。 例如,考虑到你不仅想要一个关系来表达"用户"和"故事"之间的"作者关系",而且你还想要一个关系来表达"故事"节点已经被"用户"点赞。

在这种情况下,你最终会得到"用户"和"故事"之间的两种不同关系! 为了消除它们的歧义,你需要为该关系指定一个名称:

type User { id: ID! @id writtenStories: [Story!]! @relation(name: "WrittenStories") likedStories: [Story!]! @relation(name: "LikedStories") } type Story { id: ID! @id text: String! author: User! @relation(name: "WrittenStories") likedBy: [User!]! @relation(name: "LikedStories") } 如果在这种情况下没有提供name,则无法确定writtenStories是否应该与author或likesBy字段相关。

使用@relation指令的onDelete参数 如上所述,你可以为相关节点指定专用删除行为。 这就是@relation指令的onDelete参数。

我们看下面的示例:

type User { id: ID! @id comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE) blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE) } type Blog { id: ID! @id comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE) owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL) } type Comment { id: ID! @id blog: Blog! @relation(name: "Comments", onDelete: SET_NULL) author: User @relation(name: "CommentAuthor", onDelete: SET_NULL) } 让我们研究三种类型的删除行为:

当User节点被删除时, - 将删除所有相关的Comment节点。 - 相关的Blog节点将被删除。 当Blog节点被删除时 - 将删除所有相关的Comment节点。 - 相关的User节点将其blog字段设置为null。 删除"Comment"节点时 - 相关的Blog节点继续存在,删除的Comment节点从其comments列表中删除。 - 相关的User节点继续存在,删除的Comment节点从其comments列表中删除。 生成关系的API操作 schema中包含的关系会影响Prisma API中的可用操作。以下是Prisma API中每个关系生成的CRUD和实时操作的概述:

[关系Query]允许你跨类型查询数据或为关系聚合(请注意,这也可以使用Relay的[连接模型]。 [嵌套Mutation]允许你在同一Mutation中创建,连接,更新,升级和删除多个相关节点。 [关系Subscriptions]允许你获得关系更改的通知。 SDL指令 指令用于在你的数据模型中提供其他信息。它们看起来像这样:@name(argument:"value")或者当没有参数时只有@name。

Datamodel指令 Datamodel指令描述了有关GraphQL schema中的类型或字段的其他信息。

唯一标量字段 @unique指令将标量字段标记为唯一。唯一字段将在底层数据库中应用唯一的index。

User 有唯一的 email

type User { email: String @unique } 查找有关@unique指令的更多信息。

关系字段 指令@relation(name:String,onDelete:ON_DELETE!= SET_NULL)可以附加到关系字段。

标量字段的默认值 指令@default(value:String!)为标量字段设置默认值。 请注意,value参数对于所有标量字段都是String类型(即使字段本身不是字符串):

title, published,someNumber 默认值为 New Post, false , 42

type Post { title: String! @default(value: "New Post") published: Boolean! @default(value: "false") someNumber: Int! @default(value: "42") } 临时指令 临时指令用于执行一次性迁移操作。 在部署包含临时指令的服务之后,需要从类型定义文件中手动删除临时指令。

重命名类型或字段 临时指令@rename(oldName:String!)用于重命名类型或字段。

重命名 PostStory, 并且它的 text 字段改为 content

type Story @rename(oldName: "Post") { content: String @rename(oldName: "text") } 如果未使用重命名指令,Prisma会在创建新类型和字段之前删除旧类型和字段,从而导致数据丢失!

命名约定 你在Prisma service中遇到的不同对象(如类型或关系)遵循不同的命名约定,以帮助你区分它们。

类型 类型名称确定派生查询和Mutation的名称以及嵌套Mutation的参数名称。

以下是命名类型的约定:

在 单个数据时选择类型名称:

Yes: type User { ... } No: type Users { ... } 请严格遵守,因为当创建时若为type Users,那么自动生成的代码里查询多个用户会成为Userss

标量和关系字段 标量字段的名称用于查询和Mutation的查询参数中。关系字段的名称遵循相同的约定,并确定关系Mutation的参数名称。关联字段名称只能包含 字母数字字符,并且需要以大写字母开头。它们可以包含 最多64个字符。

字段名称每种类型都是唯一的。

以下是命名字段的约定:

为列表字段选择复数名称:

Yes: friends: [User!]! No: friendList: [User!]! 为非列表字段选择单数名称:

Yes: post: Post! No: posts: Post! 更多SDL功能 在本节中,我们将介绍使用Prisma进行数据建模尚不支持的SDL功能。

接口 "与许多类型系统一样,GraphQL支持接口。接口是一种抽象类型,包含一组必须包含的字段才能实现接口。"来自官方GraphQL文档

要了解有关接口何时以及如何进入Prisma的更多信息,请查看此功能请求。

联合类型 "联合类型与接口非常相似,但它们不能指定类型之间的任何公共字段。"来自官方GraphQL文档

要了解有关联合类型何时以及如何进入Prisma的更多信息,请查看此功能请求。

MongoDB 的 datamodel 不同于MySQL和PostgreSQL等关系型数据库,MongoDB作为NoSQL在datamodel的定义上与上述有所区别,下面是独特字段的举例:

datamodel例子 一个简单的datamodel.prisma文件例子:

type Tweet { id: ID! @id createdAt: DateTime! @createdAt text: String! owner: User! location: Location! } type User { id: ID! @id createdAt: DateTime! @createdAt updatedAt: DateTime! @updatedAt handle: String! @unique name: String tweets: [Tweet!]! @relation(link: INLINE) } type Location @embedded { latitude: Float! longitude: Float! } 此示例基于[datamodel v1.1]格式,该格式当前位于[预览]中。

此示例说明了使用数据模型时的一些重要概念:

两种类型Tweet和User分别映射到MongoDB集合。 User和Tweet之间存在双向关系(通过owner和tweets字段)。请注意,在底层数据库中,只有User文档存储对Tweet文档的引用(而不是相反),因为User上的tweets字段指定@relationdirective上的link。但是,Prisma API仍然允许您直接查询Tweet的owner。 从Tweet到Location(通过location字段)有一个单向关系。 Location是一个embedded type,这意味着没有Location文档存储在他们自己的Location集合中 - 相反,每个Location都存储在Tweet集合中的Tweet文档中。 除了User上的name字段外,所有字段在数据模型中都是required(由类型后面的!表示)。 使用@id,@reatedAt和@updatedAt-指令注释的字段由Prisma管理,并且在公开的Prisma API中使用read-only,这意味着它们不能通过突变进行更改(除非[使用NDF格式导入数据])。 @unique指令表示唯一约束,这意味着Prisma确保永远不会有两个节点具有相同的注释字段值。 创建和更新数据模型就像编写和保存datamodel文件一样简单。一旦对数据模型感到满意,就可以通过运行prisma deploy来保存文件并将更改应用到Prisma服务:

这些字段的值当前是只读的(除了[在Prisma API中使用NDF格式导入数据]),但将来可以配置。有关详细信息,请参阅此提案。

系统字段 MongoDB的三个系统字段不同:

type User { id: ID! @id created_at: DateTime! @createdAt updated_at: DateTime! @updatedAt } 关系 关系定义了两种类型之间连接的语义。关系中的两种类型通过关系字段连接。当关系不明确时,需要使用@relation指令对关系字段进行注释以消除歧义。

MongoDB中的关系 文档和关系数据库之间最大的区别之一是如何处理数据类型之间的关系。

虽然关系数据库使用数据库规范化来存储通过密钥相互引用的平面数据记录,但文档数据库能够存储物理上位于同一集合内的相关对象的对象。后者称为嵌入数据(即,集合中的文档可以具有嵌入在同一集合内的子文档/阵列)。

使用MongoDB,可以通过直接在父文档中嵌入数据或使用引用来表达关系。可以在MongoDB文档中找到有关差异的良好概述。在为底层MongoDB建模数据时,Prisma采用嵌入式类型的思想。

嵌入式类型 MongoDB连接器引入了嵌入式类型的概念。嵌入式......

...始终使用@embedded指令进行注释。 ...总是(至少)有一种父类型。 ...始终直接存储在底层Mongo数据库的父类型集合中(即嵌入类型永远不会有自己的集合)。 ...不能有唯一的字段(即用@unique指令注释的字段)。 ...不能与其父类型具有(反向)关系(但它可以与其他非嵌入类型有关系)。 ...不能使用Prisma API直接查询,而只能通过父类型的嵌套操作查询。 ...不能使用Prisma API直接创建,更新或删除,而只能通过父类型的嵌套操作创建,更新或删除。 以下是数据模型的示例,其中Coordinates被定义为嵌入类型:

type City { id: ID! @id name: String! coordinates: Coordinates } type Coordinates @embedded { latitude: Float! longitude: Float! } 以下是基于此数据模型存储在基础MongoDB数据库中的数据示例:

使用此设置,无法直接查询任何坐标实例,因为坐标是嵌入类型。 只能通过城市类型查询坐标。 同样,您无法直接创建,更新或删除坐标,而是需要创建,更新或删除City才能在Coordinates实例上执行此类操作。

拓展解释

连接关系 使用MongoDB,您可以通过两种方式建立关系模型:

使用@embedded,如上所述 使用references,在Prisma术语中称为links 与MongoDB连接器的link relation的工作方式如下:

关系的一边(A)存储另一边的文档ID(B),这称为inlined link 该关系的另一方(B)对初始方面的文件完全没有引用(A) 关系的每一面都由底层MongoDB中自己的集合表示,即链接关系总是跨越多个集合。 您可以使用@relation指令的link参数来表示应该存储ID的关系的一面。 在下面的示例中,User类型存储与其相关的所有Post文档的ID值。 然而,Post文档不会在底层Mongo数据库中存储有关其author的任何信息:

type User { id: ID! @id name: String! posts: [Post!]! @relation(link: INLINE) } type Post { id: ID! @id title: String! author: User! } 以下是基于此数据模型存储在基础MongoDB数据库中的数据示例:

虽然这种方法可以直接在Prisma API中查询Post文档(而不是只能通过嵌套操作通过其父类型查询的嵌入类型),但在以这种方式建模关系时存在性能方面的考虑。

通过author字段从Post到User的操作有性能问题。 这是因为底层的Post对象不知道他们的作者是谁,而Prisma需要过滤所有User以找到Post的author是谁。

自引用关系 关系也可以将类型与自身连接起来。 然后将其称为self-relation。

请注意,自我关系还需要在@relation指令中包含link参数:

type User { id: ID! @id friends: [User!]! @relation(link: INLINE) } 自我关系也可以是双向的:

type User { id: ID! @id following: [User!]! @relation(name: "FollowRelation", link: INLINE) followers: [User!]! @relation(name: "FollowRelation") } 请注意,在这种情况下,需要使用@relation指令注释关系,并且必须提供name参数。

必需关系 对于to-one relation字段,您可以配置它是required还是optional。 !类型修饰符在GraphQL中充当契约,该字段永远不能为null。因此,用户地址的字段将是Address或Address!类型。

包含所需to-one relation字段的类型的节点只能使用[嵌套mutation]创建,以确保相应字段不为null。

再考虑以下关系:

type User { id: ID! @id car: Car! @relation(link: INLINE) } type Car { id: ID! @id owner: User! color: String! } 如果没有User,就永远不会创建Car,反之亦然,因为这会违反所需的约束。因此,您需要使用声明性嵌套写入同时创建两者:

const newUser = await prisma .createUser({ car: { create: { color: "Yellow" } } }) graphql:

mutation { createUser(data: { car: { create: { color: "Yellow" } } }) { id car { id } } } 请注意,to-many relation字段始终设置为required。例如,包含许多用户地址的字段总是使用类型[Address!]!,并且永远不能是[Address!],[Address]!或[Address]。

@relation指令 在定义类型之间的关系时,可以使用@relation指令,该指令提供关于关系的元信息。有时,可能需要@relation指令,例如如果一个关系是模糊的,你必须使用@relation指令来消除它的歧义。或者,如果要定义[link relation],则必须使用@relation指令对关系的一侧进行注释,以指定link参数。

它可能需要两个参数:

name(必需):此关系的标识符,以字符串形式提供。 link(作为[link relation]是需要的):指定关系的哪一侧应该存储对另一方的引用。 onDelete:指定删除行为并启用级联删除。在具有相关节点的节点被删除的情况下,删除行为确定相关节点应该发生什么。此参数的输入值定义为具有以下可能值的枚举: - - SET_NULL(默认值):将相关节点设置为null。 - - CASCADE:删除相关节点。请注意,无法将双向关系的结尾设置为CASCADE。 以下是使用@relation指令的数据模型示例:

type User { id: ID! @id stories: [Story!]! @relation(name: "StoriesByUser", link: INLINE, onDelete: CASCADE) } type Story { id: ID! @id text: String! author: User @relation(name: "StoriesByUser") } 该关系被命名为StoriesByUser,删除行为如下:

当User节点被删除时,其所有相关的Story节点也将被删除。 当一个Story节点被删除时,它将被简单地从相关的User节点上的stories列表中删除。 使用@relation指令的name参数 在某些情况下,您的数据模型可能包含不明确的关系。例如,考虑到你不仅想要一个关系来表达User和Story之间的StoriesByUserrelation,而且你还想要一个关系来表达User已经liked的Story节点。

在这种情况下,你最终会得到User和Story之间的两种不同关系!为了消除它们的歧义,您需要为该关系指定一个名称:

type User { id: ID! @id writtenStories: [Story!]! @relation(name: "WrittenStories", link: INLINE) likedStories: [Story!]! @relation(name: "LikedStories", link: INLINE) } type Story { id: ID! @id text: String! author: User! @relation(name: "WrittenStories") likedBy: [User!]! @relation(name: "LikedStories") } 如果在这种情况下没有提供name,则无法确定writtenStories是否应该与author或likesBy字段相关。

使用@relation指令的onDelete参数 如上所述,您可以为相关节点指定专用删除行为。这就是@relation指令的onDelete参数。

请考虑以下示例:

type User { id: ID! @id comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE, link: INLINE) blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE, link: INLINE } type Blog { id: ID! @id comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE, link: INLINE) owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL) } type Comment { id: ID! @id blog: Blog! @relation(name: "Comments", onDelete: SET_NULL) author: User @relation(name: "CommentAuthor", onDelete: SET_NULL) } 这三种类型的删除行为:

当User节点被删除时 - 将删除所有相关的Comment节点。 - 相关的Blog节点将被删除。 当Blog节点被删除时 - 将删除所有相关的Comment节点。 - 相关的User节点将其blog字段设置为null。 删除Comment节点时 - 相关的Blog节点继续存在,删除的Comment节点从其comments列表中删除。 - 相关的User节点继续存在,删除的Comment节点从其comments列表中删除。 生成关系的API操作 架构中包含的关系会影响[Prisma API]中的可用操作。以下是Prisma API中每个关系生成的CRUD和实时操作的概述:

[Relation queries]允许您跨类型查询数据或为关系聚合(请注意,这也可以使用Relay relay /)的连接模型)。 [Nested mutations]允许您在同一突变中创建,连接,更新,升级和删除多个相关节点。 [Relation subscriptions]允许您获得关系更改的通知。 SDL指令 指令用于在您的数据模型中提供其他信息。它们看起来像这样:@name(argument: "value")或者只有@name,当没有参数时。

独特的标量字段 @unique指令将标量字段标记为[唯一]。唯一字段将在底层数据库中应用唯一的索引。

type User { email: String @unique } 关系字段 指令@relation(name: String, onDelete: ON_DELETE! = SET_NULL)可以附加到关系字段。

[见上文]了解更多信息。

标量字段的默认值 指令@default(value:!)为标量字段设置[默认值]:

type Post { title: String! @default(value: "New Post") published: Boolean! @default(value: false) someNumber: Int! @default(value: 42) } 命名约定 您在Prisma服务中遇到的不同对象(如类型或关系)遵循不同的命名约定,以帮助您区分它们。

类型 类型名称确定派生查询和突变的名称以及嵌套突变的参数名称。

以下是命名类型的约定:

在单数中选择类型名称: - 正确:type User { ... } - 错误:type Users { ... } 标量和关系字段 标量字段的名称用于查询和突变的查询参数中。关系字段的名称遵循相同的约定,并确定关系突变的参数名称。关联字段名称只能包含字母数字字符,并且需要以大写字母开头。它们可以包含最多64个字符。字段名称每种类型都是唯一的。

以下是命名字段的约定:

为列表字段选择复数名称: - 正确:friends: [User!]! - 错误:friendList: [User!]! 为非列表字段选择单数名称: - 正确:post: Post! - 错误:posts: Post! 更多SDL功能 在本节中,我们将介绍使用Prisma进行数据建模尚不支持的SDL功能。

Interfaces "与许多类型系统一样,GraphQL支持接口。接口是一种抽象类型,包含一组必须包含的字段才能实现接口。"来自官方GraphQL文档

要了解有关接口何时以及如何进入Prisma的更多信息,请查看此功能请求。

Union types "联合类型与接口非常相似,但它们不能指定类型之间的任何公共字段。"来自官方GraphQL文档

要了解有关联合类型何时以及如何进入Prisma的更多信息,请查看此功能请求。

数据库迁移概述(SQL) 如果将[Prisma service]配置为允许数据库迁移,则可以使用SDL定义和迁移数据库。

Prisma使用 临时指令来执行一次性迁移操作。 在部署包含临时指令的服务之后,需要从类型定义文件中手动删除临时指令。

重命名 临时指令@rename(oldName:String!)用于重命名类型和字段。

如果未使用@rename指令,Prisma会在创建新类型和字段之前删除旧类型和字段,从而导致数据丢失!

重命名类型 将Post类型重命名为Story

type Story @rename(oldName: "Post") { content: String } 在执行prisma deploy并重命名后,需要手动删除@rename(oldName:"Post")指令。

重命名字段 将text字段重命名为content

type Story { content: String @rename(oldName: "text") } 在执行prisma deploy并重命名后,需要手动删除@rename(oldName:"text")指令。

数据库迁移概述(NoSQL) MongoDB是一个schemaless数据库,这意味着possible将各种结构的数据插入其中。 MongoDB从不因为一段插入的数据不符合某种预定义的预期格式而报错。这不同于关系数据库,其中插入的数据需要遵守预定义的数据库模式。

将MongoDB与Prisma一起使用时会发生这种情况。 Prisma在MongoDB之上添加了一个“模式”(即Prisma [datamodel])。这就是为什么migrations现在成为将MongoDB与Prisma一起使用时的相关主题。

使用Prisma执行迁移的一般过程如下:

1.调整[datamodel]文件以反映新的所需模式 1.运行[prisma deploy]以应用更改并执行Prisma API(以及可能的)基础MongoDB数据库的迁移

在完成该过程时,Prisma将只对基础MongoDB数据库进行添加式结构更改。

在迁移期间,Prisma永远不会:

删除现有集合 重命名现有集合 删除现有数据库 删除或更改现有文档上的任何字段名称 请注意,这可能会导致冗余表,即仍存在于基础MongoDB数据库中但无法通过Prisma API访问的集合或字段。

在迁移期间,Prisma可能:

创建新的集合 创建新文档 在现有文档上创建新字段 例子 重命名集合 场景1:使用@db指令

假设您有以下数据模型:

type User @db(name: "users") { id: ID! @id name: String! } 因为在类型上使用了@db指令,所以Prisma API中的类型名称是从底层MongoDB中的集合名称解耦的。

要重命名Prisma API中的类型,您需要在Prisma数据模型中调整类型的名称:

type Person @db(name: "users") { id: ID! @id name: String! } 要应用更改并更新Prisma API,请运行prisma deploy。

场景2:没有@db指令

假设您有以下数据模型:

type User { id: ID! @id name: String! } 因为没有@db指令,所以底层MongoDB中的集合也称为User。

要重命名Prisma API中的类型,您需要在Prisma数据模型中调整类型的名称:

type Person { id: ID! @id name: String! } 运行prisma deploy时,Prisma完成了三件事:

从Prisma API中删除User类型的CRUD操作 在Prisma API中为新的Person类型公开CRUD操作 在底层数据库中创建一个新的Person集合 底层的User集合保持不变,它作为冗余数据保留在那里。

如果你仍想在保留的User集合中使用Prisma API中新的Person类型的数据,你需要手动重命名集合 可能会删除那个空的Person集合)。

重命名集合的字段 假设您有以下数据模型:

type User { id: ID! @id name: String! } 现在要将name字段重命名为lastName。这实际上意味着三件事:

1.您想要在Prisma API中仅重命名字段 1.您想要在基础MongoDB中重命名字段 1.您想要重命名Prisma API 和底层MongoDB中的字段

场景1:仅在Prisma API中重命名

如果只想在Prisma API中重命名该字段,可以通过更改数据模型中字段的名称并添加@db指令来创建从lastName datamodel字段到name的映射来实现。底层MongoDB中的字段:

type User { id: ID! @id lastName: String! @db(name: "name") } 现在运行prisma deploy来应用更改。

场景2:仅在底层MongoDB中重命名

因为Prisma从不对您的底层MongoDB执行任何结构更改,所以实现此目的的唯一方法是手动重命名字段。

完成此操作后,您的Prisma API当然没有改变。这也意味着当你现在试图通过Prisma API检索User集合中文档的name时,Prisma将抛出一个错误,因为底层的MongoDB中不再存在name。

为防止这种情况,您有两种选择:

从数据模型中删除name字段并运行prisma deploy。这意味着无法通过Prisma访问新的lastName。 同样重命名Prisma API中的字段,请参阅下面。 场景3:在Prisma API和底层MongoDB中重命名

与第二种情况类似,您首先需要在底层MonngoDB中手动重命名字段。

完成此操作后,您可以在Prisma API中重命名该字段。调整datamodel如下所示:

type User { id: ID! @id lastName: String! } 最后,运行prisma deploy。

请注意,Prisma CLI可能要求您运行prisma deploy --force来执行此操作。这是[已在此处报告]的CLI中的错误(https://github.com/prisma/prisma/issues/3871)。

删除集合 假设您有以下数据模型:

type User { id: ID! @id name: String! } type Post { id: ID! title: String! } 现在,您想要从Prisma API中删除Post类型,并在底层MongoDB中删除Post集合。 首先需要从数据模型中删除Post:

type User { id: ID! @id name: String! } 现在运行prisma deploy来应用更改。这不会改变底层MongoDB中的任何内容,Post集合仍然作为冗余数据存在。

要摆脱保留集合,您需要从MongoDB数据库中手动删除集合。

下一节是Prisma的神奇操作,如何从数据库拿到数据。

cisen commented 5 years ago

Prisma client

Prisma client是一个自动生成的库,可连接到你的Prisma service。在this文章中了解更多信息。

设置 使用[Prisma CLI]的prisma generate命令自动生成Prisma client。 一个Prisma client连接到一个Prisma service。

prisma generate读取prisma.yml中generate属性下指定的信息。

在prisma.yml中配置generate generate根属性接受一个对象列表。 每个对象都有两个字段:

generator: #生成Prisma client的编程语言。 - 可选的值:typescript-client, javascript-client, flow-client, go-client. - 即将推出:reason output:应存储Prisma client的文件的路径和名称。 例如,我们看下面的prisma.yml:

datamodel: datamodel.prisma endpoint: http://localhost:4000 generate:

generator也接受graphql-schema作为值。 这将通过introspection查询下载Prisma API的GraphQL schema。

更多例子 当generate接受一个对象列表时,你也可以同时生成多个文件:

datamodel: datamodel.prisma endpoint: http://localhost:4466 secret: mysecret42 generate:

type Link { id: ID! @id createdAt: DateTime! @createdAt description: String! url: String! postedBy: User votes: [Vote!]! } type User { id: ID! @id name: String! email: String! @unique password: String! links: [Link!]! votes: [Vote!]! } type Vote { id: ID! @id link: Link! user: User! } 你可以在你电脑本地或者此处查看生成的文件的代码:

适用于JavaScript的Prisma client TypeScript的Prisma client Prisma API的GraphQL schema 生成的Prisma client已经知道在生成时prisma.yml中指定的endpoint和secret。

构造函数-JavaScript Prisma构造函数用于创建Prisma client的新实例。

constructor(options: BasePrismaOptions) BasePrismaOptions具有以下属性(都是可选的):

endpoint: string:Prisma service的端点。 如果未提供,则client将默认使用在生成client时在prisma.yml中指定的"端点"。 secret: string:保护Prisma serviceAPI的密码。 如果未提供,client将默认使用在生成client时在prisma.yml中指定的secret。 debug: boolean:如果设置为true,Prisma client上每个方法的调用都会打印发送到Prisma API的GraphQL查询到控制台。 默认值:false。 fragmentReplacements: FragmentReplacement []:将应用于发送到Prisma API的每个Query/Mutation/Subscriptions的GraphQL片段列表。 例子 使用client generate时在prisma.yml中指定的endpoint和secret的默认值:

const prisma = new Prisma({}) 覆盖默认值:

const prisma = new Prisma({ endpoint: "http://localhost:4466/hello-world/dev", secret: "mysecret42", }) 构造函数-TypeScript Prisma构造函数用于创建Prisma client的新实例。

constructor(options: BasePrismaOptions) BasePrismaOptions具有以下属性(都是可选的):

endpoint:string:Prisma service的端点。 如果未提供,则client将默认使用在生成client时在prisma.yml中指定的"端点"。 secret:string:保护Prisma serviceAPI的密码。 如果未提供,client将默认使用在生成client时在prisma.yml中指定的secret。 debug:boolean:如果设置为true,Prisma client上每个方法的调用都会打印发送到Prisma API的GraphQL查询到控制台。 默认值:false。 fragmentReplacements:FragmentReplacement []:将应用于发送到Prisma API的每个Query/Mutation/Subscriptions的GraphQL片段列表。 例子 使用client生成时在prisma.yml中指定的endpoint和secret的默认值:

const prisma = new Prisma({}) 覆盖默认值:

const prisma = new Prisma({ endpoint: "http://localhost:4466/hello-world/dev", secret: "mysecret42", }) 构造函数-Go Prisma构造函数用于创建Prisma client的新实例。

New(options PrismaOptions) Client PrismaOptions具有以下属性(都是可选的):

endpoint:string:Prisma service的端点。 如果未提供,则client将默认使用在生成client时在prisma.yml中指定的"端点"。 debug:boolean:如果设置为true,Prisma client上每个方法的调用都会打印发送到Prisma API的GraphQL查询到控制台。 默认值:false。 例子 使用client生成时在prisma.yml中指定的endpoint和secret的默认值:

db := prisma.New(nil) 覆盖默认值:

client := prisma.New(&prisma.PrismaOptions{ Endpoint: "http://localhost:4466/hello-world/dev", Debug: true, }) 读取数据-JavaScript Prisma client基于Prisma API的GraphQL schema定义生成操作。对于reading数据,它基本上复制你Prisma service中的GraphQL Query。

对于此页面,我们假设你的Prisma API基于以下datamodel:

type Link { id: ID! @id createdAt: DateTime! @createdAt description: String! url: String! postedBy: User votes: [Vote!]! } type User { id: ID! @id name: String! email: String! @unique password: String! links: [Link!]! votes: [Vote!]! } type Vote { id: ID! @id link: Link! user: User! } 你可以查看API生成的GraphQL schema这里 你可以查看生成的JavaScript client此处。 每当使用Prisma client查询模型时,都会获取该模型的所有字段。无论是查询单个对象还是对象列表,都是如此。

例如,以下查询返回单个"User"的所有标量字段:

const user = await prisma.user({ email: "bob@prisma.io" }) 在这种情况下,返回的user对象将具有四个属性(对应于User模型的标量字段):id,name,email和password。

links和votes字段都是relation fields,因此不包含在查询中。

以下是获取所有User对象列表的示例:

const users = await prisma.users() 与前面的查询类似,users数组中的每个对象只有标量和无关系字段。

获取单个对象 对于datamodel中的每个模型类型,在Prisma clientAPI中生成一种方法,允许获取该模型的单个对象。 该方法以类型命名,但以小写字符开头。 对于上面的示例数据模型,获取单个"User","Link"和"Vote"对象的三种方法称为"user","link"和"vote"。

这些函数的输入是一个对象,它具有模型的任何 唯一字段作为属性。 这意味着,对于所有三种方法,都接受id字段(因为相应的模型每个都有一个注释为@unique的id字段)。 user方法的输入对象另外有一个email字段可以来查询。

查看Link的"where"类型here 查看User的"where"类型here 查看Vote的"where"类型here 例子 以下所有例子中,第一段代码为resolvers中的代码,第二段是playground中的操作

通过其id获取单个投票:

const vote = await prisma.vote({ id: "cjlgpyueg001o0a239d3i07ao" })

generated query

query { vote(where: { id: "cjlgpyueg001o0a239d3i07ao" }) { id } } 通过email获取单个用户:

const user = await prisma.user({ email: "alice@prisma.io" })

generated query

query { user(where: { email: "alice@prisma.io" }) { id } } 获取列表 对于datamodel中的每个模型类型,在Prisma clientAPI中生成一个方法,允许获取这些模型对象的列表。 该方法以模型命名,但以小写字符开头并使用复数形式。 对于上面的示例数据模型,获取"User","Link"和"Vote"模型列表的三种方法称为"users","links"和"votes"。

这些函数的输入参数是一个具有以下属性的对象:

过滤:where 排序:orderBy 分页:before,after,first,last,skip 例子 获取所有links:

const links = await prisma.links()

generated query

query { links { id createdAt url description } } 获取users列表:

const users = await prisma.users()

query { links { id createdAt url description } } 关系 Prisma client基于fluent API来查询数据图中的关系。这意味着你可以简单地链式调用你的方法来返回模型的关系属性。

注意

这仅在检索单个对象时才有可能,而不是用于列表。这意味着你无法查询列表中返回的对象的关系字段,例如:

//不可能 const result = await prisma .users() .links() 在这个例子中,users()已经返回一个列表,因此不可能查询列表中每个用户对象的links关系。

例子 Query查询单个user的所有链接:

const linksByUser = await prisma .user({ email: "alice@prisma.io" }) .links()

query { user(where: { email: "alice@prisma.io" }) { links { id createdAt description url } } } 查询某个user所做的所有投票:

const votesByUser = await prisma .user({ email: "alice@prisma.io" }) .votes()

query { user(where: { email: "alice@prisma.io" }) { votes { id } } } 使用片段fragment进行细粒度数据访问 你可以使用$fragment API功能(基于GraphQL)指定要检索的字段,而不是查询模型的所有标量字段(这是默认行为)。这对于排除大量不需要的字段(如BLOB值或大字符串)或检索关系很有用。

例子 获取用户的name和email以及相关links的description和url:

const fragment = fragment UserWithLinks on User { name email links { description url } } const userWithPosts = await prisma.users().$fragment(fragment)

query { users { ...UserWithLinks } } fragment UserWithLinks on User { name email links { description url } } 列表的基本过滤器 Basic过滤器允许你指定某些条件来约束应在列表中返回哪些对象。过滤器在输入参数的where对象中指定,任何列表查询都接受该对象。

where对象的类型取决于生成它的模型。

查看Link,User和Vote 的where类型

查看Link的"where"类型here 查看User的"where"类型here 查看Vote的"where"类型here 也可以使用AND和OR字段组合多个过滤器。

例子 找到名称中包含"A"的用户:

const usersWithAInName = await prisma .users({ where: { name_contains: "A" } })

query { users(where: { name_contains: "A" }) { id name email password } } 使用自动完成功能来探索过滤系统

查询称为"Alice"或"Bob`的用户:

const usersCalledAliceOrBob = await prisma .users({ where: { name_in: ["Alice", "Bob"] } })

query { users(where: { name_in: ["Alice", "Bob"] }) { id name email password } } 获取2018年12月24日之前创建的链接:

const linksBeforeChristmas = await prisma .links({ where: { createdAt_lt: "2018-12-24" } })

query { links(where: { createdAt_lt: "2018-12-24" }) { id createdAt description url } } Prisma client API中的日期和时间遵循ISO 8601标准,该标准通常具有以下形式:YYYY-MM-DDThh:mm:ss。

查询在description中有prisma 或graphql的链接并且是在2018年创建的:

const filteredLinks = await prisma .links({ where: { AND: [ { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] }, { AND: [{ createdAt_gt: "2017" }, { createdAt_lt: "2019" }] } ], } })

query { links(where: { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] AND: [{ createdAt_lt: "2019" }, { createdAt_gt: "2017" }] }) { id description } } 实际上可以省略AND过滤器,因为默认情况下使用logical and组合了多个过滤条件。这意味着上面的过滤器也可以表示如下:

const filteredLinks = await prisma .links({ where: { OR: [{ description_contains: "graphql" }, { description_contains: "prisma" }], createdAt_gt: "2017", createdAt_lt: "2019" } })

query { links(where: { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }], createdAt_lt: "2019", createdAt_gt: "2017" }) { id createdAt description url } } 列表的关系过滤器 关系过滤器可用于约束关系列表字段上的返回对象。用于过滤的类型与基本过滤器相同,唯一的区别是过滤器不应用于方法调用的第一级,而是在第二级查询关系时。

例子

const linksByUserBeforeChristmas = await prisma .user({ email: "alice@prisma.io" }) .links({ where: { createdAt_lt: "2017-12-24" } })

query { user(where: { email: "alice@prisma.io" }) { links(where: { createdAt_lt: "2017-12-24" }) { id createdAt description url } } } 排序 查询模型对象列表时,可以按该模型类型的任何标量字段对列表进行排序。因此,每个生成的查询模型列表的方法都接受其输入对象上的orderBy字段。

orderBy字段的类型取决于生成它的模型的标量字段。

查看link,user和Vote的orderBy类型

查看link 的orderBy类型here 查看user 的orderBy类型here 查看Vote 的orderBy类型here 例子 _按创建日期(升序)_links:

const sortedLinks = prisma .links({ orderBy: "createdAt_ASC" })

query { links(orderBy: createdAt_ASC) { id createdAt description url } } 按字母顺序排序用户名(降序):

const sortedUsers = prisma .users({ orderBy: "name_DESC" })

query { users(orderBy: name_DESC) { id name email password } } 分页 查询模型对象列表时,可以通过提供分页参数来获取该列表的部分信息。常在feed流中使用。

用'first和last`向前和从后往前 你可以在对象中向前或从后往前并提供可选的起始对象:

要 从前往后,使用first;用after指定一个起始对象。 要 从后往前,请使用last;用before指定一个起始对象。 你 不能将first与before或last与after结合起来。如果你在查询中这样做,before/after将被忽略,实际上只应用first或last(在列表的开头或结尾,取决于你使用的是哪个) 。

请注意,你可以在没有错误消息的情况下查询比实际存在的对象更多的对象。

使用skip跳过元素 你还可以通过提供skip参数跳过任意方向的任意数量的对象:

当使用first时,skip会跳过列表开头的对象。 当使用last时,skip跳过列表末尾的对象。 例子 对于以下示例,我们假设一个正好包含30个对象的列表:

查询前3个链接(从前往后):

const links = await prisma .links({ first: 3 })

query { links(first: 3) { id createdAt description url } } 查询从位置6到位置10的对象(从前往后):

const links = await prisma .links({ first: 5, skip: 5 })

query { links( first: 5 skip: 5 ) { id createdAt description url } } 查询最后3个对象(从后往前):

const links = await prisma .links({ last: 3 })

query { links(last: 3) { id createdAt description url } } 查询从第21位到第27位的对象(从后往前):

const links = await prisma .links({ last: 7, skip: 3 })

query { links( last: 7 skip: 3 ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id查询对象之后的前3个对象:

const links = await prisma .links({ first: 3, after: "cixnen24p33lo0143bexvr52n" })

query { links( first: 3 after: "cixnen24p33lo0143bexvr52n" ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id跳过3条数据查询后面的5个对象:

const links = await prisma .links({ first: 5, after: "cixnen24p33lo0143bexvr52n", skip: 3 })

query { links( first: 5 after: "cixnen24p33lo0143bexvr52n" skip: 3 ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id查询对象之前的最后5个对象:

const links = await prisma .links({ last: 5, before: "cixnen24p33lo0143bexvr52n" })

query { links( last: 5 before: "cixnen24p33lo0143bexvr52n" ) { id createdAt description url } } 查询节点之前的最后3个节点,其中cixnen24p33lo0143bexvr52n为id并跳过5个:

const links = await prisma .links({ last: 3, before: "cixnen24p33lo0143bexvr52n", skip: 5 })

query { links( last: 3 before: "cixnen24p33lo0143bexvr52n" skip: 5 ) { id createdAt description url } } 聚合 你可以通过[Connection查询]提交聚合查询。支持以下聚合功能:

count:计算列表中的对象数 avg(即将推出):计算数字列表的平均值。 median(即将推出):计算数字列表的中位数。 max(即将推出):返回数字列表中最大的元素。 min(即将推出):返回数字列表中的最小元素。 sum(即将推出):计算数字列表的总和。 请参阅thisGitHub问题以了解有关即将推出的聚合功能的更多信息。

例子 Count链接对象的数量:

const linkCount = await prisma .linksConnection() .aggregate() .count()

query { linksConnection { aggregate { count } } } 写入数据-JavaScript 对于前面创建的的数据模型,每个模型类型生成了写入数据六种方法。这些在GraphQL schema,例如用于User模型中的Mutation而得到:

createUser:创建一个新的User记录在数据库中

updateUser:更新现有User记录在数据库中

deleteUser:从数据库中删除现有的User记录

upsertUser:更新现有的或在数据库中创建一个新的User纪录

updateManyUsers:更新许多现有的User数据库中的记录

deleteManyUsers:从数据库中删除现有的许多User记录

你可以查看一下生成GraphQL schema的API 这里

你可以查看要生成的JavaScript client这里。

这些方法中的一个的每个调用被作为[事务]执行,这意味着它要么保证完全成功,或者如果它只是部分地失败但要被全部取消而回到原先状态,所以不必担心数据一致性。

创建对象 当在数据库中创建新的记录,create方法需要用一个输入对象包裹起来所有数据。它还提供了一种用于模型创建关系数据,这可以通过使用嵌套对象写入来提供。

每个方法调用的返回包含刚刚创建的模型的所有字段。

查看createLink的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L567) 查看createUser的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L418) 查看createVote的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L214) 例子 create新用户:

const newUser = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles" })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" }) { id name email password } } create新vote:

const newVote = await prisma .createVote({ user: { connect: { email: "alice@prisma.io" } }, link: { connect: { id: "cjli47wr3005b0a23m9crhh0e" } } })

generated mutation

mutation { createVote(data: { user: { connect: { email: "alice@prisma.io" } } link: { connect: { id: "cjli47wr3005b0a23m9crhh0e" } } }) { id } } create新用户并加两个新links:

const newUserWithLinks = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles", links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] }, })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] } }) { id name email password } } 更新对象 当在数据库中更新现有记录中,update-方法接收具有两个字段的一个输入对象:

data:这类似于你提供给一个create-方法输入对象。它包含模型更新的字段,让你提供的关系数据通过嵌套对象写入。 where:这是用来选择要更新的记录。你可以使用任何[唯一]字段来标识记录。 查看data的类型和where用于Link,User和Vote

data

查看updateLink的data字段的类型这里 查看updateUser的data字段的类型这里 查看updateVote的data字段的类型这里 where

查看updateLink的where字段的类型这里 查看updateUser的where字段的类型这里 查看updateVote的where字段的类型这里 每个方法调用返回一个包含刚更新了模型的所有字段的对象。

例子 设置现有用户一个新的名字:

const updatedUser = await prisma .updateUser({ data: { name: "Alice" }, where: { id: "cjli512bd005g0a233s1ogbgy" } })

generated mutation

mutation { updateUser( data: { name: "Alice" } where: { id: "cjli512bd005g0a233s1ogbgy" } ) { id name email password } } Update一个链接,改变它的发布者为不同的用户:

const updatedLink = await prisma .updateLink({ data: { postedBy: { connect: { id: "cjli512bd005g0a233s1ogbgy" } }, }, where: { id: "cjli47wr3005b0a23m9crhh0e" } })

mutation { updateLink( data: { postedBy: { connect: { id: "cjli512bd005g0a233s1ogbgy" } } } where: { id: "cjli47wr3005b0a23m9crhh0e" } ) { id createdAt description url } } delete发布vote的用户:

const updatedVote = await prisma .updateVote({ data: { user: { delete: true }, }, where: { id: "cjli5aual005n0a233ekv89o4" } })

mutation { updateVote( data: { user: { delete: true } } where: { id: "cjli5aual005n0a233ekv89o4" } ) { id } } 为了成功执行此操作,vote的user不能是必填字段。否则,每一个Vote需要连接到一个User,删除就报错了。

删除对象 当从数据库中删除记录,该delete-方法接收一个输入对象,指定哪个记录是要被删除。类型此输入对象的是相同的update-方法where对象。

该对象的属性对应于被标记为唯一的模型的那些字段。对于上面的示例数据模型,这意味着,对于User,Vote和Link它有一个id属性。对于User它另外接受email字段。

查看类型的输入对象的Link,User和Vote

查看deleteLink的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L455) 查看deleteUser的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L357) 查看deleteVote的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L522) 每个方法调用返回一个包含刚删除的模型的所有字段的对象。

例子 delete其ID链接:

const deletedLink = await prisma .deleteLink({ id: "cjli47wr3005b0a23m9crhh0e" })

mutation { deleteLink(where: { id: "cjli47wr3005b0a23m9crhh0e" }) { id createdAt description url } } 通过他们的email来delete用户:

const deletedUser = await prisma .deleteUser({ email: "alice@prisma.io" })

mutation { deleteUser(where: { email: "alice@prisma.io" }) { id createdAt description url } } 创建或更新对象(upserts) UPSERT操作允许你尝试更新现有的记录。如果该记录实际上还不存在,它将被创建。该upsert的方法是create-和update-的搭配方法,这意味着他们收到有三个字段输入参数:

where:相同于在update方法中提供的where字段 update:相同于在update的方法中提供的data字段 create:相同于在create的方法中提供的输入input 例子 Update链接的URL。如果该链接不存在,创建一个新的:

const updatedOrCreatedLink = await prisma .upsertLink({ where: { id: "cjli47wr3005b0a23m9crhh0e" }, update: { url: "https://www.howtographql.com" }, create: { url: "https://www.howtographql.com", description: "Fullstack GraphQL Tutorial" } })

mutation { upsertLink( where: { id: "cjli47wr3005b0a23m9crhh0e" } update: { url: "https://www.howtographql.com" } create: { url: "https://www.howtographql.com" description: "Fullstack GraphQL Tutorial" } ) { id } } 更新和删除多条记录 该Prisma的clientAPI提供了特殊的方法来更新或一次删除多条记录。GraphQL API中的相应的Mutation被称为[批量Mutation(#批量Mutation)。每个updateMany-和deleteMany-方法返回最终受到影响的操作记录数。

例子 Update三个links,通过其ID指定,将其网址重置为空string:

const updatedLinksCount = await prisma .updateManyLinks({ where: { id_in: ["cjli6tknz005s0a23uf0lmlve", "cjli6tnkj005x0a2325ynfpb9", "cjli6tq3200620a23s4lp8npd"] }, data: { url: "" }, }).count

mutation { updateManyLinks({ where: { id_in: ["cjli6tknz005s0a23uf0lmlve", "cjli6tnkj005x0a2325ynfpb9", "cjli6tq3200620a23s4lp8npd"] } data: { url: "" } }) { count } } 注意

如果一个或多个所提供的ID没有在数据库中实际存在,操作将 不返回错误。

Update 所有links的描述中包含字符串'graphql`的数据,其网址重置为空string:

const updatedLinksCount = await prisma .updateManyLinks({ where: { description_contains: "graphql" }, data: { url: "" }, }).count

mutation { updateManyLinks({ where: { description_contains: "graphql" } data: { url: "" } }) { count } } delete在2018年之前创建的所有链接:

const deleteLinksCount = await prisma .deleteManyLinks({ createdAt_lte: "2018" }).count

mutation { deleteManyLinks(where: { createdAt_lte: "2018" }) { count } } 嵌套对象写入 嵌套的对象写入操作让你在一个单一的Mutation中修改多个模型数据库记录。Prisma的GraphQL API相应的概念被称为嵌套Mutation。嵌套对象写入可供create-和update的方法。

如果模型具有相对于不同的schema,对应关系字段被设定为具有以下性质中的一个子集的对象:

create:创建相关模型的新纪录,并通过关系将其连接。 update:更新通过关系已经连接了相关模型的现有记录。 upsert:更新相关模型的现有记录或创建并连接一个新的。 delete:删除通过关系连接的相关模型的现有记录。 connect:通过连接关系的相关模型的现有记录。 disconnect:删除通过关系连接相关模型的现有记录。 例子 create新用户和两个新的link,并连接一个现有的link:

const newUserWithLinks = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles", links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }], connect: { id: "cjli6tknz005s0a23uf0lmlve" } }, })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] connect: { id: "cjli6tknz005s0a23uf0lmlve" } } }) { id name email password } } delete用户的链接:

const updatedUser = await prisma .updateUser({ where: { id: "cjli8znnd006n0a23ywc6wf8w" }, data: { links: { delete: { id: "cjli6tknz005s0a23uf0lmlve" } } } })

mutation { updateUser( where: { id: "cjli8znnd006n0a23ywc6wf8w" } data: { links: { delete: { id: "cjli6tknz005s0a23uf0lmlve" } } } ) { id } } 实时数据-JavaScript 该Prisma的client,使用\$subscribe属性实时更新和订阅数据库事件和数据。对于你的数据模型中的每个模型类型,Prisma client公开以此属性的模型命名的(小写)一个函数:Subscriptions。使用此函数意味着比对该模型的[写入]事件(即create,update,delete)有实时需求。你可以提供一个过滤器对象,使得可以进一步约束要接收更新的事件类型。该函数返回async iterator,只要发生指定的数据库事件,就会发出事件通知。

该\$subscribe API是基于[WebSockets的(https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)。

例子 Subscribe"新建"和"更新"User的事件:

const createdAndUpdatedUserIterator = await prisma.$subscribe.user({ mutation_in: ['CREATED', 'UPDATED'] }).node() _SubscribeUser的电子邮件地址有包含字符串'gmail`的更新:

const userIterator = await prisma.$subscribe.user({ email_contains: gmail }).node() 检查是否存在-JavaScript 该Prisma的client,你可以为你在使用$exists属性中的数据库中是否存在一定的记录。对于你的[数据模型]每个模型类型,Prisma client公开此属性的模型命名的(小写)一个函数。这些函数的接收过滤对象作为指定该记录的条件,并返回一个布尔值;如果有至少一个数据库记录符合指定过滤,该值是TRUE,否则返回FALSE。

输入对象具有用于[过滤列表]相同的类型。

例子 检查是否有具有特定ID 存在的用户:

const userExists = prisma.$exists.user({ id: "cjli6tko8005t0a23fid7kke7" }) 检查是否有用户名为Alice或Bob 的user存在:

const userExists = prisma.$exists.user({ name_in: ['Alice', 'Bob'] }) 检查是否有在描述中有prisma或graphql字段存在的link,并且创建于2018年:

const linkExists = await prisma.$exists.link({ AND: [ { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] }, { AND: [{ createdAt_gt: "2017" }, { createdAt_lt: "2019" }] } ], }) graphql-requests-JavaScript 该Prisma的client,可以直接发送GraphQL Query和Mutation,$request方法请求你的Prisma service:

$request: <T = any>(query: string, variables?: {[key: string]: any}) => Promise; 例子 查询单个用户:

query { user(id: "cjcdi63j80adw0146z7r59bn5") { id name } } prisma.request(query) .then(result => console.log(result)) // 示例result: // {"data": { "user": { "id": "cjcdi63j80adw0146z7r59bn5", "name": "Sarah" } } } 使用variables查询单个用户:

query ($userId: ID!){ user(id: $userId) { id name } } const variables = { userId: 'cjcdi63j80adw0146z7r59bn5' } prisma.request(query, variables) .then(result => console.log(result)) // 示例result: // {"data": { "user": { "id": "cjcdi63j80adw0146z7r59bn5", "name": "Sarah" } } } 读取数据-TypeScript Prisma client基于Prisma API的GraphQL schema定义生成操作。对于reading数据,它基本上复制你Prisma service中的GraphQL Query。

对于此页面,我们假设你的Prisma API基于以下datamodel:

type Link { id: ID! @id createdAt: DateTime! @createdAt description: String! url: String! postedBy: User votes: [Vote!]! } type User { id: ID! @id name: String! email: String! @unique password: String! links: [Link!]! votes: [Vote!]! } type Vote { id: ID! @id link: Link! user: User! } 你可以查看API生成的GraphQL schema这里 你可以查看生成的TypeScript client此处。 每当使用Prisma client查询模型时,都会获取该模型的所有字段。无论是查询单个对象还是对象列表,都是如此。

例如,以下查询返回单个"User"的所有标量字段:

const user: UserNode = await prisma.user({ email: "bob@prisma.io" }) 在这种情况下,返回的user对象将具有四个属性(对应于User模型的标量字段):id,name,email和password。

links和votes字段都是relation fields,因此不包含在查询中。

以下是获取User对象list的示例:

const users: UserNode[] = await prisma.users() 与前面的查询类似,users数组中的每个对象只有标量和无关系字段。

获取单个对象 对于datamodel中的每个模型类型,在Prisma clientAPI中生成一种方法,允许获取该模型的单个对象。该方法以类型命名,但以小写字符开头。对于上面的示例数据模型,获取单个"User","Link"和"Vote"对象的三种方法称为"user","link"和"vote"。

这些函数的输入是一个对象,它具有模型的任何[唯一]字段作为属性。这意味着,对于所有三种方法,都接受id字段(因为相应的模型每个都有一个注释为@unique的id字段)。 user方法的输入对象另外有一个email字段。

查看Link,User和Vote的输入对象的类型

查看Link的"where"类型here 查看User的"where"类型here 查看Vote的"where"类型here 例子 通过其id获取单个投票:

const vote:VoteNode = await prisma.vote({id:"cjlgpyueg001o0a239d3i07ao"})

generated query

query { vote(where: { id: "cjlgpyueg001o0a239d3i07ao" }) { id } } 通过email获取单个用户:

const用户:UserNode = await prisma.user({email:"alice@prisma.io"})

generated query

query { user(where: { email: "alice@prisma.io" }) { id } } 获取列表 对于datamodel中的每个模型类型,在Prisma client API中生成一个方法,允许获取这些模型对象的列表。该方法以模型命名,但以小写字符开头并使用复数形式。对于上面的示例数据模型,获取"User","Link"和"Vote"模型列表的三种方法称为"users","links"和"votes"。

这些函数的输入参数是一个具有以下属性的对象:

过滤:where 排序:orderBy 分页:before,after,first,last,skip 例子 查询 all links:

const links: LinkNode[] = await prisma.links()

generated query

query { links { id createdAt url description } } 获取users列表:

const users: UserNode[] = await prisma.users()

generated query

query { users { id name email password } } 关系 Prisma client基于fluent API来查询数据图中的关系。这意味着你可以简单地链式调用你的方法来返回模型的关系属性。

注意

这仅在检索单个对象时才有可能,而不是用于列表。这意味着你无法查询列表中返回的对象的关系字段,例如:

//不可能 const result = await prisma .users() .links() 在这个例子中,users()已经返回一个列表,因此不可能查询列表中每个用户对象的links关系。

例子 Query查询单个user的所有链接:

const linksByUser: LinkNode[] = await prisma .user({ email: "alice@prisma.io" }) .links()

query { user(where: { email: "alice@prisma.io" }) { links { id createdAt description url } } } 查询某个user所做的所有投票:

const votesByUser: VoteNode[] = await prisma .user({ email: "alice@prisma.io" }) .votes()

query { user(where: { email: "alice@prisma.io" }) { votes { id } } } 使用片段fragment进行细粒度数据访问 你可以使用$fragment API功能(基于GraphQL)指定要检索的字段,而不是查询模型的所有标量字段(这是默认行为)。这对于排除大量不需要的字段(如BLOB值或大字符串)或检索关系很有用。

例子 获取用户的name和email以及相关links的description和url:

const fragment = fragment UserWithLinks on User { name email links { description url } } const userWithPosts = await prisma.users().$fragment(fragment)

query { users { ...UserWithLinks } } fragment UserWithLinks on User { name email links { description url } } 列表的基本过滤器 Basic过滤器允许你指定某些条件来约束应在列表中返回哪些对象。过滤器在输入参数的where对象中指定,任何列表查询都接受该对象。

where对象的类型取决于生成它的模型。

查看Link,User和Vote 的where类型

查看Link的"where"类型here 查看User的"where"类型here 查看Vote的"where"类型here 也可以使用AND和OR字段组合多个过滤器。

例子 找到名称中包含"A"的用户:

const usersWithAInName: UserNode[] = await prisma .users({ where: { name_contains: "A" } })

query { users(where: { name_contains: "A" }) { id name email password } } 使用自动完成功能来探索过滤系统

查询称为"Alice"或"Bob`的用户:

const usersCalledAliceOrBob: UserNode[] = await prisma .users({ where: { name_in: ["Alice", "Bob"] } })

query { users(where: { name_in: ["Alice", "Bob"] }) { id name email password } } 获取2018年12月24日之前创建的链接:

const linksBeforeChristmas: LinkNode[] = await prisma .links({ where: { createdAt_lt: "2018-12-24" } })

query { links(where: { createdAt_lt: "2018-12-24" }) { id createdAt description url } } Prisma clientAPI中的日期和时间遵循ISO 8601标准,该标准通常具有以下形式:YYYY-MM-DDThh:mm:ss。

查询在description中有prisma 或graphql的链接并且是在2018年创建的:

const filteredLinks: LinkNode[] = await prisma .links({ where: { AND: [ { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] }, { AND: [{ createdAt_gt: "2017" }, { createdAt_lt: "2019" }] } ], } })

query { links(where: { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] AND: [{ createdAt_lt: "2019" }, { createdAt_gt: "2017" }] }) { id description } } 实际上可以省略AND过滤器,因为默认情况下使用logical and组合了多个过滤条件。这意味着上面的过滤器也可以表示如下:

const filteredLinks: LinkNode[] = await prisma .links({ where: { OR: [{ description_contains: "graphql" }, { description_contains: "prisma" }], createdAt_gt: "2017", createdAt_lt: "2019" } })

query { links(where: { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }], createdAt_lt: "2019", createdAt_gt: "2017" }) { id createdAt description url } } 列表的关系过滤器 关系过滤器可用于约束关系列表字段上的返回对象。用于过滤的类型与基本过滤器相同,唯一的区别是过滤器不应用于方法调用的第一级,而是在第二级查询关系时。

例子

const linksByUserBeforeChristmas: LinkNode[] = await prisma .user({ email: "alice@prisma.io" }) .links({ where: { createdAt_lt: "2017-12-24" } })

query { user(where: { email: "alice@prisma.io" }) { links(where: { createdAt_lt: "2017-12-24" }) { id createdAt description url } } } 排序 查询模型对象列表时,可以按该模型类型的任何标量字段对列表进行排序。因此,每个生成的查询模型列表的方法都接受其输入对象上的orderBy字段。

orderBy字段的类型取决于生成它的模型的标量字段。

查看link,user和'vote'的orderBy类型

查看link 的orderBy类型here 查看user 的orderBy类型here 查看Vote 的orderBy类型here 例子 _按创建日期(升序)_links:

const sortedLinks: LinkNode[] = prisma .links({ orderBy: "createdAt_ASC" })

query { links(orderBy: createdAt_ASC) { id createdAt description url } } 按字母顺序排序用户名(降序):

const sortedUsers: UserNode[] = prisma .users({ orderBy: "name_DESC" })

query { users(orderBy: name_DESC) { id name email password } } 分页 查询模型对象列表时,可以通过提供分页参数来获取该列表的部分信息。常在feed流中使用。

用'first和last`向前和从后往前 你可以在对象中向前或从后往前并提供可选的起始对象:

要 从前往后,使用first;用after指定一个起始对象。 要 从后往前,请使用last;用before指定一个起始对象。 你 不能将first与before或last与after结合起来。如果你在查询中这样做,before/after将被忽略,实际上只应用first或last(在列表的开头或结尾,取决于你使用的是哪个) 。

请注意,你可以在没有错误消息的情况下查询比实际存在的对象更多的对象。

使用skip跳过元素 你还可以通过提供skip参数跳过任意方向的任意数量的对象:

当使用first时,skip会跳过列表开头的对象。 当使用last时,skip跳过列表末尾的对象。 例子 对于以下示例,我们假设一个正好包含30个对象的列表:

查询前3个链接(从前往后):

const links: LinkNode[] = await prisma .links({ first: 3 })

query { links(first: 3) { id createdAt description url } } 查询从位置6到位置10的对象(从前往后):

const links: LinkNode[] = await prisma .links({ first: 5, skip: 5 })

query { links( first: 5 skip: 5 ) { id createdAt description url } } 查询最后3个对象(从后往前):

const links: LinkNode[] = await prisma .links({ last: 3 })

query { links(last: 3) { id createdAt description url } } 查询从第21位到第27位的对象(从后往前):

const links: LinkNode[] = await prisma .links({ last: 7, skip: 3 })

query { links( last: 7 skip: 3 ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id查询对象之后的前3个对象:

const links: LinkNode[] = await prisma .links({ first: 3, after: "cixnen24p33lo0143bexvr52n" })

query { links( first: 3 after: "cixnen24p33lo0143bexvr52n" ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id跳过3条数据查询后面的5个对象:

const links: LinkNode[] = await prisma .links({ first: 5, after: "cixnen24p33lo0143bexvr52n", skip: 3 })

query { links( first: 5 after: "cixnen24p33lo0143bexvr52n" skip: 3 ) { id createdAt description url } } 使用cixnen24p33lo0143bexvr52n作为id查询对象之前的最后5个对象:

const links: LinkNode[] = await prisma .links({ last: 5, before: "cixnen24p33lo0143bexvr52n" })

query { links( last: 5 before: "cixnen24p33lo0143bexvr52n" ) { id createdAt description url } } 查询节点之前的最后3个节点,其中cixnen24p33lo0143bexvr52n为id并跳过5个:

const links: LinkNode[] = await prisma .links({ last: 3, before: "cixnen24p33lo0143bexvr52n", skip: 5 })

query { links( last: 3 before: "cixnen24p33lo0143bexvr52n" skip: 5 ) { id createdAt description url } } 聚合 你可以通过[Connection查询]提交聚合查询。支持以下聚合功能:

count:计算列表中的对象数 avg(即将推出):计算数字列表的平均值。 median(即将推出):计算数字列表的中位数。 max(即将推出):返回数字列表中最大的元素。 min(即将推出):返回数字列表中的最小元素。 sum(即将推出):计算数字列表的总和。 请参阅thisGitHub问题以了解有关即将推出的聚合功能的更多信息。

例子 Count链接对象的数量:

const linkCount: number = await prisma .linksConnection() .aggregate() .count()

query { linksConnection { aggregate { count } } } 写入数据-TypeScript 对于前面创建的的数据模型,每个模型类型生成了写入数据六种方法。这些在GraphQL schema,例如用于User模型中的Mutation而得到:

createUser:创建一个新的User记录在数据库中

updateUser:更新现有User记录在数据库中

deleteUser:从数据库中删除现有的User记录

upsertUser:更新现有的或在数据库中创建一个新的User纪录

updateManyUsers:更新许多现有的User数据库中的记录

deleteManyUsers:从数据库中删除现有的许多User记录

你可以查看一下生成GraphQL schema的API 这里

你可以查看要生成的JavaScript client这里。

这些方法中的一个的每个调用被作为[事务]执行,这意味着它要么保证完全成功,或者如果它只是部分地失败但要被全部取消而回到原先状态,所以不必担心数据一致性。

创建对象 当在数据库中创建新的记录,create方法需要用一个输入对象包裹起来所有数据。它还提供了一种用于模型创建关系数据,这可以通过使用嵌套对象写入来提供。

每个方法调用的返回包含刚刚创建的模型的所有字段。

查看createLink的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L567) 查看createUser的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L418) 查看createVote的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L214) 例子 create新用户:

const newUser: UserNode = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles" })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" }) { id name email password } } create新vote:

const newVote: VoteNode = await prisma .createVote({ user: { connect: { email: "alice@prisma.io" } }, link: { connect: { id: "cjli47wr3005b0a23m9crhh0e" } } })

generated mutation

mutation { createVote(data: { user: { connect: { email: "alice@prisma.io" } } link: { connect: { id: "cjli47wr3005b0a23m9crhh0e" } } }) { id } } create新用户并加两个新links:

const newUserWithLinks: UserNode = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles", links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] }, })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] } }) { id name email password } } 更新对象 当在数据库中更新现有记录中,update-方法接收具有两个字段的一个输入对象:

data:这类似于你提供给一个create-方法输入对象。它包含模型更新的字段,让你提供的关系数据通过嵌套对象写入。 where:这是用来选择要更新的记录。你可以使用任何[唯一]字段来标识记录。 查看data的类型和where用于Link,User和Vote

data

查看updateLink的data字段的类型这里 查看updateUser的data字段的类型这里 查看updateVote的data字段的类型这里 where

查看updateLink的where字段的类型这里 查看updateUser的where字段的类型这里 查看updateVote的where字段的类型这里 每个方法调用返回一个包含刚更新了模型的所有字段的对象。

例子 设置现有用户一个新的名字:

const updatedUser: UserNode = await prisma .updateUser({ data: { name: "Alice" }, where: { id: "cjli512bd005g0a233s1ogbgy" } })

generated mutation

mutation { updateUser( data: { name: "Alice" } where: { id: "cjli512bd005g0a233s1ogbgy" } ) { id name email password } } Update一个链接,改变它的发布者为不同的用户:

const updatedLink: LinkNode = await prisma .updateLink({ data: { postedBy: { connect: { id: "cjli512bd005g0a233s1ogbgy" } }, }, where: { id: "cjli47wr3005b0a23m9crhh0e" } })

mutation { updateLink( data: { postedBy: { connect: { id: "cjli512bd005g0a233s1ogbgy" } } } where: { id: "cjli47wr3005b0a23m9crhh0e" } ) { id createdAt description url } } delete发布vote的用户:

const updatedVote: VoteNode = await prisma .updateVote({ data: { user: { delete: true }, }, where: { id: "cjli5aual005n0a233ekv89o4" } })

mutation { updateVote( data: { user: { delete: true } } where: { id: "cjli5aual005n0a233ekv89o4" } ) { id } } 为了成功执行此操作,vote的user不能是必填字段。否则,每一个Vote需要连接到一个User,删除就报错了。

删除对象 当从数据库中删除记录,该delete-方法接收一个输入对象,指定哪个记录是要被删除。类型此输入对象的是相同的update-方法where对象。

该对象的属性对应于被标记为唯一的模型的那些字段。对于上面的示例数据模型,这意味着,对于User,Vote和Link它有一个id属性。对于User它另外接受email字段。

查看类型的输入对象的Link,User和Vote

查看deleteLink的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L455) 查看deleteUser的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L357) 查看deleteVote的的输入对象的类型(这里)(https://github.com/nikolasburk/prisma-client-examples/blob/master/generated/prisma.ts#L522) 每个方法调用返回一个包含刚删除的模型的所有字段的对象。

例子 delete其ID链接:

const deletedLink: LinkNode = await prisma .deleteLink({ id: "cjli47wr3005b0a23m9crhh0e" })

mutation { deleteLink(where: { id: "cjli47wr3005b0a23m9crhh0e" }) { id createdAt description url } } 通过他们的email来delete用户:

const deletedUser: UserNode = await prisma .deleteUser({ email: "alice@prisma.io" })

mutation { deleteUser(where: { email: "alice@prisma.io" }) { id createdAt description url } } 创建或更新对象(upserts) UPSERT操作允许你尝试更新现有的记录。如果该记录实际上还不存在,它将被创建。该upsert的方法是create-和update-的搭配方法,这意味着他们收到有三个字段输入参数:

where:相同于在update方法中提供的where字段 update:相同于在update的方法中提供的data字段 create:相同于在create的方法中提供的输入input 例子 Update链接的URL。如果该链接不存在,创建一个新的:

const updatedOrCreatedLink: LinkNode = await prisma .upsertLink({ where: { id: "cjli47wr3005b0a23m9crhh0e" }, update: { url: "https://www.howtographql.com" }, create: { url: "https://www.howtographql.com", description: "Fullstack GraphQL Tutorial" } })

mutation { upsertLink( where: { id: "cjli47wr3005b0a23m9crhh0e" } update: { url: "https://www.howtographql.com" } create: { url: "https://www.howtographql.com" description: "Fullstack GraphQL Tutorial" } ) { id } } 更新和删除多条记录 该Prisma的clientAPI提供了特殊的方法来更新或一次删除多条记录。GraphQL API中的相应的Mutation被称为[批量Mutation(#批量Mutation)。每个updateMany-和deleteMany-方法返回最终受到影响的操作记录数。

例子 Update三个links,通过其ID指定,将其网址重置为空string:

const updatedLinksCount: number = await prisma .updateManyLinks({ where: { id_in: ["cjli6tknz005s0a23uf0lmlve", "cjli6tnkj005x0a2325ynfpb9", "cjli6tq3200620a23s4lp8npd"] }, data: { url: "" }, }).count

mutation { updateManyLinks({ where: { id_in: ["cjli6tknz005s0a23uf0lmlve", "cjli6tnkj005x0a2325ynfpb9", "cjli6tq3200620a23s4lp8npd"] } data: { url: "" } }) { count } } 注意

如果一个或多个所提供的ID没有在数据库中实际存在,操作将 不返回错误。

Update 所有links的描述中包含字符串'graphql`的数据,其网址重置为空string:

const updatedLinksCount: number = await prisma .updateManyLinks({ where: { description_contains: "graphql" }, data: { url: "" }, }).count

mutation { updateManyLinks({ where: { description_contains: "graphql" } data: { url: "" } }) { count } } delete在2018年之前创建的所有链接:

const deleteLinksCount: number = await prisma .deleteManyLinks({ createdAt_lte: "2018" }).count

mutation { deleteManyLinks(where: { createdAt_lte: "2018" }) { count } } 嵌套对象写入 嵌套的对象写入操作让你在一个单一的Mutation中修改多个模型数据库记录。Prisma的GraphQL API相应的概念被称为嵌套Mutation。嵌套对象写入可供create-和update的方法。

如果模型具有相对于不同的schema,对应关系字段被设定为具有以下性质中的一个子集的对象:

create:创建相关模型的新纪录,并通过关系将其连接。 update:更新通过关系已经连接了相关模型的现有记录。 upsert:更新相关模型的现有记录或创建并连接一个新的。 delete:删除通过关系连接的相关模型的现有记录。 connect:通过连接关系的相关模型的现有记录。 disconnect:删除通过关系连接相关模型的现有记录。 例子 create新用户和两个新的link,并连接一个现有的link:

const newUserWithLinks: UserNode = await prisma .createUser({ name: "Alice", email: "alice@prisma.io", password: "IlikeTurtles", links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }], connect: { id: "cjli6tknz005s0a23uf0lmlve" } }, })

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" links: { create: [{ description: "My first link", url: "https://www.prisma.io" }, { description: "My second link", url: "https://www.howtographql.com" }] connect: { id: "cjli6tknz005s0a23uf0lmlve" } } }) { id name email password } } delete用户的链接:

const updatedUser: UserNode = await prisma .updateUser({ where: { id: "cjli8znnd006n0a23ywc6wf8w" }, data: { links: { delete: { id: "cjli6tknz005s0a23uf0lmlve" } } } })

mutation { updateUser( where: { id: "cjli8znnd006n0a23ywc6wf8w" } data: { links: { delete: { id: "cjli6tknz005s0a23uf0lmlve" } } } ) { id } } 实时数据-TypeScript 该Prisma的client,使用\$subscribe属性实时更新和订阅数据库事件和数据。对于你的数据模型中的每个模型类型,Prisma client公开以此属性的模型命名的(小写)一个函数:Subscriptions。使用此函数意味着比对该模型的[写入]事件(即create,update,delete)有实时需求。你可以提供一个过滤器对象,使得可以进一步约束要接收更新的事件类型。该函数返回async iterator,只要发生指定的数据库事件,就会发出事件通知。

该\$subscribe API是基于[WebSockets的(https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)。

例子 Subscribe"新建"和"更新"User的事件:

const createdAndUpdatedUserIterator: UserAsyncIterator = await db.$subscribe.user({ mutation_in: ['CREATED', 'UPDATED'] }).node() _SubscribeUser的电子邮件地址有包含字符串'gmail`的更新:

const userIterator: UserAsyncIterator = await db.$subscribe.user({ email_contains: gmail }).node() 检查是否存在-TypeScript 该Prisma的client,你可以为你在使用$exists属性中的数据库中是否存在一定的记录。对于你的[数据模型]每个模型类型,Prisma client公开此属性的模型命名的(小写)一个函数。这些函数的接收过滤对象作为指定该记录的条件,并返回一个布尔值;如果有至少一个数据库记录符合指定过滤,该值是TRUE,否则返回FALSE。

输入对象具有用于[过滤列表]相同的类型。

例子 检查是否有具有特定ID 存在的用户:

const userExists: boolean = prisma.$exists.user({ id: "cjli6tko8005t0a23fid7kke7" }) 检查是否有用户名为Alice或Bob 的user存在:

const userExists: boolean = prisma.$exists.user({ name_in: ['Alice', 'Bob'] }) 检查是否有在描述中有prisma或graphql字段存在的link,并且创建于2018年:

const linkExists: boolean = await prisma.$exists.link({ AND: [ { OR: [{ description_contains: "graphql", }, { description_contains: "prisma", }] }, { AND: [{ createdAt_gt: "2017" }, { createdAt_lt: "2019" }] } ], }) graphql-requests-TypeScript 该Prisma的client,可以直接发送GraphQL Query和Mutation,$request方法请求你的Prisma service:

$request: <T = any>(query: string, variables?: {[key: string]: any}) => Promise; 例子 查询单个用户:

query { user(id: "cjcdi63j80adw0146z7r59bn5") { id name } } const result: any = prisma.$request(query) // 示例result: // {"data": { "user": { "id": "cjcdi63j80adw0146z7r59bn5", "name": "Sarah" } } } 使用variables查询单个用户:

query ($userId: ID!){ user(id: $userId) { id name } } const variables: UserWhereUniqueInput = { id: 'cjcdi63j80adw0146z7r59bn5' } const result: any = prisma.$request(query, variables) .then(result => console.log(result)) // 示例result: // {"data": { "user": { "id": "cjcdi63j80adw0146z7r59bn5", "name": "Sarah" } } } 读取数据-Go 每当使用Prisma client查询模型时,都会获取该模型的all标量fields。 无论是查询单个对象还是对象列表,都是如此。

例如,以下查询返回单个"User"的所有标量字段:

user := client.User(&prisma.UserWhereInput{ "email": "bob@prisma.io" }) 在这种情况下,返回的user对象将具有四个属性(对应于User模型的标量字段):id,name,email和password。

links和votes字段都是relation fields,因此不包含在查询中。

以下是获取User对象list的示例:

users := client.Users(nil).Exec() 与前面的查询类似,users数组中的每个对象只有标量和无关系字段。

写入数据-Go 与JS和TS类似,仅语法不同。

创建对象 当在数据库中创建新的记录,create法需要它包装上创建的所有记录的字段一个输入对象。它还提供了一种用于模型创建关系数据,这可以通过使用嵌套对象写入来供给。

每个方法调用返回包含刚刚创建的模型的所有字段的对象。

例子 create新用户:

name := "Alice", email := "alice@prisma.io", password := "IlikeTurtles" newUser, err := db.CreateUser(&prisma.UserCreateInput{ Name: &name, Email: &email, Password: &password, }).Exec()

generated mutation

mutation { createUser(data: { name: "Alice" email: "alice@prisma.io" password: "IlikeTurtles" }) { id name email password } } Subscription-Go 目前暂时没有。

检查是否存在-Go 例子 检查是否具有特定ID存在的用户:

userExists := client.Exists.User(&prisma.UserWhereUniqueInput{ ID: "cjli6tko8005t0a23fid7kke7", }) graphql-request-go 该Prisma的client,你可以直接使用$request方法发送GraphQL Query和Mutation到你的Prisma service:

GraphQL(query string, variables map[string]interface{}) (map[string]interface{}, error) 由于GraphQL操作传递无类型(作为一个普通字符串),返回的承诺的类型是any。

例子 查询单个用户:

query := query { user(id: "cjcdi63j80adw0146z7r59bn5") { id name } } result := client.GraphQL(query, nil) // sample result: // map[data:[map[user:[map[id:cjcdi63j80adw0146z7r59bn5 name:Sarah]]]]] 使用variables查询单个用户:

query := query ($id: ID!){ user(id: $id) { id name } } variables := map[string]interface{}{ "id": "cjcdi63j80adw0146z7r59bn5", result := client.GraphQL(query, variables) // sample result: // map[data:[map[user:[map[id:cjcdi63j80adw0146z7r59bn5 name:Sarah]]]]] Creating新用户:

mutation := mutation ($name: String!){ createUser(name: $name) { id name } } variables := map[string]interface{}{ "name": "Alice" } result := client.GraphQL(mutation, variables) // sample result: // map[data:[map[createUser:[map[id:cjlhqfbfa003t0a23rhzjragl name:Alice]]]]] //{ "数据":{ "的createUser":{ "ID": "cjlhqfbfa003t0a23rhzjragl", "名": "爱丽丝"}}} 下一节是Prisma概念,来深入理解它。

cisen commented 5 years ago

深入理解Prisma

什么是Prisma? Prisma的是一个data layer在你的应用程序体系结构取代了传统的ORM。

数据层由几个部分组成:

一个充当数据库的代理的 Prisma服务器 一个Prisma服务器上运行的并产生实际的数据库查询的高性能 查询引擎 一个连接到Prisma服务器的 Prisma client 一个可让你Subscriptions数据库事件的 实时事件系统 使用Prisma cloud的说明

Prisma是开源的,可以随意使用在别的云服务上。Prisma Cloud是一种Prisma自己的提供围绕Prisma的工具和服务的应用程序(通过CLI及web接口使用)。当你使用Prisma时Prisma云的使用是可选的。

Prisma cloud的目标是简化Prisma的项目工作流程。它具有一个 数据浏览器和一个 部署历史(很快推出 自动回滚),各种 团队协作选项,以及 云提供商的集成,以方便为开发者建立和维护他们的Prisma服务器。

开始使用Prisma云这里。

用例 Prisma在你使用数据库的任何content中。

构建GraphQL服务器 Prisma是构建GraphQL服务器的完美工具。Prisma client与Apollo生态系统兼容,具有GraphQLSubscriptions和Relay-style分页默认支持,提供端到端的类型安全,并配有一个内置的DataLoader解决N + 1问题。

构建REST API Prisma也可以构建REST API,它完全可以取代传统的ORM。它提供了N多好处,如类型安全,先进的API和读写相关数据灵活的方式。

CLIs, Scripts, Serverless Functions & a lot more Prisma的有一个非常灵活的API,这使得它非常适合用于各种使用情况。每当你需要跟一个或多个数据库连接,Prisma将对简化数据库工作流程有很大的帮助。

为什么要使用Prisma的? 简单的数据库工作流 Prisma的总体目标是从常见数据库的工作流中删除的复杂性并简化你的应用程序数据访问:

类型安全的数据库访问得益于定制和自动生成的Prisma client。 关系数据和事务简单而强大的API。 Prisma统一(即将推出)访问多个数据库,并且因此显着地降低了跨数据库工程的复杂性。 数据库的实时流操作和事件系统确保使你的数据库中发生的所有重要事件实时更新。 根据数据模型声明自动数据库迁移(可选)使用GraphQL的schema定义语言(SDL)表示。 其他数据库的工作流如数据导入,导出。还有更多…… 数据库的实时层 一些数据库,如RethinkDB或DynamoDB提供开箱即用的实时API。这样的API可以让客户订阅任何修改数据库中发生的事情。绝大多数传统的数据库不提供这样的实时API,手动实现它变得非常复杂。

但Prisma提供所有支持的数据库的实时API,让你Subscriptions任何数据库事件,如creating,updating或deleting数据。

端到端类型安全 类型安全地编程是现代应用开发的默认(如TypeScript)。下面是一些类型安全提供的核心优势:

信心:由于静态分析和编译时错误检查,开发人员可以对其代码充满信心。 开发者体验:具有明确定义的数据类型时,开发人员的开发体验会极大提升。类型定义是IDE功能的基础,如智能自动完成或跳转到定义。 代码生成:在开发工作流程中利用代码生成很容易,避免编写样板代码。 跨系统约定:类型定义可以跨系统共享(例如,在客户端和服务器之间),并用作定义相应接口/API的约定。 端到端类型安全是指从客户端到数据库在整个堆栈中具有类型安全性。 端到端类型安全体系结构可能如下所示:

数据库:Prisma提供强类型数据库层,datamodel定义存储在数据库中的数据类型。 应用服务器:应用程序服务器定义自己的schema(例如,使用GraphQL或OpenAPI / Swagger),它可以重用或转换数据库中的数据类型。 应用程序服务器需要使用类型安全的语言(例如TypeScript,Scala,Go)编写。 客户端:了解应用程序服务器架构的客户端可以在构建时验证API请求和潜在响应。 干净和分层的架构 在开发应用程序服务器时,最复杂的是在同步,查询优化/性能和安全性方面实现安全且组织良好的数据库访问。 当涉及多个数据库时,这会变得更加复杂。

解决这个问题的一个常见解决方案是引入专用数据访问层(DAL),它抽象出数据库访问的复杂性。 DAL的API由应用程序服务器使用,允许API开发人员简单地思考他们需要什么数据,而不必担心如何从数据库安全地和高效地检索它。

使用DAL可确保 明确区分关注点,从而提高代码的 可维护性和可重用性。 具有某种数据库抽象(无论是简单的ORM库还是独立的基础架构组件)是小型应用程序以及大规模运行的应用程序的最佳实践。 它确保应用程序服务器能够以 安全且高效的方式与你的数据库通信。

Prisma是一个自动生成的DAL,遵循与行业领先的DAL(例如Twitter的Strato或Facebook的TAO)相同的原则,同时仍然可以灵活应用于较小的应用程序。

Prisma允许你从一开始就使用干净的体系结构启动项目,并使你免于编写将数据库和应用程序服务器强耦合在一起的代码。

如何使Prisma融入你的体系? Prisma是一个独立的基础架构组件,位于数据库之上。 然后,你在应用程序服务器中使用Prisma client(可以使用各种语言)连接到Prisma。

这使你可以通过简单而现代的API与数据库通信,从而确保高性能和安全的数据库访问。

Prisma基础:datamodel、Prisma server、Prisma client 当使用Prisma时,你主要得清楚这三个概念:

datamodel:定义应用程序的数据模型,是Prisma client API的基础。(可选:可以用来对数据库进行迁移。) Prisma server:位于数据库顶层的独立基础架构组件。 Prisma client:一个自动生成的库,它连接到Prisma服务器,允许你在数据库中读取,写入和实时传输数据。 它应用于应用程序中的数据访问。 datamodel 数据模型的主要用途是定义数据库、定义Prisma client的API基础。

Prisma client操作都是从数据模型得出 对于在数据模型中定义的每个模型中,会自动为你生成以下Prisma client操作:

读取单个数据 读取数据的列表(包括过滤,排序和分页) 写入新数据 更新现有的数据 删除现有的数据 检查是否存在某些数据 有实时更新时,对任何数据写操作 你可以在part3找到所有这些操作。

schema定义语言(SDL) 数据模型在.prisma文件定义好,用了GraphQL SDL语法。使用SDL是因为它与编程语言无关,使用简单,富有表现力和直观。

使用SDL定义数据模型没有严格的技术要求,并且API可能会在将来以其他方式指定数据模型。 如果你想要查看指定数据模型的其他方法,请创建功能请求。

使用datamodel迁移数据模型(可选) 有两种情况创建的数据模型:

手动写入:从头开始手动编写数据模型时,数据模型还用于执行数据库迁移(这仅适用于没有现有数据的新数据库)。 数据模型的模型定义不仅确定了Prisma client的API操作,还映射到数据库(这意味着它们用于以声明方式执行数据库迁移)。 从现有的数据库schema(Introspecting )当你开始将Prisma与现有数据库一起使用时,数据模型将从现有数据库模式派生。 在这种情况下,Prisma不用于执行数据库迁移(相反,你也可以在使用时手动迁移数据库)。 示例 以下是两个简单的数据模型:

type Post { id: ID! @id createdAt: DateTime! @createdAt published: Boolean! @default(value: "false") title: String! author: User! } type User { id: ID! @id email: String @unique name: String! posts: [Post!]! } 这里有几件事情需要注意:

该POST和User模型经由关系(由author和posts字段中定义)连接。 !意味着该模型的相应字段永远不能null。 @unique指令保证不会有两个记录有该字段的值相同。 @default指令设置默认值。 createdAt不可设置,将由Prisma生成,表示创建一个记录的确切时间。还有就是updatedAt表明一个记录的最后更新的时间。 Prisma client 该Prisma client是一个自动生成的库,它取代了API服务器中的传统ORM。 它连接到位于数据库顶部的Prisma服务器:

无缝关系API 该Prisma client是在不同的编程语言,并提供类型安全的数据库访问。相对于传统的ORM,它提供了一个无缝的API与关系数据的工作,连接和交易。

以下是处理在单个事务几个写操作的例子:

创建一个新的User纪录 创建两个新的POST记录 通过posts关系新建两个post并和现有的一个post记录连接到新的User纪录 JavaScript:

.createUser({ name: "Alice", password: "IlikeTurtles", posts: { create: [{ title: "Prisma is the data layer for modern applications", }, { title: "Check out How to GraphQL - The fullstack tutorial for GraphQL", }], connect: { id: "cjli6tknz005s0a23uf0lmlve" } }, }) TypeScript:

.createUser({ name: "Alice", password: "IlikeTurtles", links: { create: [{ description: "The data layer for modern applications", url: "https://www.prisma.io" }, { description: "Fullstack GraphQL tutorial", url: "https://www.howtographql.com" }], connect: { id: "cjli6tknz005s0a23uf0lmlve" } }, }) Go:

password := "IlikeTurtles"
description := "The data layer for modern applications"
url := "https://www.prisma.io"
linkId := "cjli6tknz005s0a23uf0lmlve"
db.CreateUser(&prisma.UserCreateInput{
    Name:     &name,
    Password: &password,
    Links: &prisma.LinkCreateManyWithoutPostedByInput{
        Create: &prisma.LinkCreateWithoutPostedByInput{
            Description: &description,
            Url:         &url,
        },
        Connect: &prisma.LinkWhereUniqueInput{
            ID: &linkId,
        },
    },
})

对于传统的ORM,这样一组写操作需要你手动控制数据库事务。而现在Prisma client可以处理你的操作。

类型安全数据访问 通过你的Prisma client暴露的操作是强类型。 对于任何类型化的编程语言,这意味着你可以获得所有数据库操作的编译时安全性,并且你确切地知道要返回的数据形状。

它还为你提供了令人惊叹的开发人员体验:你可以在编辑器中获得自动补全功能。

所有类型都是自动生成的,因此你无需为此编写任何样板文件。 更改数据模型后,只需重新生成Prisma客户端,所有类型都将更新。

实时更新 将实时事件系统添加到数据库是一项非常复杂的任务。 Prisma客户端允许你潜入任何数据库事件,而无需处理底层基础架构。 你可以通过$ subscribe属性上的生成方法执行此操作。

下面是一个订阅事件的示例,其中创建了在其电子邮件地址中包含gmail的新用户记录:

JavaScript:

prisma.$subsribe.user({ mutation_in: ["CREATED"], email_contains: "gmail" }).node() TypeScript:

prisma.$subsribe.user({ mutation_in: ["CREATED"], email_contains: "gmail" }).node() Go:

// subscriptions for the Prisma Go client will be available soon 原生GraphQL Prisma client可以运行使用$graphql方法对数据库运行GraphQL的Query和Mutation。

这里展示了如何使用原生API GraphQL创建一个新的User记录的例子:

const mutation = mutation ($name: String!){ createUser(name: $name) { id } } const variables = { name: 'Alice' } const result = prisma.$graphql(mutation, variables)

const mutation = mutation ($name: String!){ createUser(name: $name) { id } } const variables: UserCreateInput = { name: 'Alice' } const result: any = prisma.$graphql(mutation, variables)

db.GraphQL( mutation ($name: String!){ createUser(name: $name) { id } } , map[string]interface{}{ "name": "Alice" }) Prisma server Prisma server是连接到数据库的独立基础架构组件:

它需要与你的数据库连接信息和用户名密码来配置部署时。

Prisma server的主要职责是翻译由Prisma client生成的实际的数据库查询请求。

虽然有几种方法可以设置Prisma服务器,但目前推荐的方法是使用Docker。 下面是一个示例Docker Compose文件,用于配置连接到本地MySQL数据库的Prisma服务器:

version: '3' services: prisma: image: prismagraphql/prisma:1.33 restart: always ports:

在Prisma cloud管理服务器

Prisma cloud是一套工具,帮助你管理你的Prisma服务器和服务。它由基于Web的[Pri​​sma的控制台(https://app.prisma.io/),是Prisma CLI的部分。

你可以在任何地方托管Prisma服务器 - 无论是你自己的服务器还是使用你喜欢的云提供商(例如aliyun,AWS,Google Cloud,Digital Ocean,......)。 无论Prisma服务器托管在哪里,你仍然可以通过Prisma Cloud进行管理。 这包括维护任务,如升级Prisma版本。

为了充分利用这些优势,你需要使你的Prisma服务器连接到Prisma cloud。

通过云提供商集成,你还可以直接通过Web UI设置Prisma服务器,而不是操作Docker和云提供商的API / UI。 例如,setup a new Prisma server with a connected database on Heroku.

要在没有设置自己的数据库和Prisma服务器的麻烦的情况下开始使用Prisma,你可以在Prisma Cloud中使用免费的托管Demo服务器。 运行prisma init并选择Demo服务器选项或按照“part1”教程。

Prisma & GraphQL Prisma使用[GraphQL(http://graphql.org/)作为一种通用的数据库抽象,这意味着它可以转变数据库为GraphQL API,使你可以:

读写数据使用GraphQL queries和mutations 使用GraphQL subscriptions接收数据库事件的实时更新 使用GraphQL SDL执行迁移你的数据模型 当Prisma客户端向Prisma服务器发出请求时,它实际上会生成GraphQL操作,这些操作将被发送到Prisma的GraphQL API。 然后,客户端将GraphQL响应转换为预期的结果结构,并从调用的方法返回它。

Prisma services 数据库的GraphQL映射由Prisma服务提供。 每个服务都为数据库提供自己的GraphQL CRUD映射。 GraphQL API是自动生成的,并为服务的数据模型中的每个模型提供CRUD操作。

Prisma服务正在Prisma服务器上运行。 Prisma服务器可以配置为托管多个Prisma服务。

Prisma服务是使用两个组件构成:

prisma.yml:为Prisma services根配置文件(包括服务的端点,密码,数据模型文件的路径,...) datamodel:在数据模型中,可以定义其Prisma的使用来生成数据库的GraphQL API模型。它是一个使用GraphQL SDL语法和存储在一个叫做'datamodel.prisma`的文件中。 一个prisma.yml类似于此:

endpoint: http://localhost:4466 datamodel: datamodel.prisma secret: mysecret42 这是它包含的配置属性:

endpoint:应该部署服务的Prisma服务器的HTTP端点。 此端点公开服务的Prisma API。 datamodel:数据模型的file path,它作为GraphQL CRUD / realtime API的基础。 secret:service secret用于使用基于JWT的身份验证来保护服务的GraphQL API端点。 如果未指定secret,则该服务不需要身份验证。 每个Prisma services只能有一个endpoint。端点是由以下几部分组成:

Host: 你Prisma服务器的host(含协议和端口),例如https://example.com:4466 Service name: 端点URL的第一个路径组件是Prisma服务的名称,例如my-service。 如果未指定服务名称,则默认为default。 Service stage: 端点URL的第二个路径组件是服务的stage。 与服务名称一样,这可以是随机字符串 - 但你通常使用描述部署环境的术语(例如dev,staging,prod,...)。 如果未指定服务stage,则默认为default。 全部放在一起,一个服务可能看起来如下:https://example.com:4466/my-service/dev

Prisma services也可以部署到没有任何路径组件的端点(例如,通过http://localhost:4466),在这些情况下PRISMA采用default。

这意味着http://localhost:4466/default/default总是可以写成http://localhost:4466

端点结构的另一个例外是Prisma云上的Demo服务器,它们在服务名称和阶段之前有一个额外的路径组件。这对应于工作区的名称。例如,如果你的工作区名为john-doe,则端点可能如下所示:http://prisma-eu1.sh/john-doe/my-service/dev

下一节是Prisma创造的Graphql

cisen commented 5 years ago

Prisma GraphQL API

每个Prisma服务都公开一个GraphQL API,其中包含CRUD和服务数据模型中定义的类型的实时操作。 此API称为 Prisma API。

定义Prisma API的类型和操作的GraphQL schema称为 Prisma GraphQL schema。 它由Prisma自动生成。

试试的Prisma的playground

点击这里试试在GraphQL playground 中的Prisma API。

所述Prisma API是基于以下的数据模型:

type Post { id: ID! @id createdAt: DateTime! @createdAt updatedAt: DateTime! @updatedAt isPublished: Boolean! @default(value: "false") title: String! text: String! author: User! } type User { id: ID! @id email: String! @unique password: String! name: String! posts: [Post!]! } 本章介绍所有你需要了解的Prisma API,以及如何可以在各种环境中使用。

概念 本节解释了一些重要的概念,以了解Prisma API。

除非另有说明,本页上的例子是基于以下数据模型的Prisma API:

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 } Prisma GraphQL schema Prisma数据库是定义Prisma API的数据类型和操作的GraphQL schema。 它是自动生成的,并为service的数据模型中指定的模型指定CRUD和实时操作。

节点选择 Prisma API中的许多操作仅影响现有节点的子集,有时仅影响单个节点,例如更新或删除节点。

在这些情况下,你需要一种方法来询问API中的特定节点。 这就是where过滤器参数的用途。 它允许你指定用于选择应应用操作的节点的条件。

可以通过使用@unique指令注释的任何字段选择节点。

以下是需要选择节点的几种情况。

通过其email检索单个User节点:

query { user(where: { email: "alice@prisma.io" }) { id } }

{ "data": { "user": { "id": "cjkawhcz44a4c0a84un9a86wt" } } } 更新单个POST节点的title:

mutation { updatePost( where: { id: "ohco0iewee6eizidohwigheif" } data: { title: "GraphQL is awesome" } ) { id } }

{ "data": { "updatePost": { "id": "ohco0iewee6eizidohwigheif" } } } 一次性完成对POST节点的批量更新published(见[批量Mutation(#批量Mutation)):

mutation { updateManyPosts( where: { id_in: ["ohco0iewee6eizidohwigheif", "phah4ooqueengij0kan4sahlo", "chae8keizohmiothuewuvahpa"] } data: { published: true } ) { count } }

{ "data": { "updateManyPosts": { "count": 3 } } } 批量Mutation 节点选择概念的一个应用是Prisma API暴露的批量Mutation。 批量更新或删除已经过优化,可以更改大量节点。 因此,这些Mutation仅返回受影响的节点数,而不是特定节点上的完整信息。

例如,MutationupdateManyPosts和deleteManyPosts提供了一个where参数来选择特定节点,并返回一个带有受影响节点数的count字段(参见例子)。

mutation { deleteManyUsers( where: { email_in: ["alice@prisma.io", "bob@prisma.io"] } ) { count } }

{ "data": { "deleteManyUsers": { "count": 2 } } } 连接(Relay Pagination & Aggregations) PRISMA提供两种方法通过关系字段检索节点列表:

简单的关系查询(direct节点检索):

query { posts { id title published } } Connection的查询:

query { postsConnection { edges { node { id title published } } } } 与直接返回节点列表的简单关系查询相比,连接查询基于Relay Connection模型。 除了分页信息之外,Prisma API中的连接还具有高级功能,如聚合。

例如,虽然posts查询允许你选择特定的Post节点,按某些字段对它们进行排序并对结果进行分页,但postsConnection查询还允许你计算所有未发布的帖子:

query { postsConnection {

aggregate 允许执行常见的聚合操作

aggregate {
  count
}
edges {
  # 每个“node”指的是一个“Post”节点
  node {
    title
  }
}

} }

{ "data": { "postsConnection": { "aggregate": { "count": 1 }, "edges": [ { "node": { "title": "Watch all the talks from GraphQL Europe: bit.ly/gql-eu" } } ] } } } 查看这些功能的要求,了解即将实现的聚合操作。

Transactions&嵌套Mutation Prisma API中不是批处理操作的单个Mutation总是以事务方式执行,即使它们包含许多可能分布在多个相关节点上的操作。 这对于在多种类型上执行多个数据库写入的嵌套Mutation特别有用。

嵌套Mutation是触及至少两个节点的Mutation,每个节点具有不同类型。 这是一个简单的例子:

mutation { createUser(data: { name: "Sarah" posts: { create: [ { title: "GraphQL is great" } { title: "Prisma is a data access layer" } ], connect: [ { id: "cjk1e3t7i1ark0b299pvrge5m" } ] } }) { id posts { id } } } 这种Mutation总共涉及了 4个节点:

它creates 1个新的User节点。 它creates 2个POST节点。这两个POST节点也是直接connected到新的User节点。 它connects新的User节点到1个现有POST节点。 这种Mutation总进行6个单操作:

Creating 1个User节点。 Creating 2个POST节点。 Connecting 3个POST节点到新的User节点。 如果上述任何一项操作的失败(例如,由于的违反@unique约束),则_全部_Mutation回滚!

Mutation是事务性的,意味着它们是原子的和孤立的。 这意味着在相同嵌套Mutation的两个单独动作之间,没有其他Mutation可以改变数据。 在处理完整Mutation之前,也不能观察到单个动作的结果。

级联删除 Prisma支持数据模型中关系的不同删除行为。 有两种主要的删除行为:

CASCADE:当删除与一个或多个其他节点有关的节点时,也会删除这些节点。 SET_NULL:当删除与一个或多个其他节点有关的节点时,引用已删除节点的字段将设置为null。 如上所述,你可以为相关节点指定专用删除行为。 这就是@ relation指令的onDelete参数。

请看下面的例子:

type User { id: ID! @id comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE) blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE) } type Blog { id: ID! @id comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE) owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL) } type Comment { id: ID! @id blog: Blog! @relation(name: "Comments", onDelete: SET_NULL) author: User @relation(name: "CommentAuthor", onDelete: SET_NULL) } 让我们研究一下三种类型的删除行为:

当一个User节点被删除,

所有相关的Comment节点将被删除。 相关Blog节点将被删除。 当一个Blog节点被删除,

所有相关的Comment节点将被删除。 相关User节点将有其blog字段设置为null。 当一个Comment节点被删除,

相关Blog节点继续存在,并且删除Comment节点从其comments列表中删除。 相关User节点继续存在,并且删除Comment节点从其comments列表中删除。 Authentication认证 可以使用prisma.yml中的服务secret(指定为secret属性)来保护Prisma API:

secret: my-secret-42 作为Prisma services的开发者,你可以选择自己的服务密码。当服务被使用包含secret属性prisma.yml部署,该服务的Prisma API将需要通过Service token(JWT)的认证:

获得业务令牌最简单的方法是通过在你的prisma.yml所在的同一目录中运行prisma token命令:

$ prisma token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJkZW1vQGRldiIsInJvbGVzIjpbImFkbWluIl19LCJpYXQiOjE1MzI1ODgzNzAsImV4cCI6MTUzMzE5MzE3MH0.Nv8coqsiwdwoSfWCBJHYfnr0WK2GRyqO5xTN6Q3IVkw 将所生成的令牌复制到到所述Prisma API的HTTP请求Autorization头:

curl 'YOUR_PRISMA_ENDPOINT' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer YOUR_SERVICE_TOKEN' \ --data-binary '{"query":"mutation { createUser(data: { name: "Sarah" }) { id } }"' 注意

不要将服务机密和服务“令牌”与用于保护Prisma服务器的Management API的Prisma Management API密钥混淆。

Service secret Service secret是由Prisma services的开发者指定的字母数字的随机字符串。在prisma.yml的secret属性中设置:

secret: my-secret-42 Service token Service token遵守JSON web token(JWT)规范(RFC 7519):

"JSON Web Token"(JWT)是一种紧凑的,URL安全的方式,用于表示要在双方之间传输的声明.JWT中的声明被编码为JSON对象,用作JSON Web签名的有效负载(JWS) )结构或作为JSON Web加密(JWE)结构的明文,使声明能够通过消息验证代码(MAC)进行数字签名或完整性保护和/或加密。“

一个JWT有以下三个部分组成:

Header: 标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的散列算法(在Prisma服务令牌的情况下为HS256)。

{ "alg": "HS256", "typ": "JWT" } Payload:payload包含声明。 声明是关于实体(通常是用户)和其他数据的声明。 以下是部署到dev阶段的名为demo的服务的样子:

{ "data": { "service": "demo@dev", "roles": [ "admin" ] }, "iat": 1532530208, "exp": 1533135008 } } Signature:签名用于验证消息在此过程中未发生更改。 要创建签名部分,你必须采用编码标头,编码的有效负载,秘密,标头中指定的算法,并对其进行签名。 例如,如果要使用HMAC SHA256算法,将按以下方式创建签名:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 因此,JWT通常是这样的:xxxxx.yyyyy.zzzzz

了解更多关于JWTs 这里。

Claims 所述JWT必须包含以下的Claims:

Issued at:iat字段包含一个Unix时间戳,其中包含生成令牌的确切时间。 Expiration date:exp字段包含表示令牌到期日期的Unix时间戳。 服务令牌完全有效一周。 Service information:data字段是一个有两个键的对象 - service字段指定服务的name和stage - “roles”字段包含使用该令牌授予的访问权限。 将来可能会通过引入一个角色概念来支持更细粒度的访问控制,例如[“write:Log”,“read:*”] 这里有一个JWT的示例Payload:

{ "data": { "service": "myservice@prod", "roles": [ "admin" ] }, "iat": 1532530208, "exp": 1533135008 } 发送service token 该service token是_bearer_token:

_ _bearer_token:一种安全令牌,其属性是拥有该令牌的任何一方(“持票人”)可以以任何其他拥有该令牌的方式使用该令牌。 使用不记名令牌不需要持票人证明拥有加密密钥材料(占有证明)。

它需要由OAuth2.0的授权框架规格中指定要被发送到Prisma API。

当发送在/由HTTP定义的授权请求报头字段1.1 [RFC2617]的访问令牌,客户端使用的Bearer认证方案来发送访问令牌。

例如:

GET /resource HTTP/1.1 Host: server.example.com Authorization: Bearer mF_9.B5f-4.1JqM 此方案的Authorization标头字段的语法遵循[RFC2617]第2节中定义的Basic方案的使用。 请注意,与Basic一样,它不符合[RFC2617]第1.2节中定义的通用语法,但与为HTTP 1.1 [HTTP-AUTH]开发的通用身份验证框架兼容,尽管它不遵循首选实践 其中概述的是为了反映现有的部署。 Bearer凭证的语法如下:

b64token = 1( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) "=" credentials = "Bearer" 1*SP b64token Clients SHOULD make authenticated requests with a bearer token using the Authorization request header field with the Bearer HTTP authorization scheme. Resource servers MUST support this method. 例:保护你的Prisma API 下面是一个例子,如何保护你的Prisma API,使对请求身份验证。

未受保护的Prisma API 假设你开始通过以下服务配置配置一个新的Prisma services:

datamodel.prisma

type User { id: ID! @id name: String! } ****prisma.yml

endpoint: https://eu1.prisma.sh/john-doe/demo/dev # deployed to a Prisma Demo server datamodel: datamodel.prisma 在这个例子中,假设该服务被部署到一个演示服务器,因此可在公共互联网上。

当使用上述服务配置部署服务时,知道服务端点的每个人都能够向其API发送查询和Mutation,这意味着他们可以有效地对你的数据库执行任意读取和写入:

curl 'https://eu1.prisma.sh/jane-doe/demo/dev' \ -H 'Content-Type: application/json' \ --data-binary '{"query":"mutation { createUser(data: { name: "Sarah" }) { id }}"}' 当使用GraphQL Playground,需要跟Prisma API请求时不需要对HTTP头进行设置。

保护Prisma API 你需要为了你的Prisma API做的唯一的事情是在prisma.yml设置服务密码:

endpoint: https://eu1.prisma.sh/john-doe/demo/dev # deployed to a Prisma Demo server datamodel: datamodel.prisma secret: my-secret-42 要应用此更改,你还需要重新部署服务:

prisma deploy 如果你现在正在试图使用curl重新发送上述HTTP请求:

curl 'https://eu1.prisma.sh/jane-doe/demo/dev' \ -H 'Content-Type: application/json' \ --data-binary '{"query":"mutation { createUser(data: { name: "Sarah" }) { id }}"}' 你会收到以下错误:

{ "errors" : [ { "message" : "Your token is invalid. It might have expired or you might be using a token from a different project.", "code" : 3015, "requestId" : "eu1:api:cjk28t8e55tld0b296sr2ey6v" } ] }⏎ 对于针对Prisma API发出的所有请求,无论是请求的查询,Mutation还是订阅,都会发生这种情况。 如果在GraphQL Playground中未设置HTTP标头,请求将失败并显示相同的错误消息:

要解决此问题,你需要在HTTP请求的Authorization标头字段中包含服务标记。 你可以使用Prisma CLI获取服务令牌:

prisma token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJkZW1vQGRldiIsInJvbGVzIjpbImFkbWluIl19LCJpYXQiOjE1MzI1MzAyMDgsImV4cCI6MTUzMzEzNTAwOH0.FM6haUilhi89-C-2h7asV3-Ot6NQrs1qoaKL-wPjj04 CLI打印的令牌需要在Authorization标头字段中设置,并以Bearer为前缀:

curl 'https://eu1.prisma.sh/jane-doe/demo/dev' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJkZW1vQGRldiIsInJvbGVzIjpbImFkbWluIl19LCJpYXQiOjE1MzI1MzAyMDgsImV4cCI6MTUzMzEzNTAwOH0.FM6haUilhi89-c-2h7asV3-Ot6NQrs1qoaKL-wPjj04' \ --data-binary '{"query":"mutation { createUser(data: { name: "Sarah" }) { id }}"}' 同样地,在GraphQL Playground,你需要(JSON格式)在左下角的HTTP header窗格设置它:

{ "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJkZW1vQGRldiIsInJvbGVzIjpbImFkbWluIl19LCJpYXQiOjE1MzI1MzAyMDgsImV4cCI6MTUzMzEzNTAwOH0.FM6haUilhi89-c-2h7asV3-Ot6NQrs1qoaKL-wPjj04M" } 如果你使用了PRISMA playground命令打开Playground上,Prisma的CLI自动注入了一个有效的服务标识Authorization头。

Queries 如何生成Prisma API中的query? 一个Prisma services的GraphQL API在Prisma的GraphQL schema中指定。Prisma GraphQL schema是基于数据模型自动生成的:

在Query类型Prisma的GraphQL schema定义了所有的Prisma API接受查询。

作为一个例子,考虑下面的数据模型:

type User { id: ID! @id name: String! } 这是Prisma会自动生成的Query的type:

type Query { users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! user(where: UserWhereUniqueInput!): User usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection! } 对于datamodel中的每种类型,都会生成三个查询。 以上面的“User”类型为例,这些查询是:

user:检索单个User节点 users:检索User节点列表(作为一个对象查询) usersConnection:检索User节点列表(作为聚合查询) 要详细检查你的Prisma API的所有可用的操作,你可以读取Prisma services的GraphQL schema。它可以用GraphQL CLI下载:

graphql get-schema --endpoint YOUR_PRISMA_ENDPOINT --output prisma.graphql --no-all 了解Prisma API具体功能的另一种方法是探索GraphQL Playground中自动生成的API文档。 你可以通过单击Playground右边缘的绿色 SCHEMA - 按钮来执行此操作:

本页数据模型例子 本节的所有示例查询基于此数据模型配置Prisma services:

type Post { id: ID! @id title: String! published: Boolean! author: User! } type User { id: ID! @id age: Int email: String! @unique name: String! accessRole: AccessRole posts: [Post!]! } enum AccessRole { USER ADMIN } Query查询的概念 Prisma API提供两种查询:

对象查询:获取特定对象类型的单个或多个节点。 聚合查询:公开聚合和“Relay”兼容连接等高级功能,实现强大的分页模型。 下面的概念也得记住有用:

分层(或嵌套)查询:跨关系获取数据。 查询参数:允许筛选,排序,分页等。 对象查询 我们可以使用对象查询来获取无论是单个节点,或某个对象类型节点的列表。

在这里,我们使用posts查询来获取POST节点列表。在响应中,我们只包括id和每个POST节点的title:

query { posts { id title } } 我们也可以查询特定POST节点。请注意,我们使用了where参数选择节点:

query { post(where: { id: "cixnen24p33lo0143bexvr52n" }) { id title published } } 因为User是在我们的数据模型另一种类型,users是另一种可用的查询。同样,我们可以使用where参数指定返回的用户条件。在这个例子中,我们筛选出age比18更高的所有User节点:

query { users(where: { age_gt: 18 }) { id name } } 这也适用于跨关系。在这里,我们要提取那些节点中age比18更高的author的POST:

query { posts(where: { author: { age_gt: 18 } }) { id title author { name age } } } 聚合查询 对象查询直接返回节点列表。在特殊情况下,或者使用高级功能时,使用聚合查询是较好的选择。它们的附加信息(完全符合)Relay connections。

Relay connections的核心思想是提供关于在数据图中的edges元信息。例如,每个edge不仅具有关于对应的对象访问信息(node),而且还有cursor,允许实现强大的基于指针的分页。

在这里,我们撷取了使用postsConnection查询所有POST节点。请注意,我们还要求每个edge附带cursor:

Fetch all posts

query { postsConnection { edges { cursor node { id title } } } } 聚合查询还通过字段公开聚合功能aggregate:

含有"GraphQL"标题的所有帖子

query { postsConnection(where: { title_contains: "GraphQL" }) { aggregate { count } } } 更多聚合将随时间被添加。找到[这里]路线图的更多信息(https://github.com/prisma/prismagraphql/issues/1312)。

跨关系查询数据 数据模型中的每个可用关系都会为其连接的两个模型的查询添加一个新字段。

在这里,我们取一个特定的User节点,并使用posts获取与其相关的全部POST节点:

query { user(where: { id: "cixnekqnu2ify0134ekw4pox8" }) { id name posts { id published } } } user.posts行为与posts查询相同,因为它允许你指定你感兴趣的Post类型的字段。

查询参数 在整个Prisma API中,你将找到可以提供的查询参数,以进一步控制查询响应。它可以是以下任何一种:

Ordering排序:使用orderBy对任何字段值排序节点 Filtering过滤:使用标量或关系过滤器选择查询中的节点where Pagination分页:使用first和before,last和after,和在查询节点切片skip 这些查询参数可以结合起来,实现非常具体的查询响应。

排序 查询的所有节点可以提供orderBy参数的类型的每个字段:ORDERBY:<字段> _ASC或ORDERBY:<字段> _DESC。

按title正序排序所有Post节点的列表:

query { posts(orderBy: title_ASC) { id title published } } 按published倒序排序所有Post节点的列表:

query { posts(orderBy: published_DESC) { id title published } } 不必在实际查询中选择要排序的字段。如果未指定排序,则会按id字段自动按顺序排序响应。

注意

这是目前无法排序的多个字段

过滤 当查询类型的所有节点上,你可以提供不同的参数给where参数根据你的要求来约束数据。可用选项取决于在相关类型上定义的标量和关系字段。

应用单一过滤器 如果只为参数提供一个参数where,则查询响应将仅包含符合此约束的节点。可以使用AND/OR组合多个过滤器,有关详细信息,请参见下文。

通过一个具体的值过滤 筛选查询响应的最简单方法是为要筛选的特定字段提供具体值。

查询尚未published的 所有POST节点:

query { posts(where: { published: false }) { id title published } } 查询所有User节点特定的name:

query { users(where: { name: "Alice" }) { id } } 查询所有具有特定age 的User节点:

query { users(where: { age: 30 }) { id } } 高级过滤条件 根据要筛选的字段类型,你可以访问可用于筛选查询响应的不同高级条件。

查询所有POST节点,其title是一个指定的字符串:

query { posts(where: { title_in: ["My biggest Adventure", "My latest Hobbies"] }) { id title published } } 查询所有User节点,其age小于42 :

query { users(where: { age_lt: 42 }) { id } } 关系过滤器 对于一对一关系,你可以通过嵌入相应的参数来定义相关节点上的条件where。

查询其中author有USER访问角色的所有POST节点:

query { posts(where: { author: { accessRole: USER } }) { title } } 对于一对多关系,另外三个参数可用:every,some和none,定义的条件应符合every,some或none相关节点。

查询至少有一个Post 节点published的所有User节点:

query { users(where: { posts_some: { published: true } }) { id posts { published } } } 对于一对一或多对多关系的嵌套参数中也可以使用关系过滤器。

查询like的author不是ADMIN访问角色的POST所有User节点:

query { users(where: { likedPosts_none: { author: { accessRole: ADMIN } } }) { name } } likedPosts不是上述数据模型的一部分,但可以很容易地通过将相应字段添加到User:likedPosts: [Post!]! @relation(name: "LikedPosts")。我们还提供了一个name关系来解决我们原本会创建的歧义,因为有两个关系字段针对Post该User类型。

结合使用多种过滤器 你可以使用过滤器组合程序OR``AND和NOT创建的过滤条件的任意逻辑组合:

对于要评估为“true”的AND过滤器,all嵌套条件必须为“true”。 对于要评估为“true”的“OR”过滤器,至少有一个嵌套条件必须为“true”。 对于要评估为“true”的NOT过滤器,所有嵌套条件都必须为“false”。 使用OR``AND和 NOT 让我们先从一个简单的例子:

查询published 和其title是一个字符串的给定列表中的所有POST节点:

query { posts(where: { AND: [{ title_in: ["My biggest Adventure", "My latest Hobbies"] }, { published: true }] }) { id title published } } OR,AND和NOT每个接受一个list作为输入,其中每个列表项是一个对象,因此需要用大括号包裹。从上述查询的样品过滤器条件包含两个滤波器对象:

{title_in: ["My biggest Adventure", "My latest Hobbies"]} {published: true} 只有当针对POST节点这两个过滤条件符合时,该节点将被包含在响应。

AND,OR和NOT过滤器任意组合 你可以组合甚至嵌套过滤器组合器AND,OR并NOT创建过滤条件的任意逻辑组合。

在提供的列表中的所有POST节点查询要么published 和其title是在给定的字符串,或者有具体的id:

query { posts(where: { OR: [{ AND: [{ title_in: ["My biggest Adventure", "My latest Hobbies"] }, { published: true }] }, { id: "cixnen24p33lo0143bexvr52n" }] }) { id title published } } 注意我们如何将AND组合嵌套在OR组合器中。

局限性 目前,标量列表过滤器JSON滤波器不可用。加入GitHub相应功能的讨论。

分页 当查询的特定对象类型的所有节点,可以提供参数,让你进行分页查询响应。

用first和last实现从前往后查询和从后往前查询 分页允许你请求一定数量的节点。 你可以通过节点向前或向后搜索并提供可选的起始节点:

从前往后查询,用first; 指定起始节点用after。 从后往前查询,使用last; 指定起始节点用before。 你不能把first和before或last和after结合起来。 如果你在查询中这样做,before或after将被简单地忽略,只应用first或last(在列表的开头或结尾,取决于你使用的是哪一个)。

请注意,当你只有5条数据时,你可以查询更多比如10条数据,也不会有错误消息。

跳过skip 你还可以通过提供skip参数跳过任意方向的任意数量的节点:

当使用first时,skip会跳过列表开头的元素 当使用last时,skip跳过列表末尾的元素 例子 对于下面的例子,我们假设有三十条数据:

查询前3个节点(从前往后):

query { posts(first: 3) { id title } } 从第6条数据开始,取5条数据(从前往后):

query { posts( first: 5 skip: 5 ) { id title } } 查询的最后3个节点(从后往前):

query { posts(last: 3) { id title } } 从倒数第4条数据取到倒数第10条(寻从后往前):

query { posts( last: 7 skip: 3 ) { id title } } 查询id为cixnen24p33lo0143bexvr52n的后面3个数据:

query { posts( first: 3 after: "cixnen24p33lo0143bexvr52n" ) { id title } } 查询id为cixnen24p33lo0143bexvr52n的数据再跳过3条,取5个:

query { posts( first: 5 after: "cixnen24p33lo0143bexvr52n" skip: 3 ) { id title } } cixnen24p33lo0143bexvr52n当id的那条数据的前面5条:

query { posts( last: 5 before: "cixnen24p33lo0143bexvr52n" ) { id title } } cixnen24p33lo0143bexvr52n当id的那条数据的前面5条数据略过,再取3个:

query { posts( last: 3 before: "cixnen24p33lo0143bexvr52n" skip: 5 ) { id title } } Mutations 如何生成Prisma API中的mutation? 一个Prisma services的GraphQL API在Prisma的GraphQL schema中指定。Prisma GraphQL schema是基于数据模型自动生成的:

Prisma GraphQL schema中的Mutation类型中定义了所有的Prisma API接受的Mutation。

作为一个例子,考虑下面的数据模型:

type User { id: ID! @id name: String! } 这是Prisma会自动生成的Mutation:

type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User! updateManyUsers(data: UserUpdateInput!, where: UserWhereInput!): BatchPayload! deleteManyUsers(where: UserWhereInput!): BatchPayload! } 对于你的数据模型每一种type,都形成了6个Mutation。以上述User类型作为示例,这些Mutation是:

createUser:创建一个新的User节点 updateUser:更新现有User节点 deleteUser:删除现有User节点 upsertUser:更新现有User节点; 如果它不存在,创建一个新的User节点 updateManyUsers:更新很多User节点 deleteManyUsers:一次性删除很多User节点 要详细检查你的Prisma API的所有可用的操作,你可以读取Prisma services 中的Prisma GraphQL schema。它可以用GraphQL CLI下载:

graphql get-schema --endpoint YOUR_PRISMA_ENDPOINT --output prisma.graphql --no-all 了解你的Prisma API的具体能力的另一种方式是通过打开GraphQL Playground内自动生成的API文档。你可以通过点击在Playground的右边按钮SCHEMA:

数据模型本页例子 本节的所有Mutation例子基于这个数据模型的Prisma services:

type Post { id: ID! @id title: String! published: Boolean! @default(value: "false") author: User } type User { id: ID! @id age: Int email: String! @unique name: String! posts: [Post!]! } Mutation概念 Prisma API提供了三种Mutation:

对象Mutation:创建,更新,更新插入或删除某[对象类型]的单个节点。 嵌套Mutation:创建,更新,更新插入,删除,连接,断开连接两个或多个相关类型的多个节点。 批量Mutation:更新和删除[对象类型]的许多节点。 当使用Prisma API时,下面的概念也记住有用:

Relation Mutation:连接和断开两个节点的关系(这是通过嵌套Mutation完成)。 Input types:Prisma API遵循GraphQL Mutation设计的最佳实践,并从'data`这个单个对象中接受Mutation参数。 对象Mutation 你可以使用对象Mutation来修改特定模型的单个节点。

创建节点 你可以使用createMutation创建新的节点。这些Mutation采用一个称为data的输入对象包裹所有必填字段相应类型的字段。

创建一个新的User节点:

mutation { createUser( data: { age: 42 email: "alice@prisma.io" name: "Alice" } ) { id name } } 更新节点 你可以使用updateMutation改变一个节点的一个或多个字段值。要更新的节点[选择]使用where参数,经由data参数提供新的值。节点可以通过@unique指令标注的任何字段进行选择。

更改User节点的name:

mutation { updateUser( data: { name: "Alice" } where: { id: "cjcdi63j80adw0146z7r59bn5" } ) { id name } } 通过其email标识更改User节点的name:

mutation { updateUser( data: { name: "Alice" } where: { email: "alice@prisma.io" } ) { id name } } Upserting节点 当你想要么更新现有的节点,要么创建一个新的节点,你可以使用_upsert_Mutation。

根据email更新User,如果没有该User则创建一个新的User:

mutation { upsertUser( where: { email: "alice@prisma.io" } create: { email: "alice@prisma.io" age: 42 name: "Alice" } update: { age: 42 } ) { name } } 删除节点 要删除节点,需要先选择节点,在deleteMutation中使用where参数。

通过id删除User节点:

mutation { deleteUser(where: { id: "cjcdi63l20adx0146vg20j1ck" }) { id name email } } 通过email删除User节点:

mutation { deleteUser(where: { email: "cjcdi63l20adx0146vg20j1ck" }) { id name email } } 嵌套Mutation 你可以在使用create和updateMutation的同时修改整个关系的节点。这被称为嵌套Mutation,其执行方式为[事务]。

嵌套Mutation:

create update upsert delete connect disconnect 我们提供一系列例子,罗列出所有可能出现的情况。

我们建议使用GraphQL Playground尝试不同的嵌套Mutation的行为。

创建和连接相关节点 你可以使用嵌套输入对象字段内的connectMutation连接到一个或多个相关的节点。

创建一个新的POST节点,并通过唯一email字段将其连接到现有的User节点:

mutation { createPost(data: { title: "This is a draft" author: { connect: { email: "alice@prisma.io" } } }) { id author { name } } } 如果你在author中提供create参数,而不是connect,你会create一个新的User节点,同时新的POST节点将连接到它:

mutation { createPost(data: { title: "This is a draft" author: { create: { name: "Bob" email: "bob@prisma.io" age: 42 } } }) { id author { name } } } 当使用createUser而不是createPostMutation,实际上你可以同时创建并连接到多个'POST节点,因为User和POST`有一对多关系。

创建一个新的User节点,并直接将其连接到几个新创建的POST节点:

mutation { createUser( data: { name: "Bob" email: "bob@prisma.io" age: 42 posts: { create: [ { published: true title: "Hello World" }, { title: "GraphQL is great" } ] } } ) { id posts { id } } } 创建一个新的User节点,并直接将其连接到几个新创建的和现有的POST节点:

mutation { createUser( data: { name: "Bob" email: "bob@prisma.io" age: 42 posts: { create: [ { published: true title: "Hello World" }, { title: "GraphQL is great" } ] connect: [ { id: "cjcdi63j80adw0146z7r59bn5" }, { id: "cjcdi63l80ady014658ud1u02" } ] } } ) { id posts { id } } } Updating和upserting关系节点 当更新的节点时,可以同时更新一个或多个关系的节点。

更新属于某一User节点的POST节点的title:

mutation { updateUser( data: { posts: { update: [ { where: { id: "cjcf1cj0r017z014605713ym0" } data: { title: "Hello World" } } ] } } where: { id: "cjcf1cj0c017y01461c6enbfe" } ) { id posts { id } } } update接受列表数据并以where和data字段的参数更新相关字段。

更新某个POST节点让它连接到现有User节点的name,如果没有就创建新User节点:

嵌套upserting工作方式类似:

mutation { updatePost( where: { id: "cjcf1cj0r017z014605713ym0" } data: { author: { upsert: { where: { id: "cjcf1cj0c017y01461c6enbfe" } update: { id: "cjcdi63l80ady014658ud1u02" name: "Alice" } create: { email: "alice@prisma.io" name: "Alice" age: 42 } } } } ) { id author { id } } } 从User节点断开一个POST节点:

mutation { updateUser( where: { id: "cjcf1cj0c017y01461c6enbfe" } data: { posts: { disconnect: [ { id: "cjcdi63l80ady014658ud1u02" } ] } } ) { id posts { id } } } 删除相关节点 当更新的节点时,你可以在同时删除一个或多个相关节点。

删除属于某User节点的POST节点:

mutation { updateUser( data: { posts: { delete: [ { id: "cjcf1cj0u01800146jii8h8ch" }, { id: "cjcf1cj0u01810146m84cnt34" } ] } } where: { id: "cjcf1cj0c017y01461c6enbfe" } ) { id } } scalar list Mutation 当对象类型的字段具有标量列表作为其类型时,可以使用许多特殊的Mutation。

在下面的数据模型,User类型有三个这样的字段:

type User { id: ID! @id scores: [String!]! @scalarList(strategy: RELATION) # scalar list for integers friends: [String!]! @scalarList(strategy: RELATION) # scalar list for strings coinFlips: [String!]! @scalarList(strategy: RELATION) # scalar list for booleans } 创建节点 当创建User的一个新的节点,可以使用set提供用于每个标量字段的值的列表。

创建一个新的User并设置scores,friends和coinFlips值:

mutation { createUser(data: { scores: { set: [1, 2, 3] } friends: { set: ["Sarah", "Jane"] } throws: { set: [false, false] } }) { id } } 更新节点 当更新类型的现有节点User,可以在标量列表字段中进行许多附加操作(如scores: [Int!]!!):

set:用全新的列表覆盖现有的列表。 push(即将推出):在列表中的任意位置添加一个或多个元素。 pop(即将推出):删除列表开头或末尾的一个或多个元素。 remove(即将推出):删除列表中匹配给定过滤器的所有元素。 push,pop和remove尚未实现。如果你感兴趣,你可以在这里预览。

Each scalar list field takes an object with a set field in an update mutation. The value of that field is a single value or a list of the corresponding scalar type.

设置现有User节点的scores到[1]:

mutation { updateUser( where: { id: "cjd4lfdyww0h00144zst9alur" } data: { scores: { set: 1 } } ) { id } } 设置现有User节点的scores到[10,20,30]:

mutation { updateUser( where: { id: "cjd4lfdyww0h00144zst9alur" } data: { scores: { set: [10,20,30] } } ) { id } } 批量Mutation 批量Mutation可用于一次更新或删除多个节点。返回的数据只包含受影响的节点的count。

注意

批量Mutation****不适用于[Subscriptions]事件!

对于许多更新的节点,你可以使用where参数选择受影响的节点,而用'data`输入新的值。所有节点都将更新为相同的值。

设置在创建于2017年的所有未发表的'POST节点published`字段为TRUE:

Mutation{ mutation { updateManyPosts( where: { createdAt_gte: "2017" createdAt_lt: "2018" published: false } data: { published: true } ) { count } } 删除其中author的name是Alice的所有未发布POST节点:

mutation { deleteManyPosts( where: { published: false author: { name: "Alice" } } ) { count } } Subscriptions Prisma API中的Subscriptions如何生成? 一个Prisma services的GraphQL API在Prisma的GraphQL schema中指定。Prisma GraphQL schema是基于数据模型自动生成的:

Prisma GraphQL schema中的Subscriptiontype定义了所有的Prisma API接受的subscription。

作为一个例子,考虑下面的数据模型:

type User { id: ID! @id name: String! } 这是Prisma会生成的Subscriptiontype:

type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload } 对于你的数据模型每一个类型,生成一个Subscriptions。以上述Usertype为例,此Subscriptions是:

user:每当创建,更新或删除User节点时触发。(除[批量Mutation]时)。 要详细检查你的Prisma API的所有可用的操作,你可以读取Prisma services Prisma的GraphQL schema。它可以用GraphQL CLI下载:

graphql get-schema --endpoint YOUR_PRISMA_ENDPOINT --output prisma.graphql --no-all 了解你的Prisma API的具体能力的另一种方式是通过查看GraphQL Playground内自动生成的API文档。你可以通过点击在Playground的右边按钮链接SCHEMA:

本例的datamodel 本节的所有例子Subscriptions基于这个datamodel配置的Prisma services:

type Post { id: ID! @id title: String! published: Boolean! @default(value: "false") author: User } type User { id: ID! @id name: String! posts: [Post!]! } 了解Prisma的的Subscriptions API Prisma允许你订阅三种不同类型的事件(数据模型中的每种类型)。以Post上面数据模型中的类型为例,这些事件是:

创建一个新的'Post`节点 更新现有的Post节点 删除现有的Post节点 注意

Subscriptions不支持[批量Mutation]。

Subscription类型的对应定义如下所示(在此定义可在Prisma的GraphQL schema中找到):

type Subscription { post(where: PostSubscriptionWhereInput): PostSubscriptionPayload } 下面是一个Subscriptions操作:

subscription { post { node { id title } } } 如果没有通过where参数进一步约束,则post订阅将针对上述所有事件触发。PostSubscriptionPayload来自服务器的消息中包含哪些字段取决于事件的类型。

过滤特定事件 where参数允许用户指定监听什么事件,也许用户只希望出现某些更新的时候监听,比如:

...删除一个POST时 ...一个其中title包含特定关键字的POST创建时 这些类型的约束可以使用where参数来表示。该类型的where是PostSubscriptionWhereInput:

input PostSubscriptionWhereInput {

Filter for a specific mutation:

CREATED, UPDATED, DELETED

mutation_in: [MutationType!]

Filter for a specific field being updated

updatedFields_contains: String updatedFields_contains_every: [String!] updatedFields_contains_some: [String!]

Filter for concrete values of the Post being mutated

node: PostWhereInput

Combine several filter conditions

AND: [PostSubscriptionWhereInput!] OR: [PostSubscriptionWhereInput!] } 上面提到的两个例子可与Prisma API中的以下Subscriptions来表示:

Only fire for deleted posts

subscription { post(where: { mutation_in: [DELETED] }) {

... we'll talk about the selection set in a bit

} }

Only fire when a post whose title contains "GraphQL" is created

subscription { post(where: { mutation_in: [CREATED] node: { title_contains: "GraphQL" } }) {

... we'll talk about the selection set in a bit

} } 探索Subscriptions的选择集 你现在已经很好地了解了如何订阅你感兴趣的活动。但是,你现在如何得知与事件相关的数据?

该PostSubscriptionPayload类型定义了你可以在post订阅中请求的字段。这是它的样子:

type PostSubscriptionPayload { mutation: MutationType! node: Post updatedFields: [String!] previousValues: PostPreviousValues } 下面是根据触发Subscriptions该事件的每个字段值的概述:

mutation node previousValues updatedFields
CREATE 创建的节点 null null
UPDATE 更新节点的新值 更新节点前的值 更新的字段的字符串列表
DELETE null 被删除的节点 null

在下文中,我们队每个字段进行更详细的讨论。

Mutation 该mutation字段类型是MutationType。MutationType是一个有三个值的枚举enum类型:

enum MutationType { CREATED UPDATED DELETED } 因此,PostSubscriptionPayload类型的mutation字段包含发生了什么类型的信息。

node 该node字段具有Post类型。它表示创建或更新的Post元素,并允许你检索有关它的更多信息。

请注意,对于DELETED-mutations,node将永远是null。如果你需要了解有关已删除Post节点的更多详细信息,可以使用该previousValues字段(稍后会详细介绍)。

updatedFields updatedFields的类型为[String!]。

你可能对UPDATED-mutations 感兴趣的一条信息是哪些字段已更新为Mutation。这就是该updatedFields领域的用途。

假设一个clientSubscriptions与以下SubscriptionsPrisma API:

subscription { post { updatedFields } } 现在,假设服务器接收以下Mutation更新指定POST的title:

mutation { updatePost( where: { id: "..." } data: { title: "Prisma is the best way to build GraphQL servers" } ) { id } } 然后,订阅的client端将收到以下Payload:

{ "data": { "post": { "updatedFields": ["title"] } } } 这是因为Mutation只更新了Post节点的title字段 - 没有别的。

previousValues previousValues是PostPreviousValues的类型,和POST本身看起来非常相似:

type PostPreviousValues { id: ID! title: String! } 它基本上是一个镜像字段的辅助类型Post。

previousValues仅用于UPDATED- 和 - DELETEDMutation。对于CREATED-mutations,它将永远是null(出于同样的原因,节点null用于DELETED-mutations)。

将所有内容放在一起 再考虑前面示例的updatePostMutation。但是现在假设,订阅查询包括所有我们刚才讨论的字段:

subscription { post { mutation updatedFields node { title } previousValues { title } } } 以下是服务器在执行之前的Mutation后推送到客户端的payload:

{ "data": { "post": { "mutation": "UPDATED", "updatedFields": ["title"], "node": { "title": "Prisma is the best way to build GraphQL servers", }, "previousValues": { "title": "GraphQL servers are best built with conventional ORMs", } } } } 请注意,这假设更新在执行Mutation之前Post具有以下内容title:“GraphQL servers are best built with conventional ORMs”。

对象Subscriptions 对于datamodel中的每个可用对象类型,将自动生成一个对象订阅。

订阅创建节点 对于给定的type,你可以订阅正在使用生成的对象正在创建的所有节点。

订阅所有创建的节点 要订阅某种type创建的节点,则可以使用所生成的对象的Subscription和where参数指定的mutation_in:[CREATED]。

订阅创建POST节点:

subscription { post(where: { mutation_in: [CREATED] }) { mutation node { description imageUrl author { id } } } } 使用过滤订阅特定创建的节点 可以利用where对象的node参数订阅特定节点。

订阅创建特定author的POST节点:

subscription { post(where: { AND: [ { mutation_in: [CREATED] }, { node: { author: { id: "cj03x3nacox6m0119755kmcm3" } } ] }) { mutation node { description imageUrl author { id } } } } 订阅删除节点 要订阅某种类型删除的节点,则可以使用所生成的对象的subscription和用where参数指定的mutation_in:[DELETED]。

订阅所有被删除的节点 订阅删除POST节点:

subscription deletePost { post(where: { mutation_in: [DELETED] }) { mutation previousValues { id } } } 订阅特定删除节点 可以使用where对象的node参数过滤特定节点。

订阅其中title包含特定字符串的POST节点:

subscription { post(where: { mutation_in: [DELETED] node: { title_contains: "GraphQL" } }) { mutation previousValues { id } } } 订阅更新节点 要订阅某种type的更新节点,则可以使用所生成的对象的subscription和用where参数指定的mutation_in:[UPDATED]。

订阅所有更新节点 订阅更新POST节点:

subscription { post(where: { mutation_in: [UPDATED] }) { mutation updatedFields node { description imageUrl } previousValues { description imageUrl } } } 订阅特定字段的更新 订阅POST节点的其中一个description字段得到更新的事件,:

subscription { post(where: { mutation_in: [UPDATED] updatedFields_contains: "description" }) { mutation node { description } updatedFields previousValues { description } } } 类似于updatedFields_contains,多个过滤器的情况:

updatedFields_contains_every:[String!]:如果匹配指定的所有字段都进行了更新。 updatedFields_contains_some:[String!]:如果某些指定的字段已经更新匹配。 订阅所有POST节点的description 和published字段得到更新的事件,:

subscription { post(where: { mutation_in: [UPDATED] updatedFields_contains_every: ["description", "published"] }) { mutation node { description } updatedFields previousValues { description } } } 订阅其中一个POST节点的description 或者published字段得到更新的事件,:

subscription { post(where: { mutation_in: [UPDATED] updatedFields_contains_some: ["description", "published"] }) { mutation node { description } updatedFields previousValues { description } } } 注意

不能同时使用updatedFields过滤器,mutation_in: [CREATED]或mutation_in: [DELETED]仅适用于UPDATE事件!

Relation Subscriptions 目前,关系字段Subscriptions仅可使用UPDATEDSubscriptions一种解决方法。

你可以通过“touching”节点强制进行通知。dummy: String向相关类型添加字段,并为关系状态刚刚更改的节点更新此字段。

mutation updatePost { updatePost( where: { id: "some-id" } data: { dummy: "dummy" # do a dummy change to trigger update subscription } ) } 如果你对订阅的直接关系触发感兴趣,[请加入GitHub上的讨论(https://github.com/prismagraphql/feature-requests/issues/146)。

用原始数据库SQL命令 配置docker-compose.yml文件:

version: '3' services: prisma: image: prismagraphql/prisma:1.33 restart: always ports:

使用executeRaw Mutation:

创建一个新用户:

mutation { executeRaw(query: "INSERT INTO default$default."User" (Id, Name, "updatedAt","createdAt") VALUES ('cjnkpvm0b000d0b22j7csr04v', 'Abhi', '2018-10-22T19:54:47.606Z', '2018-10-22T19:54:47.606Z');") } 查询所有用户:

mutation { executeRaw(query: "SELECT * FROM default$default."User"") } 运用 使用Prisma Graphql API Prisma API通过HTTP使用。 这意味着你可以使用任何你喜欢的HTTP工具/库与Prisma API进行通信。

这里是createUserMutation的HTTP POST请求的结构:

Header

Authorization:携带用于验证请求的service token(前缀为'Bearer`); 仅在使用service secret部署服务时才需要。 Content-Type:指定请求主体(JSON)的格式,通常是application / json。 Body (JSON)

query:要发送给API的GraphQL操作; 请注意,尽管该字段被称为“query”,它也用于 Mutation!!! variables:一个JSON对象,包含在query中提交的GraphQL操作中定义的变量。 此小节所有例子都基于与下面的服务配置的Prisma services:

prisma.yml

datamodel: datamodel.prisma secret: my-secret-42 datamodel.prisma

type User { id: ID! @id name: String! } curl curl是一个命令行工具(除其他事项外),让你发送HTTP请求到URL。

这里是你如何发送createUserMutation使用curlPrisma API:

curl 'YOUR_PRISMA_ENDPOINT' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer YOUR_SERVICE_TOKEN' \ --data-binary '{"query":"mutation ($name: String!) { createUser(data: { name: $name }) { id } }","variables":{"name":"Sarah"}}' 为了尝试这个例子,从Prisma services替换相应值YOUR_PRISMA_ENDPOINTYOUR_SERVICE_TOKEN占位符,并将所得片段贴到终端。

一般来说在本地部署的服务endpoint为http://localhost:4466,使用demo server的请在yml文件中查看你的endpoint。

token请在终端输入prisma token获得,Authorization格式类似这样: Bearer sadoifjaosif……

以上请求和下面的Mutation作用相同:

mutation { createUser(data: { name: "Sarah" }) { id } } 试试这个例子:

1.从Prisma services替换相应值YOUR_PRISMA_ENDPOINTYOUR_SERVICE_TOKEN占位符 1.所生成的片段粘贴到你的终端,并按下回车键

fetch (Node.JS) fetch功能可以让你的HTTP请求从JavaScript发送到URL。

下面是你在一个node脚本中使用fetch发送createUserMutation的Prisma API:

const fetch = require('node-fetch') const endpoint = 'YOUR_PRISMA_ENDPOINT' const query = mutation($name: String!) { createUser(data: { name: $name }) { id } } const variables = { name: 'Sarah' } fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer YOUR_SERVICE_TOKEN', }, body: JSON.stringify({ query: query, variables: variables }), }) .then(response => response.json()) .then(data => console.log(JSON.stringify(data))) 同样为以下功能:

mutation { createUser(data: { name: "Sarah" }) { id } } 试试这个例子:

1.从Prisma services替换相应值YOUR_PRISMA_ENDPOINTYOUR_SERVICE_TOKEN占位符 1.存放在一个JavaScript文件script.js 1.通过在script.js所在的同一目录中运行yarn add node-fetch来安装node-fetch库 1.使用以下终端命令执行该脚本:node script.js

graphql-request graphql-request库是fetch的轻量级封装。它主要用于需要跟一个GraphQL API交互的轻量级应用。

使用request 注意

request不支持将headers头放入请求(尚未)。因此,这个特殊的例子假定你的Prisma services部署时没有服务密码。

const { request } = require('graphql-request') const query = mutation($name: String!) { createUser(data: { name: $name }) { id } } const variables = { name: 'Sarah' } request('YOUR_PRISMA_ENDPOINT', query, variables) .then(data => console.log(data)) 使用GraphQLClient

const { GraphQLClient } = require('graphql-request') const client = new GraphQLClient('YOUR_PRISMA_ENDPOINT', { headers: { Authorization: 'Bearer YOUR_SERVICE_TOKEN', }, }) const query = mutation($name: String!) { createUser(data: { name: $name }) { id } } const variables = { name: 'Sarah' } client.request(query, variables) .then(data => console.log(data)) GraphQL Playground 一个GraphQL Playground是一个GraphQL IDE,让你发送Query,Mutation和Subscriptions GraphQL的API。

你可以通过将终端cd到服务的prisma.yml所在的同一目录来打开Prisma API的Playground,然后运行以下命令:

prisma playground 如果prisma.yml包含secret,Playground将已经用Service token配置。你可以在窗格的左下角验证这一点,加入 HTTP header:

这意味着你可以开始将请求发送到Prisma API的时候了。Query,Mutation和Subscriptions都写在Playground的左侧窗格中。然后你点击 play - 按钮实际发送请求。结果将出现在Playground的右窗格中:

Apollo Client Apollo Client是在较大的前端应用中通常使用的复杂的GraphQL库。虽然所有前面的例子中使用的通用工具来发送Query和Mutation一样,Apollo Client使用用于发送Query和Mutation的专用方法:query&mutation

query

const { ApolloClient } = require('apollo-boost') const gql = require('graphql-tag') const endpoint = 'https://eu1.prisma.sh/nikolas-burk/demodofin/dev' const client = new ApolloClient({ uri: endpoint }); const query = gql query { users { id name } } client.query({ query: query, }) .then(data => console.log(data)) mutation

const { ApolloClient } = require('apollo-boost') const gql = require('graphql-tag') const endpoint = 'https://eu1.prisma.sh/nikolas-burk/demodofin/dev' const client = new ApolloClient({ uri: endpoint }); const mutation = gql mutation($name: String!) { createUser(data: { name: $name }) { id } } const variables = { name: 'Sarah' } client.mutate({ mutation: mutation, variables: variables }) .then(data => console.log(data)) Prisma Bindings Prisma Bindings可以看做Prisma client替代版本。

关于GraphQL bindings 的文档here.

它和Prisma client的区别,阅读这里this forum post.

安装 cnpm i -S prisma-binding

示例 还是那个datamodel:

type User { id: ID! @id name: String } 在.graphqlconfig.yml的prisma中定义:

prisma: schemaPath: src/generated/prisma.graphql includes: [ "prisma.graphql", "seed.graphql", "datamodel.graphql", ] extensions: prisma: prisma/prisma.yml codegen:

import { GraphQLServer } from 'graphql-yoga' import { Prisma } from './generated/prisma-client' import { resolvers } from './resolvers' const prisma = new Prisma({ endpoint: process.env.PRISMA_ENDPOINT!, secret: process.env.PRISMA_SECRET!, // debug: true, }) // 根据id查询某个user的名字 prisma.query.user({ where { id: 'abc' } }, '{ name }') // 查询所有user的id和name prisma.query.users(null, '{ id name }') // 创建新user并返回她的id prisma.mutation.createUser({ data: { name: 'Sarah' } }, '{ id }') // 更新一个用户的名字并返回id prisma.mutation.updateUser({ where: { id: 'abc' }, data: { name: 'Sarah' } }, '{ id }') // 删除一个用户并返回该用户存在时的id prisma.mutation.deleteUser({ where: { id: 'abc' } }, '{ id }') const server = new GraphQLServer({ typeDefs: './src/schema.graphql', resolvers: resolvers as any, context: req => ({ ...req, db }), }) server.start(({ port }) => console.log('Server is running on http://localhost:${port}'), ) 在底层,每一个函数的调用都简单地转换为对你的Prisma service的实际HTTP请求(使用了graphql-request)

查询某条数据是否存在:

prisma.exists.Post({ id: 'abc', author: { name: 'Sarah' } }) 更多使用方式查看GraphQL bindings 的原文档here

下一节来学习Prisma的工具和配置

cisen commented 5 years ago

Prisma CLI&配置文件

使用Prisma CLI Prisma的命令行界面(CLI)是部署和管理你的Prisma services的主要工具。

Prisma CLI可以在以下地方帮助你:

为新services生成服务配置文件 部署services到Prisma的服务器 生成认证令牌token 导入种子数据,导入和导出数据 ...云服务等更多 获取有关所有的CLI命令的相关详细信息,请参阅下文命令参考

安装 Prisma CLI可从[NPM]安装(https://docs.npmjs.com/misc/registry)。

NPM

cnpm install -g prisma Yarn

yarn global add prisma Synopsis

PRISMA

GraphQL Database Gateway (https://www.prisma.io)

Usage: prisma COMMAND

Service:

init Initialize a new service

deploy Deploy service changes (or new service)

introspect Introspect database schema(s) of service

info Display service information (endpoints, cluster, ...)

token Create a new service token

list List all deployed services

delete Delete an existing service

Data workflows:

playground Open service endpoints in GraphQL Playground

seed Seed a service with data specified in the prisma.yml

import Import data into a service

export Export service data to local file

reset Reset the stage data

Cloud:

login Login or signup to the Prisma Cloud

logout Logout from Prisma Cloud

console Open Prisma Console in browser

account Display account information

Use prisma help [command] for more information about a command.

Docs can be found here: https://bit.ly/prisma-cli-commands

Examples:

- Initialize files for a new Prisma service

prisma init

- Deploy service changes (or new service)

prisma deploy

快速开始 一旦安装,执行下面的命令,让你的Prisma API跑起来,并开始发送Query和Mutation给它:

prisma init hello-world

按part1示例

cd hello-world prisma deploy prisma playground graphql-config用法 Prisma CLI与[graphql-config]集成(https://github.com/prismagraphql/graphql-config)。如果你的项目使用了.graphqlconfig文件,你可以使用prisma扩展,它指向你的prisma.yml:

projects: prisma: schemaPath: prisma.graphql extensions: prisma: prisma.yml 使用HTTP代理的CLI Prisma CLI支持[定制HTTP代理(https://github.com/graphcool/prisma/issues/618)。在企业防火墙后面时,这一点尤为重要

要激活代理,所提供的环境变量HTTP_PROXY和HTTPS_PROXY。该行为非常类似于如何npm handles this。

可以提供以下环境变量:

HTTP_PROXY或http_proxy:HTTP流量代理URL,例如通过http://localhost:8080 HTTPS_PROXY或https_proxy:为HTTPS流量代理URL,例如的https://localhost:8080 NO_PROXY或no_proxy:要禁用某些URL的代理,请为'NO_PROXY提供glob,例如*`。 为了得到一个简单的本地代理,则可以使用proxy模块:

npm install -g proxy DEBUG="*" proxy -p 8080 HTTP_PROXY=http://localhost:8080 HTTPS_PROXY=https://localhost:8080 prisma deploy PRISMA INIT 起新Prisma services的服务配置。该命令将通过默认启动交互式CLI向导,帮助你决定在何处部署(即哪个Prisma服务器)。

如果你已经知道端点,则可以使用该--endpointflags传递它并跳过交互式向导。

生成的文件有:

prisma.yml datamodel.prisma docker-compose.yml(可选) 如果你提供一个目录名作为参数传递给命令,生成的文件将被放置在该名称的新目录中。

Flags

-e, --endpoint The endpoint to be written into prisma.yml 用法

prisma init DIRNAME 例子 在一个叫做'myapp`的目录使用向导创建Prisma services服务配置

prisma init myapp 跳过向导,并在一个叫myapp目录创建Prisma services服务配置

prisma init myapp --endpoint http://localhost:4466/myapp/dev prisma deploy 部署服务配置到服务器。

每当你改变服务配置的时候,你需要将这些变化与运行的Prisma services同步。

在第一次部署服务时,如果提供prisma.yml的seed属性,命令将执行初始数据导入。你可以通过传递--no-seed选项防止这一点。

如果endpoint在prisma.yml中未指定任何属性,则该命令将提示你以交互方式选择Prisma服务器作为服务的部署目标。选择Prisma服务器后,CLI会将endpointprisma.yml 写入未来部署的默认设置。要再次调出交互式提示,只需endpoint手动从prisma.yml中删除该属性或传递--new参数。

用法

prisma deploy [flags] flags -d,--dry 干跑执行部署 -e,--env-file ENV-FILE 注入ENV的.ENV文件路径 -f,--force 强制deploy,接受由数据更改造成的数据丢失 -j,--json JSON输出 -n,--new 强制交互模式来选择集群 -w,--watch 监视更改 --no-seed 禁止种子数据导入 `} 例子 部署当前目录服务配置

prisma deploy 部署服务和交互式选择Prisma服务器作为部署目标:

prisma deploy --new 在.env.prod指定的环境变量部署服务

prisma deploy --env-file .env.prod prisma generate 在调用prisma.yml指定的generators。可用生成器是:

以下生成器内置到Prisma CLI:

Prisma client in JavaScript: javascript-client Prisma client in TypeScript: typescript-client Prisma client in Flow: flow-client Prisma client in Go: go-client GraphQL schema of the Prisma API: graphql-schema 用法

prisma generate [flags] flags -e, --env-file ENV-FILE Path to .env file to inject env vars `} 例子 生成TypeScript client并将生成的文件存储在./generated/prisma

prisma.yml:

generate:

prisma generate Go client和GraphQL schema并存储生成的文件在./generated/prisma

prisma.yml:

generate:

prisma generate prisma introspect 由现有数据库的模式introspecting创建一个数据模型。

该命令启动交互式向导,要求你提供你的数据库连接的详细信息:

Host: The host of your Database server, e.g. localhost. Port: The port where your Database server listens, e.g. 5432. User & Password: The credentials for your Database server. Name of existing database: The name of the Database database. Use SSL (Yes/No): If your database connection is using SSL, you need to select Yes, otherwise No. Name of existing schema: The name of the Database schema, e.g. public. 用法

prisma introspect 例子 Introspect现有的数据库

prisma introspect 这里是通过向导提供的数据库连接细节:

? What kind of database do you want to introspect? Postgres

? Enter database host localhost

? Enter database port 5432

? Enter database user prisma

? Enter database password ****

? Enter name of existing database prisma-db

? Enter name of existing schema public

Introspecting database 402ms

Created datamodel mapping based on 7 database tables.

Created 1 new file:

datamodel-[TIMESTAMP].graphql GraphQL SDL-based datamodel (derived from existing database)

所生成的数据模型文件将包含时间戳的名称,以避免重写现有的datamodel.prisma文件。

prisma info 显示服务信息:

服务名称 服务版本 API端点(HTTP和的WebSocket) 用法

prisma info flags

-e, --env-file ENV-FILE Path to .env file to inject env vars -j, --json JSON Output -s, --secret Print secret in JSON output (requires --json option) 例子 打印关于当前服务的信息

prisma info 打印有关JSON当前服务信息

prisma info --json 打印有关JSON当前服务信息,包括服务密码

prisma info --json --secret 提供两个--json和--secretflags的时,服务密码才会打印。

prisma token 生成一个新的service token。service token是用JWT与service token签名加密的。

用法

prisma token [flags] flags

-c, --copy Copy token to clipboard -e, --env-file ENV-FILE Path to .env file to inject env vars 例子 打印service toke

prisma token 复制service toke到剪贴板

prisma token --copy prisma list 列出所有部署的服务。

用法

prisma list 例子 列出所有部署的服务

prisma list prisma delete 从Prisma服务器删除的其上运行现有服务。

这个命令需要在Prisma services的根目录中执行。

用法

prisma delete [flags] flags

-e, --env-file ENV-FILE Path to .env file to inject env vars -f, --force Force delete, without confirmation 例子 删除现有的服务(与确认提示)

prisma delete 删除现有的服务(没有确认提示)

prisma delete --force prisma playground 打开一个GraphQL Playground用于当前的服务。

默认情况下,这将打开Playground的Desktop version(如果已安装)。基于浏览器的Playground可以通过传递--webflags被打开。

Playground运行在3000端口上。

用法

prisma playground [flags] flags

--dotenv DOTENV Path to .env file to inject env vars -w, --web Open browser-based Playground 例子 打开Playground(桌面版本,如果已安装)

prisma playground 打开Playground(基于浏览器的版本)

prisma playground --web prisma seed 服务的种子数据。

此命令需要在prisma.yml中[seed]属性已设置。

用法

prisma seed [flags] flags

-e, --env-file ENV-FILE Path to .env file to inject env vars -r, --reset Reset the service before seeding 例子 与初始数据种子

prisma seed 删除所有服务的数据后先初始数据种子

prisma seed --reset prisma import 将数据导入到你的Prisma services的数据库。

数据需要根据归一化数据格式进行格式化。欲了解更多信息,请阅读[数据导入和导出]。

用法

prisma import [flags] flags

-d, --data PATH (required) Path to zip or directory with import data (NDF) -e, --env-file ENV-FILE Path to .env file to inject env vars prisma export 导出你的业务数据到本地的zip目录。欲了解更多信息,请阅读[数据导入和导出]。

用法

prisma export [flags] flags

-e, --env-file ENV-FILE Path to .env file to inject env vars -p, --path PATH Path to export .zip file 例子 导出数据到默认名称(export-.zip)文件

prisma export 数据导出到文件称为mydata.zip

prisma export --path mydata.zip prisma reset 删除所有业务数据。

用法

prisma reset flags

-e, --env-file ENV-FILE Path to .env file to inject env vars -f, --force Force reset data without confirmation 例子 删除所有服务的数据(确认提示)。

prisma reset 删除所有服务的数据(没有确认提示)。

prisma reset --force prisma login 与Prisma cloud进行认证。

该命令打开Prisma cloud端控制台,你需要注册或登录。

在浏览器中成功认证后,CLI写入cloudSessionKey到〜/.prisma/config.yml。从那里,它被用于通过对Prisma cloud CLI所做的所有后续请求。

请注意,不仅是通过--keyflags提供你的云会话密钥,你还可以设置PRISMA_CLOUD_SESSION_KEY环境变量,这在CI的环境中尤其有用。

用法

prisma login [flags] flags

-k, --key KEY Cloud session key 例子 与Prisma cloud验证(打开浏览器)

prisma login 通过手动传递云会话密钥与Prisma cloud验证

prisma login --key KEY 在上面的命令中,KEY需要与你的有效的云会话密钥的值来代替。你可以在〜/.prisma/config.yml了cloudSessionKey。

prisma logout 从Prisma cloud注销。

这个命令只是从〜/.prisma/config.yml删除cloudSessionKey。

用法

prisma logout 例子 登录Prisma cloud出

prisma logout prisma console 在浏览器中打开Prisma cloud端控制台。

用法

prisma console 例子 打开Prisma cloud端控制台在浏览器

prisma console prisma account 显示当前帐户Prisma cloud(基于〜/.prisma/config.yml的cloudSessionKey)的信息。

显示的信息包括:

用户名 电子邮件地址 用法

prisma account 例子 Prisma cloud用户显示帐户信息

prisma account prisma.yml prisma.yml为Prisma services的配置根 文件。每个Prisma services由一个prisma.yml定义。你可以认为prisma.yml是一个Prisma services的地基。

prisma.yml指定了许多有关Prisma services的属性,例如:

服务的endpoint(包括name和stage) 如何以及在何处生成Prisma client Service secret用于验证服务的API的请求 datamodel文件的位置(一个或多个) hooks指定shell命令在deployment过程中哪些点要被执行 例子 最小示例 有效的prisma.yml至少包含两个属性:

datamodel endpoint 为了能够指定一个endpoint,你需要访问Prisma服务器。如果你没有Prisma服务器,在这种情况下,CLI向导将引导你完成创建本地Prisma服务器的过程或可以部署Prisma cloud的Demo Servers。然后为你提供的服务被部署之前将endpoint写入到prisma.yml:

datamodel: datamodel.prisma / 标准的例子 在prisma.yml最常用的属性是:

datamodel endpoint secret hooks generate 下面是具有这些特性的标准prisma.yml:

datamodel: datamodel.prisma endpoint: http://localhost:4466/myservice/dev secret: mysecret42 generate:

该服务是基于这两个文件类型定义

databasetypes.graphql和database/enums.graphql

datamodel:

. │ .graphqlconfig ├── prisma.yml └── database ├── subscriptions │ └── welcomeEmail.graphql ├── types.graphql └── enums.graphql 使用变量 变量允许你动态地更换你的prisma.yml配置值。当为你服务设置secrets时特别有用的,或者在多个开发人员共同开发时。

要使用prisma.yml变量,需要引用括在'${}'括号内的值:

yamlKeyXYZ: ${variableSource} 一个variable source可以是以下两种:

自引用值 环境变量 你只能在属性值中使用变量- 而不是在属性键中。因此,你无法使用变量来例如在自定义资源部分中生成动态逻辑ID。

递归自引用 你可以递归引用同一prisma.yml文件中的其他属性值。

使用递归自引用作为变量时,放入括号中的值由以下内容组成:

前缀 self: (可选)引用属性的路径 ; 如果未指定路径,则变量的值将是整个YAML文件。

subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: url: https://${self:custom.serverlessEndpoint}/sendWelcomeEmail custom: serverlessEndpoint: example.org / 这适用于prisma.yml内任何属性,不只是custom。

环境变量 你可以在prisma.yml中引用环境变量。

使用环境变量时,放入括号中的值由以下内容组成:

前缀 env: 环境变量的name 在以下示例中,引用了一个环境变量来指定webhook的URL和身份验证令牌:

subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: url: https://example.org/sendWelcomeEmail headers: Authorization: ${env:MY_TOKEN} 参考 根属性 服务定义文件prisma.yml具有以下根属性:

datamodel(必需):数据库模型,关系,枚举和其他类型的类型定义。 endpoint:HTTP endpoint的Prisma API。可以省略CLI部署向导。 secret:用于固定API endpoint的服务密码。 hooks:定义CLI命令之前/Prisma CLI的特定动作之后执行。 subscriptions:订阅webhooks的配置。 seed:指向包含种子数据的Mutation文件。 custom:用于提供可在其他地方被prisma.yml引用的变量。 prisma.yml的确切结构是使用JSON模式定义的。你可以找到相应的模式定义这里。JSON schem定义还允许提升你的工具,让你的代码编辑器和IDE帮助你完成prisma.yml的正确结构。

数据模型(必需) datamodel指向.graphql包含用GraphQL SDL编写的模型定义的一个或多个文件的点。如果提供了多个文件,CLI只会在部署时连接其内容。

Type 该datamodel属性需要一个字符串或一个字符串列表。

例子 数据模型是在一个名为'types.graphql`文件中定义。

datamodel: types.graphql 数据模型在称为types.graphql和enums.graphl的两个文件定义。当服务被部署时,这两个文件的内容将通过CLI进行关联。

datamodel:

Prisma server:将承载你的Prisma services的服务器。 Workspace(仅适用于Prisma cloudDemo Servers):你通过Prisma cloud配置的工作区的名称。 Service name:你Prisma的SERVICE中的描述性名称。 Service stage:你集群的版本(比如'dev,staging,prod`)。 请注意,部署Prisma服务时endpoint实际上是必填的。但是,如果在运行prisma deploy之前未在prisma.yml中指定它,CLI会提示你使用向导来帮助你确定Prisma服务器作为部署目标,然后将endpoint写入prisma.yml。

Type 该endpoint属性需要一个字符串。

例子 下面的示例端点编码以下信息:

Prisma server:HTTP://localhost:4466意味着你的机器上运行的服务器Prisma在本地(例如,使用docker)。 Service name: default Stage: default 当服务名称和阶段都被设置为'default,它们可以被省略。这意味着本实施例中的端点是相当于:http://localhost:4466/`

endpoint: http://localhost:4466/default/default 下面的示例端点编码以下信息:

Prisma server:https://eu1.prisma.sh意味着你使用的是Prisma的Demo Servers作为你的Prisma services部署目标。 Workspace:jane-doe是Prisma Cloud工作区的名称。 Service name:myservice Stage:dev

终点:https://eu1.prisma.sh/jane-doe/myservice/dev 下面的示例端点编码以下信息:

Prisma server: http://my-pr-Publi-1GXX8QUZU3T89-413349553.us-east-1.elb.amazonaws.com 表示你正在使用AWS上的Prisma服务器来部署你的Prisma服务。 Service name: cat-pictures Stage: prod

endpoint: http://my-pr-Publi-1GXX8QUZU3T89-413349553.us-east-1.elb.amazonaws.com/cat-pictures/prod 密码(可选) 所述服务密码被用于生成(或sign)认证令牌(JWT)。token需要附加到服务公开的针对Prisma API的HTTP请求(在Authorization头字段中)。

密码必须遵循以下要求:

必须是UTF8编码 不能包含空格 最多256个字符 注意

如果Prisma services部署没有secret,其API不需要验证。这意味着每个人都可以访问到endpoint发送Query和Mutation API,因此可以任意读取和写入数据库!

Type secret属性需要一个字符串(不是字符串列表)。如果要指定多个密码,你需要为他们提供一个逗号分隔列表(空格被忽略),但仍然作为一个字符串值。

例子 定义一个密码moo4ahn3ahb4phein1eingaep。

secret: moo4ahn3ahb4phein1eingaep 定义三个密码的值myFirstSecret,SECRET_NUMBER_2和3-secret。需要注意的是第二个密码之前的空格将被忽略。

secret: myFirstSecret, SECRET_NUMBER_2,3rd-secret 使用环境变量MY_SECRET的密码(一个或多个)的值。

secret: ${env:MY_SECRET} generate(可选) generate属性用于指定如何以及在何处应该生成Prisma client(或其他文件)。

以下生成器内置到Prisma CLI:

Prisma client in JavaScript: javascript-client Prisma client in TypeScript: typescript-client Prisma client in Flow: flow-client Prisma client in Go: go-client GraphQL schema of the Prisma API: graphql-schema Type generate属性需要一个对象列表。这些对象有两个属性:

generator:从上面的列表中提供的生成器中的其中一个。 output:指定生成的文件应在哪个位置。 例子

generate:

目前提供以下钩子:

post-deploy:将在命令后调用prisma deploy Type 该hooks属性需要一个对象。该对象的属性与当前可用挂钩的名称匹配。

例子 以下是执行prisma deploy后先执行三个命令的例子:

1.打印"Deployment finished" 1.下载在.graphqlconfig指定的db项目的GraphQL schema 1.调用代码生成

hooks: post-deploy:

projects: db: schemaPath: generated/prisma.graphql extensions: prisma: prisma.yml codegen:

一个subscription query定义在哪个事件的函数被调用,Payload的样子 一旦事件发生,其通过HTTP调用的webhook的网址 (任选)要附加到发送到URL的请求的多个HTTP header Type subscriptions属性需要一个具有以下属性的对象:

query(必需):该文件路径的subscription query(存储在一个.graphql文件)。 webhook(必需):有关要调用的webhook的信息(URL和可选的HTTP header)。如果没有header,你可以直接向此属性提供URL(请参阅下面的第一个示例)。否则,webhook需要另一个对象的url和headers(见下文第二示例)。 例子 没有指定的HTTP头的一个Subscriptions。

subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/sendWelcomeEmail `

指定一个Subscriptions事件有两个HTTP header。

subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: url: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/sendWelcomeEmail headers: Authorization: ${env:MY_SECRET} Content-Type: application/json seed(可选) 填充数据库初始数据,测试数据的标准方法。

Type seed属性需要一个对象,具有以下两个子属性之一:

import:导入数据的说明。你可以参考两种文件中的任何一种:

.graphql 具有GraphQL操作的文件的路径。 .zip 包含规范化数据格式(NDF)中的数据集的文件的路径 run:导入时将要执行的Shell命令。这适用于未涵盖的更复杂的seed设置的import。 首次部署服务时(除非使用上PRISMA deploy的--no-seedflags明确禁用)seed被隐式执行。

例子 含有Mutation的.graphql文件:

seed: import: database/seed.graphql 与NDF数据集的.zip文件:

seed: import: database/backup.zip seed时运行节点脚本:

seed: run: node script.js 自定义(可选) 该custom属性用于指定你想在你的prisma.yml其他地方重用值的任何种类。因此,它没有一个预定义的结构。可以参考使用与[self可变源]的变量,例如:${self:custom.myVariable}。

Type 该custom属性需要一个对象。没有关于物体的形状假设。

例子 定义两个自定义值,并在事件Subscriptions的定义中重用他们。

custom: serverlessEndpoint: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev subscriptionQueries: database/subscriptions/ subscriptions: sendWelcomeEmail: query: ${self:custom.subscriptionQueries}/sendWelcomeEmail.graphql webhook: https://${self:custom.serverlessEndpoint}/sendWelcomeEmail 数据导入 要导入的数据需要遵循规范化数据格式(NDF)。截至今天,必须手动执行从任何具体数据源(如MySQL,MongoDB或Firebase)到NDF的转换。在未来,该Prisma的CLI将支持直接从这些数据源导入。

下面是数据导入过程的概述:

+--------------+ +----------------+ +------------+ +--------------+
SQL (1) transform NDF (2) chunked upload Prisma
MongoDB +--------------> +------------------->
JSON
+--------------+ +----------------+ +------------+

+--------------+ 如上所述,步骤1必须手动执行。然后,可以使用原始导入API或prisma importCLI中的命令来完成步骤2 。

要在CLI中查看支持的转换的当前状态并提交所需的转换,你可以查看这个GitHub的讨论。

当用NDF格式上传文件,你需要将导入数据拆分提供三个不同种类的文件:

nodes:数据单个节点(即数据库记录) lists:数据节点的列表 relations:数据的关系节点 你可以为每种类型上传无限数量的文件,但建议每个文件最多1 MB。否则你可能会遇到超时。

重要注意事项 一般约束 导入数据时,所述id字段最多25个字符。

幂等 请注意,导入操作不是幂等的。这意味着运行导入始终会向你的服务添加数据。它永远不会更新现有节点。这意味着多次导入相同的数据集将导致未知的错误。

例如,导入节点具有相同id多次将导致不确定的行为,并有可能弄挂掉你的服务!

数据验证 [导入API]不执行对数据的任何验证检查。当使用[CLI]导入数据时,将执行基本验证检查。

导入无效的数据会导致不确定的行为,并可能会破坏你服务!作为服务的维护者,你有责任确保导入数据的有效性。

提示:导入时确保有效数据的一种好方法是检查具有相同数据模型的服务上先前导出的数据。

使用CLI导入数据 Prisma CLI提供了PRISMA import命令。它接受一个选项:

--data(简写:-d):包含要导入数据的目录的文件路径(可以是常规文件或压缩文件) 在底层,CLI使用下一节中描述的导入API。但是,使用CLI提供了一些主要好处:

一次上传多个文件(而不是必须单独上传每个文件) 利用CLI的身份验证机制(即你无需手动发送身份验证令牌) 能够暂停和恢复正在进行的导入 从 MySQL,MongoDB或Firebase等各种数据源导入(尚不可用) 输入格式 使用CLI导入数据时,包含NDF中数据的文件需要位于其类型后调用的目录中:nodes,lists和relations。

NDF文件是遵循特定结构的JSON文件,因此每个包含导入数据的文件都需要后缀.json。当放置在各自的目录(nodes,lists或relations)中时,.json-files需要逐步编号,从1开始,例如1.json。文件名可以添加任意数量的零,例如01.json或0000001.json。

实例 考虑定义一个Prisma services下列文件结构:

. ├── data │ ├── lists │ │ ├── 0001.json │ │ ├── 0002.json │ │ └── 0003.json │ ├── nodes │ │ ├── 0001.json │ │ └── 0002.json │ └── relations │ └── 0001.json ├── datamodel.prisma └── prisma.yml data包含文件中要导入的数据。此外,在.json结尾的文件都秉承NDF。从这些文件中导入数据,你可以简单地在终端运行下面的命令:

prisma import --data data 使用原始导入API进行数据导入 原始导入API在HTTP端点的/import路径下公开。例如:

http://localhost:4466/my-app/dev/import https://eu1.prisma.sh/my-app/prod/import 一个请求可以上传最多10 MB大小的JSON数据(在NDF中)。请注意,你需要在请求的HTTP 标头中提供Authorization身份验证令牌!

下面是一个例子curl命令上传一些JSON数据(NDF类型的nodes):

curl 'http://localhost:4466/my-app/dev/import' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1MTM1OTQzMTEsImV4cCI6MTU0NTEzMDMxMSwiYXVkIjasd3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.L7DwH7vIfTSmuwfxBI82D64DlgoLBLXOwR5iMjZ_7nI' -d '{"valueType":"nodes","values":[{"_typeName":"Model0","id":"0","a":"test","b":0,"createdAt":"2017-11-29 14:35:13"},{"_typeName":"Model1","id":"1","a":"test","b":1},{"_typeName":"Model2","id":"2","a":"test","b":2,"createdAt":"2017-11-29 14:35:13"},{"_typeName":"Model0","id":"3","a":"test","b":3},{"_typeName":"Model3","id":"4","a":"test","b":4,"createdAt":"2017-11-29 14:35:13","updatedAt":"2017-11-29 14:35:13"},{"_typeName":"Model3","id":"5","a":"test","b":5},{"_typeName":"Model3","id":"6","a":"test","b":6},{"_typeName":"Model4","id":"7"},{"_typeName":"Model4","id":"8","string":"test","int":4,"boolean":true,"dateTime":"1015-11-29 14:35:13","float":13.333,"createdAt":"2017-11-29 14:35:13","updatedAt":"2017-11-29 14:35:13"},{"_typeName":"Model5","id":"9","string":"test","int":4,"boolean":true,"dateTime":"1015-11-29 14:35:13","float":13.333,"createdAt":"2017-11-29 14:35:13","updatedAt":"2017-11-29 14:35:13"}]}' -sSv 对于curl(使用占位符)的通用版本将如下所示:

curl 'SERVICE_ENDPOINT/import' -H 'Content-Type: application/json' -H 'Authorization: Bearer JWT_AUTH_TOKEN' -d '{"valueType":"NDF_TYPE","values": DATA }' -sSv 数据导出 可以使用CLI或原始导出API导出数据。在这两种情况下,下载的数据都以JSON格式化,并遵循规范化数据格式(NDF)。由于导出的数据位于NDF中,因此可以直接将其导入具有相同模式的服务。当服务需要测试数据时,例如在dev阶段中,这可能是有用的。

使用CLI导出数据 Prisma CLI提供了prisma export命令。它接受两个选项:

--env-file(简写:-e):导出环境变量

--path(简写: -p):指向.zip目录的文件路径,该目录将由CLI创建并存储导出的数据

在底层,CLI使用下一节中描述的导出API。但是,使用CLI提供了一些主要好处:

CLI的身份验证机制(即你不需要手动发送你的token) 直接下载数据到文件系统 光标管理以防需要多个请求来导出所有应用程序数据(手动这样做时你需要发送多个请求,并调整记录位置) 输出格式 以NDF格式执行数据导出,将被放置在被不同类型的NDF格式命名的三个目录:nodes,lists和relations。

使用原始导出API进行数据导出 原始导出API在服务的HTTP端点的路径/export下公开。例如:

http://localhost:4466/my-app/dev/export https://database.prisma.sh/my-app/prod/export 一个请求可以下载最大10 MB的JSON数据(在NDF中)。请注意,你需要在请求的HTTP header 的Authorization中提供身份验证令牌!

端点需要一个POST请求,其中正文为JSON,其中包含以下内容:

{ "fileType": "nodes", "cursor": { "table": 0, "row": 0, "field": 0, "array": 0 } } cursor描述了数据库中应从何处导出数据的偏移量。请注意,导出请求的每个响应都将返回一个具有以下两种状态之一的新游标:

Terminated (not full): 如果table所有的值,row,field和array返回为-1这意味着出口已完成。 Non-terminated (_full): 如果对于任何table,row,field或array是与-1不同的值的,这意味着10个MB用于该响应的最大尺寸已经到达。如果发生这种情况,你可以使用返回cursor值作为输入你的下一个export的参数。 实例 下面是一个curl命令上传一些JSON数据(NDF类型nodes)的例子:

curl 'http://localhost:4466/my-app/dev/export' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1MTM1OTQzMTEsImV4cCI6MTU0NTEzMDMxMSwiYXVkIjasd3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.L7DwH7vIfTSmuwfxBI82D64DlgoLBLXOwR5iMjZ_7nI' -d '{"fileType":"nodes","cursor":{"table":0,"row":0,"field":0,"array":0}}' -sSv curl(使用占位符)的通用版本将如下所示:

curl 'SERVICE_ENDPOINT/export' -H 'Content-Type: application/json' -H 'Authorization: Bearer JWT_AUTH_TOKEN' -d '{"fileType":"NDF_TYPE","cursor": {"table":TABLE,"row":ROW,"field":FIELD,"array":ARRAY}} }' -sSv 规范化数据格式 规范化数据格式(NDF)用作Prisma服务中导入和导出的中间数据格式。NDF描述了JSON的特定结构。

NDF值类型 当使用NDF,数据会拆分三个不同的"值类型":

Nodes: Contains data for the scalar fields of nodes Lists: Contains data for list fields of nodes Relations: Contains data to connect two nodes via a relation by their relation fields 结构 在NDF JSON文档的结构是具有以下两个键的对象:

valueType:表示文档中数据的值类型(可以是"nodes","lists"或者"relations") values:包含作为数组的实际数据(遵循值类型) 在下面的例子都是基于这个数据模型:

type User { id: String! @unique firstName: String! lastName: String! hobbies: [String!]! @scalarList(strategy: RELATION) partner: User } 节点 如果valueType是"nodes",则values数组内对象的结构如下:

{ "valueType": "nodes", "values": [ { "_typeName": STRING, "id": STRING, "": ANY, "": ANY, ..., "": ANY }, ... ] } 该符号表示的字段_typeName和id都是字符串类型的。_typeName是指从你的数据模型的SDL类型的名称。在-placeholders将是SDL类型的字段的名称。

例如,下面的JSON文档可用于导入两个User节点的标量值:

{ "valueType": "nodes", "values": [ {"_typeName": "User", "id": "johndoe", "firstName": "John", "lastName": "Doe"}, {"_typeName": "User", "id": "sarahdoe", "firstName": "Sarah", "lastName": "Doe"} ] } Lists 如果valueType是"lists",则values数组内对象的结构如下:

{ "valueType": "lists", "values": [ { "_typeName": STRING, "id": STRING, "": [ANY] }, ... ] } 该符号表示的字段_typeName和id都是字符串类型的。_typeName是指你的数据模型的SDL类型的名称。在-placeholder是那个SDL类型的列表中的字段名称。需要注意的是对比的是标量list字段,每个对象只能有一个字段。

例如,下面的JSON文档可以用于导入值两个User节点的hobbies列表字段:

{ "valueType": "lists", "values": [ {"_typeName": "User", "id": "johndoe", "hobbies": ["Fishing", "Cooking"]}, {"_typeName": "User", "id": "sarahdoe", "hobbies": ["Biking", "Coding"]} ] } relations 如果valueType是"relations",则values数组内对象的结构如下:

{ "valueType": "relations", "values": [ [ { "_typeName": STRING, "id": STRING, "fieldName": STRING }, { "_typeName": STRING, "id": STRING, "fieldName": STRING } ], ... ] } 该符号表示的字段_typeName,id和fieldName都是字符串类型的。

_typeName是指从你的数据模型的SDL类型的名称。在-placeholder是那个SDL类型的关系字段的名称。由于关系数据的目标是通过关系连接两个节点,因此values数组中的每个元素本身都是一对(写成一个总是包含两个元素的数组),而不是像"nodes"和的情况那样的单个对象"lists"。

例如,以下JSON文档可用于User通过partner关系字段在两个节点之间创建关系:

{ "valueType": "relations", "values": [ [ { "_typeName": "User", "id": "johndoe", "fieldName": "partner" }, { "_typeName": "User", "id": "sarahdoe", "fieldName": "partner" } ] ] } 下一节来学习Prisma Server部署

cisen commented 5 years ago

Prisma Server 部署

Prisma services 部署在 Prisma servers上.

本章提供配置,启动,维护和监视 Prisma 服务器所需的所有信息。

本地 Prisma 配置 你可以使用 Docker 运行 Prisma 服务的本地实例。

本页介绍如何将 Prisma 与本地数据库实例一起使用。Prisma 服务器和数据库都是通过 Docker 在本地配置的。

示例 Docker Compose 设置 MySQL 下面的 Docker Compose 文件,配置了两个 Docker 容器:

prisma:容器中运行你的 Prisma 服务器。 mysql-db:容器运行的本地 MySQL 实例,基于MySQL 的 docker 镜像。 prisma 正在使用 mysql-db 容器作为其数据库。它不是通过 IP 地址或 URL 引用数据库 host,而是简单地将 mysql-db 镜像作为数据库引用 host:

version: '3' services: prisma: image: prismagraphql/prisma:1.33 restart: always ports:

prisma:容器中运行你的 Prisma 服务器。 postgres-db:容器运行的本地 postgres 实例,基于[postgres 多克尔图像]上(https://hub.docker.com/_/postgres/)。 prisma使用postgres-db容器作为数据库。它不是通过 IP 地址或 URL 引用数据库 host,而是简单地将 postgres-db 镜像作为数据库引用 host:

version: '3' services: prisma: image: prismagraphql/prisma:1.33 restart: always ports:

prisma:容器中运行你的 Prisma 服务器。 mongo:容器运行的本地 mongo 实例,基于mongo 的 docker 镜像。 prisma 正在使用 mongo 容器作为其数据库。它不是通过 IP 地址或 URL 引用数据库 host,而是简单地将 mongo 镜像作为数据库引用 host:

version: '3' services: prisma: image: prismagraphql/prisma:1.33 restart: always ports:

Management API Prisma 服务器的 Management API 用于部署和管理服务器上运行的 Prisma 服务。它可以通过/management路径获得,例如http://localhost:4466/management。

API 参考 Management API 公开以下操作(你可以在此处找到 Management API 的完整 GraphQL 架构)。链接)。

type Query { """ Shows the status of the next migration in line to be applied to the project. If no such migration exists, it shows the last applied migration. """ migrationStatus(name: String!, stage: String!): Migration! """Shows all projects the caller has access to.""" listProjects: [Project!]! """ Shows all migrations for the project. Debug query, will likely be removed in the future. """ listMigrations(name: String!, stage: String!): [Migration!]! """Gets a project by name and stage.""" project(name: String!, stage: String!): Project! """Information about the server""" serverInfo: ServerInfo! """generates a token for the given project""" generateProjectToken(name: String!, stage: String!): String! } type Mutation { deploy(input: DeployInput!): DeployPayload addProject(input: AddProjectInput!): AddProjectPayload deleteProject(input: DeleteProjectInput!): DeleteProjectPayload setCloudSecret(input: SetCloudSecretInput!): SetCloudSecretPayload } 注意

由于遗留原因,Management API 目前使用过时的术语来引用某些 Prisma 概念。最重要的是,Prisma services称为projects(例如 listProjects),Prisma servers在 API 术语中称为clusters。

身份验证和安全 一旦启动并运行,Prisma 服务器主要通过 Management API 使用。本页介绍了如何保护 Management API 并保护 Prisma 服务器免受不必要的请求的影响。

Management API 的密码 为了确保只有授权的用户能够通过管理 API 来执行操作,它需要一个secret保护。这个密码被称为Management API secret。

Management API 密钥在你用于配置 Prisma 服务器的 Docker Compose 文件中设置。它是 PRISMA_CONFIG 环境变量的一部分,并通过 management Api Secret 密钥指定:

version: '3' services: prisma: image: prismagraphql/prisma:LATEST_PRISMA_VERSION restart: always ports:

如果 Prisma 服务器的 Docker 配置不包含 management Api Secret,则每个有权访问 Prisma 服务器 URL 的人都可以向其发出任意请求。这包括部署和删除 Prisma 服务以及修改数据。

Management API token service toke 遵守JSON Web Token(JWT)规范(RFC 7519):

一个 JWT 有以下三个部分组成:

header头:通常由两部分组成:令牌的类型,即 JWT 使用的散列算法(在 Prisma 服务令牌的环境下为 HS256)。

{ "alg": "HS256", "typ": "JWT" } Payload:包含声明。声明是关于实体(通常是用户)和其他数据的声明。以下是修改调用 demo 部署到 dev 服务的权限时的样子:

{ "grants": [ { "target": "demo/dev", "action": "*" } ], "iat": 1532956915, "exp": 1690744915 } Signature签名:签名用于验证消息在此过程中未被更改。要创建签名部分,你必须采用编码 header,编码的 payload,secret,标头中指定的算法,并对其进行签名。例如,如果要使用 HMAC SHA256 算法,将按以下方式创建签名:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 因此,JWT 通常如下所示: xxxxx.yyyyy.zzzzz

了解 JWTs 这里。

Claims 所述 JWT 必须包含以下的Claims:

Issued at: iat字段包含一个 Unix 时间戳和当生成所述令牌的确切时间。 Expiration date: exp字段包含表示令牌到期日期的 Unix 时间戳。 Grants: 该grants字段是具有两个键的对象 在target字段指定name和stage指定使用特定操作修改的服务。使用/通配符来允许 Prisma 服务器上运行的所有服务执行操作。 在action字段指定允许哪些操作 target。通配符*允许所有操作。 以下是使用通配符的 JWT 的示例 payload,以允许对所有服务执行所有操作:

{ "grants": [ { "target": "/", "action": "*" } ], "iat": 1532956915, "exp": 1690744915 } 对 Management API 进行身份验证 有两种方法可以使用 Management API:

使用 Prisma CLI,然后进行实际的 API 请求 直接将请求发送到你的 Prisma 服务器的/management路径 使用 Prisma CLI 使用 Prisma CLI 来执行 Management API 请求(例如,使用prisma deploy),你无需担心生成 Management API 令牌。CLI 会在每次 API 请求时为你生成它们。因此,CLI 需要知道 Prisma 服务器的 Management API 机密,以便生成 Management API 令牌并验证其请求。

这就是使用 Prisma CLI 时必须设置环境变量 PRISMA_MANAGEMENT_API_SECRET 的原因。。

该 CLI 读取来自PRISMA_MANAGEMENT_API_SECRET环境变量的密码,并用它来生成 JWT 然后附加到 API 请求。

根据你的 shell,设置环境变量的语法可能会有所不同。标准 Unix shell(linux、mac)使用以下方式设置环境变量:

export PRISMA_MANAGEMENT_API_SECRET="my-server-secret-42" 直接发送请求 如果要将 HTTP 请求直接发送到 Prisma 服务器的 Management API,则需要生成 JWT 并将其附加到 HTTP 头的 Authorization 字段(前缀 Bearer 和空格)。

注意

Prisma CLI 有一个隐藏的帮助器命令,用于生成 Management API 令牌:prisma cluster-token。在运行之前,你需要设置 PRISMA_MANAGEMENT_API_SECRET 环境变量。请注意,此命令未正式记录,可能会更改,恕不另行通知。

Demo Servers (Prisma Cloud) Prisma 的 Demo Servers 都在Prisma cloud运行 Prisma 服务器。Demo Servers 的使用是免费的!

注意

Demo Servers 不能在生产中使用,因为它们受传输速率限制并且在存储方面存在限制。

Prisma 的 Demo Servers 有两种regions可供选择:

欧盟(爱尔兰) 美国西部(俄勒冈) 为了使用 Prisma 的 Demo Servers,你需要在 Prisma cloud 登录。

Service endpoints 部署到 Prisma Demo 服务器的 Prisma 服务的端点与在“常规”Prisma 服务器上运行的服务的端点略有不同。

部署到 Prisma Demo 服务器的服务在其 URL 中有一个额外的路径组件(前缀为服务的名称和阶段),即 Prisma Cloud 工作区的名称。

以下面的端点为例: https://eu1.prisma.sh/jane-doe/myservice/dev

此端点对有关服务的以下信息进行编码:

eu1.prisma.sh:该服务被部署到EU区域。(US区域使用us1.prisma.sh)。 jane-doe:在 Prisma cloud 工作区的名称。 myservice:该服务的名称。 dev:服务版本。 使用 目标用户:

测试原型 学习 没有明显的用户群的个人项目 限制 速率限制 Prisma 的 Demo Servers 是速率限制为每 10 秒可发生 10 个请求(平均)。

如果超过这个速度,请求被排队在内存中。如果队列超过 25 个请求,立即返回一个错误。

header 字段 throttled-by 包含在 HTTP 响应中。它表示请求因节流而延迟了多长时间(以毫秒为单位)。

存储 在 Demo Servers 上运行的服务 Prisma 的存储容量的上限是 100MB。

认证 登录 用 Demo Servers 部署 Prisma services 之前,Prisma CLI 需要与 Prisma cloud 进行身份验证。

要用 CLI 登录到 Prisma cloud,使用下面的命令:

prisma login 此命令会打开需要提供你的 Prisma cloud 凭据的浏览器窗口。如果凭据有效,则 CLI 在〜/.prisma/config.yml为cloudSessionKey生成 Prisma cloud token,并将其存储。

此 token 在后续请求用于对 Demo Servers 进行身份验证。

退出登录 从 Prisma CLI 注销,使用PRISMA logout命令。它只是从〜/.prisma/config.yml删除cloudSessionKey。

价钱 FREE

Database Connector (MySQL) 数据库连接器是 Prisma 的和底层数据库之间的link。

MySQL 的连接器用于将一个 Prisma 服务器连接到MySQL 的数据库。

数据库连接器的核心是:

翻译传入 GraphQL 查询到 SQL 语句 根据查询结果生成返回数据 执行数据库迁移 Migrations(可选) 使用 Docker 配置 Prisma 服务器时,需要指定要使用哪个连接器才能连接到某个数据库。你可以通过 connector 属性提供此信息:

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: mysql migrations: ENABLE_DB_MIGRATIONS host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD connectionLimit: YOUR_CONNECTION_LIMIT 迁移 Migrations 在PRISMA_CONFIG环境变量migrations属性指定的数据库连接器是否能够更改基础数据库模式。

对于现有的数据库(生产环境),将 Prisma 服务器配置为不迁移已连接的数据库很有用。这可确保你不会意外地引入不需要的架构更改或丢失数据。

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: postgres migrations: false host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD 数据库迁移 通过 Prisma 启用迁移 如果migrations被设置为 TRUE;则部署 Prisma 服务将改变已连接数据库的结构。在这些情况下,Prisma CLI 将是基于服务的数据模型管理数据库结构的主要接口:

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: postgres migrations: true host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD 禁用 Prisma 迁移 如果migrations设置为FALSE,数据库架构被"锁定",Prisma 将不对其执行任何修改:

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: postgres migrations: false host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD 管理数据库连接 PRISMA_CONFIG 中的 connectionLimit 属性定义 Prisma 服务将使用的数据库连接数。

注意

connectionLimit需要至少被设定为2。始终为 Management API 保留一个连接,所有其他连接用于 Prisma 服务。

Database Connector 使用 Docker 配置 Prisma 服务器时,需要指定要使用哪个连接器才能连接到某个数据库。你可以通过 connector 属性提供此信息:

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: mysql | postgres | mongo migrations: ENABLE_DB_MIGRATIONS host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD connectionLimit: YOUR_CONNECTION_LIMIT 迁移示例

PRISMA_CONFIG: | managementApiSecret: YOUR_PRISMA_MANAGEMENT_API_SECRET port: 4466 databases: default: connector: postgres migrations: ENABLE_DB_MIGRATIONS host: YOUR_DATABASE_HOST port: YOUR_DATABASE_PORT user: YOUR_DATABASE_USER password: YOUR_DATABASE_PASSWORD connectionLimit: YOUR_CONNECTION_LIMIT Channels & Releases 发布版本可让你使用不同的 Prisma 版本。如果你想在正式发布之前测试新功能,这可能会有所帮助。

有三种channels:stable, beta, alpha

了解更多关于Prisma 的发布过程这里。

Stable 始终运行在生产服务器上的稳定版本。在稳定频道发布遵循双周节奏,相比测试只包括非常小的改动。这可确保版本中的功能组合已在 beta 上进行了全面测试。

Beta 测试版版本上的功能经过测试,在最终版本发布之前不太可能发生变化。测试版频道是尝试新功能并在稳定版本中提供反馈之前提供反馈的绝佳方式。

Αlpha alpha 版本非常适合希望尽早尝试新功能的贡献者和开发人员。alpha 版本上的功能尚未经过全面测试,在将它们包含在稳定版本中之前可能会发生重大变化。

使用 alpha 和 beta 版本 稳定的版本是你 Prisma 的所做的一切默认。无论是安装 Prisma CLI 还是拉动最新的 Docker 镜像,你都可以下载最新的稳定版本。

要使用 Alpha 和 Beta 版本,请按照下面列出的步骤操作。

测试版 Prisma CLI 使用 CNPM 安装BetaPrisma CLI:

cnpm install -g prisma@beta Docker image 你可以在 Docker Hub 上找到最新的 beta 镜像,或者在使用 Docker CLI 时使用VERSION-beta 标记(VERSION是你要定位的 Prisma 版本的占位符):

docker pull prismagraphql/prisma:1.33-beta Αlpha 使用 CNPM 安装 Prisma CLI 的 alpha 版本:

Prisma CLI

cnpm install -g prisma@alpha Docker image

docker pull prismagraphql/prisma:1.33-alpha 完整的 docker-compose 配置属性示例:

version: '3' services: prisma: image: prismagraphql/prisma:LATEST_PRISMA_VERSION restart: always ports: