Prisma

What Prisma ❓

Prisma是一个能够快速构建Graphql API服务的后端框架。是现代应用程序的数据层。 它通过 Prisma client使用通用数据库抽象替换传统的 ORM 和数据访问层。用于构建 GraphQL 服务器,REST API 等

Why Prisma ❓

  • 各种语言的 Prisma client,如 JavaScript,TypeScript,Flow,Go,Java,Python
  • 支持多个数据库,如 MySQL,PostgreSQL,MongoDB(目前仅以上三种,后续支持更多)
  • 类型安全的数据库访问包括过滤器,聚合,分页和事务
  • 数据库的实时事件系统以获得有关数据库事件的通知
  • 具有简单 SDL 语法的声明性数据建模和迁移(可选)

Prisma 安装与部署

安装

  • 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
    

连接数据库和 Prisma

  • 建立目录

    $ mkdir prismaDemo
    $ cd prismaDemo
    
  • 创建Docker Compose文件

    要在计算机上启动Prisma前,需要一个Docker Compose文件,该文件配置Prisma并指定它可以连接的数据库

    $ touch docker-compose.yml
    
  • 添加Prisma和数据库Docker映像

    将以下内容粘贴到刚创建的Docker Compose文件中,我这里用的是mogondb数据库

    version: '3'
    services:
      prisma:
        image: prismagraphql/prisma:1.34
        restart: always
        ports:
          - '4466:4466'
        environment:
          PRISMA_CONFIG: |
            port: 4466
            databases:
              default:
                connector: mongo
                uri: mongodb://prisma:prisma@mongo
      mongo:
        image: mongo:3.6
        restart: always
        environment:
          MONGO_INITDB_ROOT_USERNAME: prisma
          MONGO_INITDB_ROOT_PASSWORD: prisma
        ports:
          - '27017:27017'
        volumes:
          - mongo:/var/lib/mongo
    volumes:
      mongo: ~
    
  • 启动Prisma和连接的数据库

    要启动Prisma并启动连接的数据库,请运行以下命令

    $ docker-compose up -d
    

    Prisma现在已连接到本地数据库并在上运行http://localhost:4466

  • 配置您的Prisma API

    在endpoint需要匹配运行Prisma的服务器的URL,要为Prisma客户端引导配置文件,请运行以下命令

    $ prisma init --endpoint http://localhost:4466
    
  • 部署Prisma数据模型

    prisma init命令创建了部署prisma数据模型所需的最小设置:prisma.yml和datamodel.prisma

    MongoDB连接器使用了比当前支持的SQL数据库更新的数据模型格式。这个数据模型和prisma.yml需要专门为MongoDB配置

    在prisma.yml中添加databaseType属性

    endpoint: http://localhost:4466
    datamodel: datamodel.prisma
    databaseType: document
    
    • 现在调整datamodel.prisma以使用新的指令
    type User {
      id: ID! @id
      name: String!
    }
    

    使用这些配置文件后,现在可以部署 Prisma API

    $ prisma deploy
    

    现在,就可以开始使用Prisma客户端通过代码与数据库对话

  • 在Prisma Admin中查看和编辑数据

    如果要查看和编辑数据库中的数据,可以使用Prisma Admin。要访问Prisma Admin,需要附加/_admin 到 Prisma 端点,例如:http://localhost:4466/_admin

  • 生成Prisma客户端

    Prisma客户端是一个自定义的、自动生成的库,它连接到Prisma API。需要在prisma.yml的末尾附加以下行:

    generate:
      - generator: javascript-client
        output: ./generated/prisma-client/
    

    现在使用以下命令生成客户端:

    prisma generate
    

    CLI现在将Prisma客户端存储在Prisma.yml中指定的./generated/Prisma client/目录中

  • 准备节点应用程序

    运行以下命令以创建一个空的Node脚本

    touch index.js
    

    接下来,在当前目录中初始化一个空的NPM项目并安装所需的依赖项👇

    npm init -y
    npm install --save prisma-client-lib
    
  • 使用Prisma客户端读取和写入数据

    现在将以下代码添加到index.js

    const { prisma } = require('./generated/prisma-client')
    
    // A `main` function so that we can use async/await
    async function main() {
      // Create a new user called `Doris`
      const newUser = await prisma.createUser({ name: 'Doris' })
      console.log(`Created new user: ${newUser.name} (ID: ${newUser.id})`)
    
      // Read all users from the database and print them to the console
      const allUsers = await prisma.users()
      console.log(allUsers)
    }
    
    main().catch(e => console.error(e))
    

    使用以下命令执行脚本

    node index.js
    

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

  • prisma.yml

    为Prisma services根配置文件(包括服务的端点,密码,数据模型文件的路径,…)

    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(含协议和端口),如localhost:4466

    • Service name

      端点URL的第一个路径组件是Prisma服务的名称,例如my-service。 如果未指定服务名称,则默认为default

    • Service stage

      端点URL的第二个路径组件是服务的stage。 与服务名称一样,这可以是随机字符串

      全部放在一起后,一个服务长这样👉 https://localhost:4466/my-service/dev

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

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

  • datamodel

    在数据模型中,可以定义其Prisma的使用来生成数据库的GraphQL API模型(使用GraphQL的 schema定义语言SDL编写)。存储在一个叫做 datamodel.prisma 的文件中

What datamodel ❓

定义应用程序的数据模型,是Prisma client API的基础(也可以用来对数据库进行迁移)

概述

datamodel有两个主要功能:

  • 定义底层数据库模式和表(在无模式数据库(如MongoDB)的情况下,它们映射到数据库表或等效结构,如documents)
  • 它是Prisma API的自动生成的CRUD和实时操作代码的基础

文件

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

包含datamodel的.graphql文件需要在prisma.yml下的属性datamodel中指定👇

datamodel:
  - types.graphql
  - enums.graphql

如果只有一个文件定义了数据模型,则可以按如下方式指定:

datamodel: datamodel.prisma

对象类型

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

类型具有 name 和一个或多个 fields 字段

  • 类型名称只能包含 字母、数字、字符,需要以大写字母开头。 它们可以包含最多64个字符
  • 字段名称只能包含 字母数字字符,需要以小写字母开头。它们可以包含 最多64个字符
定义对象类型
  • 在datamodel中使用关键字"type"定义对象类型

    type Article {
     id: ID! @id
     title: String!
     text: String
     isPublished: Boolean! @default(value: false)
    }
    
  • 生成type的API操作

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

    • Query 允许你获取该类型的一个或多个节点
    • Mutations 允许你创建,更新或删除该类型的节点
    • Subscriptions 可以让你收到有关该类型节点更改的实时通知(即新节点是created或现有节点是updated或deleted)
一个简单的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或手机号不会相同从而造成重复注册
  • @default 指令设置默认值

创建和更新数据模型就像编写和保存datamodel文件一样。一旦对数据模型感到满意,就可以通过运行prisma deploy来保存文件并将更改应用到Prisma service

使用 Prisma client 读取和写入数据

接上次的demo

  • 查看

    const allUsers = await prisma.users()
    console.log(allUsers)
    
    const user = await prisma.user({ id: '5e0885f5a7b11b0007cf278f' })
    console.log(user);
    
    const usersCalledAlice = await prisma
      .users({
        where: {
          name: 'Annie'
        }
      })
    console.log(usersCalledAlice);
    

    或者可以用下面的这种方法去http://localhost:4466/_admin中查看👇

    {
      users(
        where:{
            id:"5e0885f5a7b11b0007cf278f"
        }) {
        name
      }
    }
    
    {
      users(
        where:{
          name: "Annie"
        }) {
        id
      }
    }
    
    
  • 更新

    const updatedUser = await prisma
    .updateUser({
      where: {
        id: '5e0885f5a7b11b0007cf278f'
      },
      data: {
        name: 'doris'
      }
    })
    console.log(updatedUser);
    
  • 删除

    const deletedUser = await prisma.deleteUser({ id: '5e1095ed24aa9a0007cdef09' })
    const allUser = await prisma.users()
    console.log(allUser)
    

Prisma & GraphQL

Prisma 使用 GraphQL 作为通用数据库抽象,这意味着它将你的数据库转换为 GraphQL API,并能够👇

  • 使用 GraphQL queries 和 mutations 读取和写入数据库
  • 使用 GraphQL subscriptions 接收数据库事件的实时更新
  • 使用 GraphQL SDL 执行迁移并为数据建模

当 Prisma Client 向 Prisma Server 发出请求时,它实际上会生成 GraphQL 操作,这些操作将被发送到 Prisma 的 GraphQL API 。然后,Client 将 GraphQL 响应转换为预期的结果结构,并从调用的方法中将其返回

配置项目

graphql-yoga

是一个基于Express.js,功能齐全的GraphQL服务器,专注于简单的设置、性能和良好的开发体验。使用以下命令将其添加到项目中

$ npm install --save graphql-yoga

定义 GraphQL API

每个GraphQL API都基于一个GraphQL Schema,该 Schema 指定所有API操作和数据结构,是客户端和服务器之间的协定

$ touch schema.graphql

要定义API操作,需要在GraphQL模式中指定查询和变异类型

下面的操作是一个简单的blogging应用程序的示例

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 中指定的模型的直接重新定义

实现解析器函数

下面是如何为 GraphQL 模式中定义的六个API操作实现解析器函数的。将 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 并附加到上下文对象的方法

配置GraphQL服务器

现在需要从 graphql-yoga 中实例化 GraphQLServer,并将 GraphQL schema 及其解析器函数一起传递。还将导入的 prisma Client 实例附加到上下文,以便解析器可以访问它

将下面的内容添加到刚才添加过的 index.js 的文档最下方

const server = new GraphQLServer({
  typeDefs: './schema.graphql',
  resolvers,
  context: {
    prisma,
  },
})
server.start(() => console.log('Server is running on http://localhost:4000'))

启动 GraphQL server

$ node index.js

在playground 中测试

应用层的 GraphQL API 现在公开 schema.GraphQL 中定义的六个操作。http://localhost:4000

  • Create a new User

    mutation {
      createUser(name: "A") {
        id
      }
    }
    
  • Create new draft

    mutation {
      createDraft(
        title: "GraphQL is great", 
        userId: "5e23ffa0857aba00079f1350"
      ){
        id
        published
        author {
          id
        }
      }
    }
    
  • Publish a Post

    mutation {
      publish(postId: "5e24006f857aba00079f1351") {
        id
        title
        published
      }
    }
    
  • Fetch published Posts

    query {
      publishedPosts {
        id
        title
        author {
          id
          name
        }
      }
    }
    

Prisma vs Mongoose

MongoosePrisma
获取单个对象const user = await findById(id)const user = await prisma.user({ id })
获取单个对象的选定标量const user = await findById(id).select(‘id email’)const userFragment = await prisma.user({ id }).$fragment(`fragment NameAndEmail on User { id email }`)
获取关系const userWithPosts = await User.findById(id).populate(‘posts’)const postsByUser = await prisma.user({ id }).posts()
过滤具体值const user = await User.find({name: ‘Alice’})const users = await prisma.users({where: {name: ‘Alice’}})

fragment 👉 细粒度数据访问

使用 $fragment API 功能(基于 GraphQL )指定要检索的字段,而不是查询模型的所有标量字段(这是默认行为)

一些改变

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

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

现在开始所说 Prisma API 是基于以上的数据模型👆

⚠️⚠️⚠️

  • 修改过数据模型之后需要重新部署一下 Prisma API

    $ prisma deploy
    
  • 此外,由于 Prisma Cli 是基于你的数据模型的,因此需要在每次更新数据模型时重新生成它

    $ prisma generate
    

💡你也可以在 Prisma.yml 中添加下面的代码,来确保每次部署后都自动更新 Prisma Cli

hooks:
  post-deploy:
    - prisma generate

操作

  • 现在可以在 index.js 中写入以下代码进行数据的插入👇

    const { prisma } = require('./generated/prisma-client')
    
    async function main() {
      const newUser = await prisma.createUser({
        name: 'Doris',
        email: 'Doris@prisma.io',
        posts: {
          create: [
            {
              title: 'hello~',
            },
            {
              title: 'goodbye~',
            },
          ],
        },
      })
      console.log(`Created new user: ${newUser.name} (ID: ${newUser.id})`)
    
      // const allUsers = await prisma.users()
      // console.log(allUsers)
    
      // const allPosts = await prisma.posts()
      // console.log(allPosts)
    }
    
    main().catch(e => console.error(e))
    
  • 查询关系数据

    可以看到现在的数据模型是有所关联的,使用 Prisma Cli API,可以使用链式方法调用查看(也称为 fluentapi )数据中关联的关系数据 👇

    const { prisma } = require('./generated/prisma-client')
    
    async function main() {
      const postsByUser = await prisma.user({ email: 'bob@prisma.io' }).posts()
      console.log(`All posts by that user: ${JSON.stringify(postsByUser)}`)
    }
    
    main().catch(e => console.error(e))
    

概念

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

Prisma GraphQL schema

Prisma 数据库是定义 Prisma API 的数据类型和操作的 GraphQL schema。 它是自动生成的,并为 service 的数据模型中指定的模型指定 CRUD 和实时操作

  • 检索单个节点

    query {
      user(where: {
        email: "Doris@prisma.io"
      }) {
        id
        name
      }
    }
    // or
    query {
      post(where: {
        id: "5e1b07470274390007fd4eab"
      }) {
        id
        title
      }
    }
    
  • 更新单个POST节点的 title:

    mutation {
      updatePost(
        where: {
          id: "5e1b07470274390007fd4eab"
        }
        data: {
          title: "hi~"
        }
      ) {
        id
        title
      }
    }
    
  • 批量修改

    Mutation 的 updateManyPosts 和 deleteManyPosts 提供了一个 where 参数来选择特定节点,并返回一个带有受影响节点数的 count 字段

    将所有 post 的 published 属性改为 true 👇

    mutation {
      updateManyPosts(
        where: {
          id_in: ["5e1b01090274390007fd4ea8", "5e1b07470274390007fd4eab", "5e1b0d540274390007fd4eae","5e1b01090274390007fd4ea9","5e1b07470274390007fd4eac","5e1b0d540274390007fd4eaf"]
        }
        data: {
          published: true
        }
      ) {
        count
      }
    }
    

Prisma 查询

PRISMA提供两种方法通过关系字段检索节点列表

  • 简单的对象查询(关系查询)

    使用对象查询来获取单个节点或某个对象类型的节点列表。这里使用 posts 查询来获取 Post 节点的列表

    query {
      posts {
        id
        title
        published
      }
    }
    

    ⚠️这里需要说明的一点是,也可以在查询的时候进行筛选,比如下面使用 where 参数为返回的 users 指定条件,筛选所有age大于18岁的User节点 👇当然这里我没有在 user 中添加 age 这个字段,so…自行领会😂

    query {
      users(where: {
        age_gt: 18
      }) {
        id
        name
      }
    }
    
  • 连接查询

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

    对象查询直接返回节点列表。在特殊情况下或使用高级功能时,使用连接查询是首选选项。它们是 Relay connections(中继连接)的延伸(并且完全符合)。Relay connections 的核心思想是提供关于数据中边缘的元信息。例如,每条边不仅可以访问关于相应对象(节点)的信息,还可以与允许实现强大分页的游标相关联

    query {
      postsConnection {
        edges {
          node {
            id
            title
            published
          }
        }
      }
    }
    

查询参数

在整个Prisma API中,您会发现可以提供的查询参数以进一步控制查询响应。查询参数有:

使用orderBy按节点任何字段值排序
通过使用where的标量或关系过滤器在查询中选择节点
查询字符串中使用first、before、last、after和skip对节点分页

排序查询

查询某个类型的所有节点时,可以为每个类型的标量字段提供 orderBy 参数:orderBy: _ASC 或 orderBy: _DESC

  • 按照title升序排序
query {
  posts(orderBy: title_ASC) {
    id
    title
    published
  }
}

⚠️ 你所排序的字段不必在实际查询中选择。如果没有指定顺序,则响应会自动按id字段升序排列

Change Datamodel

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值