Prisma
Prisma ORM凭借其直观的数据模型、自动迁移、类型安全和自动完成功能,使开发人员在使用数据库时获得了全新的体验。
@gqloom/prisma
提供了 GQLoom 与 Prisma 的集成:
- 从 Prisma Schema 生成丝线;
- 从 Prisma 快速生成 CRUD 操作。
安装
npm install @gqloom/core @gqloom/prisma
在 Prisma 文档中,你可以找到更多关于安装的信息。
配置
在 prisma/schema.prisma
文件中定义你的 Prisma Schema:
generator client {
provider = "prisma-client-js"
}
generator gqloom {
provider = "prisma-gqloom"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
generator 参数
generator
接受以下参数:
参数 | 说明 | 默认值 |
---|
gqloomPath | GQLoom 包的路径。 | @gqloom/prisma |
clientOutput | Prisma 客户端的路径。 | node_modules/@prisma/client |
output | 生成的文件所在的文件夹路径。 | node_modules/@gqloom/prisma/generated |
commonjsFile | CommonJS 文件的文件名。使用 "" 将跳过 CommonJS 文件的生成。 | index.cjs |
moduleFile | ES 模块文件的文件名。使用 "" 将跳过 ES 模块文件的生成。 | index.js |
typesFiles | TypeScript 声明文件的文件名。使用 [] 将跳过 TypeScript 声明文件的生成。 | ["index.d.ts"] |
生成丝线
使用丝线
在生成丝线后,我们可以在 resolver
中使用:
import { resolver, query, field, weave } from '@gqloom/core'
import { ValibotWeaver } from '@gqloom/valibot'
import * as p from '@gqloom/prisma/generated'
import * as v from 'valibot'
import { PrismaClient } from '@prisma/client'
const db = new PrismaClient({})
const userResolver = resolver.of(p.User, {
user: query(p.User.nullable(), {
input: { id: v.number() },
resolve: ({ id }) => {
return db.user.findUnique({ where: { id } })
},
}),
posts: field(p.Post.list(), async (user) => {
const posts = await db.user.findUnique({ where: { id: user.id } }).posts()
return posts ?? []
}),
})
const postResolver = resolver.of(p.Post, {
author: field(p.User, async (post) => {
const author = await db.post.findUnique({ where: { id: post.id } }).author()
return author!
}),
})
export const schema = weave(ValibotWeaver, userResolver, postResolver)
隐藏字段
@gqloom/prisma
默认将暴露所有字段。如果你希望隐藏某些字段,你可以使用 field.hidden
:
const postResolver = resolver.of(p.Post, {
author: field(p.User, async (post) => {
const author = await db.post.findUnique({ where: { id: post.id } }).author()
return author!
}),
authorId: field.hidden,
})
在上面的代码中,我们隐藏了 authorId
字段,这意味着它将不会出现在生成的 GraphQL Schema 中。
使用线筒
@gqloom/prisma
提供了 PrismaModelBobbin
来帮助你创建线筒。
使用线筒,你可以快速定义常用的查询、变更和字段,线筒还预置了常见操作输入的输入类型,使用线筒可以大大减少样板代码,这在快速开发时非常有用。
import * as p from '@gqloom/prisma/generated'
import { PrismaModelBobbin } from '@gqloom/prisma'
import { PrismaClient } from '@prisma/client'
const db = new PrismaClient({})
const userBobbin = new PrismaModelBobbin(p.User, db)
const postBobbin = new PrismaModelBobbin(p.Post, db)
在上面的代码中,我们创建了 User
和 Post
模型的线筒。PrismaModelBobbin
接受两个参数,第一个是作为丝线的模型,第二个是 PrismaClient
实例。
关系字段
线筒提供了 relationField
方法来定义关系字段:
const userResolver = resolver.of(p.User, {
user: query(p.User.nullable(), {
input: { id: v.number() },
resolve: ({ id }) => {
return db.user.findUnique({ where: { id } })
},
}),
posts: field(p.Post.list(), async (user) => {
const posts = await db.user.findUnique({ where: { id: user.id } }).posts()
return posts ?? []
}),
posts: userBobbin.relationField('posts'),
})
const postResolver = resolver.of(p.Post, {
author: field(p.User, async (post) => {
const author = await db.post.findUnique({ where: { id: post.id } }).author()
return author!
}),
author: postBobbin.relationField('author'),
authorId: field.hidden,
})
在上面的代码中,我们使用 userBobbin.relationField('posts')
和 postBobbin.relationField('author')
来定义关系字段。
relationField
方法接受一个字符串参数,表示关系字段的名称。
查询
线筒预置了常用的查询,你可以直接使用它们:
const userResolver = resolver.of(p.User, {
user: query(p.User.nullable(), {
input: { id: v.number() },
resolve: ({ id }) => {
return db.user.findUnique({ where: { id } })
},
}),
user: userBobbin.findUniqueQuery(),
posts: userBobbin.relationField('posts'),
})
在上面的代码中,我们使用 userBobbin.findUniqueQuery()
来定义 user
查询。线筒将自动创建输入类型和解析函数。
预置查询
线筒预置了以下查询:
- countQuery
- findFirstQuery
- findManyQuery
- findUniqueQuery
变更
线筒预置了常用的变更,你可以直接使用它们:
const postResolver = resolver.of(p.Post, {
createPost: postBobbin.createMutation(),
author: postBobbin.relationField('author'),
authorId: field.hidden,
})
在上面的代码中,我们使用 postBobbin.createMutation()
来定义 createPost
变更。线筒将自动创建输入类型和解析函数。
预置变更
线筒预置了以下变更:
- createMutation
- createManyMutation
- deleteMutation
- deleteManyMutation
- updateMutation
- updateManyMutation
- upsertMutation
自定义输入
线筒预置的查询和变更支持自定义输入,你可以通过 input
选项来定义输入类型:
const userResolver = resolver.of(p.User, {
user: userBobbin.findUniqueQuery({
input: valibotSilk(
v.pipe(
v.object({ id: v.number() }),
asInputArgs(),
v.transform(({ id }) => ({ where: { id } }))
)
),
}),
posts: userBobbin.relationField('posts'),
})
在上面的代码中,我们使用 valibotSilk
来定义输入类型,
v.object({ id: v.number() })
定义了输入对象的类型,
asInputArgs()
将这个对象标注为 GraphQL 的输入参数,
v.transform(({ id }) => ({ where: { id } }))
将输入参数转换为 Prisma 的查询参数。
添加中间件
线筒预置的查询、变更和字段支持添加中间件,你可以通过 middlewares
选项来定义中间件:
const postResolver = resolver.of(p.Post, {
createPost: postBobbin.createMutation({
middlewares: [
async (next) => {
const user = await useLoggedUser()
if (user == null) throw new GraphQLError('Please login first')
return next()
},
],
}),
author: postBobbin.relationField('author'),
authorId: field.hidden,
})
在上面的代码中,我们使用 middlewares
选项来定义中间件,
async (next) => { ... }
定义了一个中间件,
useLoggedUser()
是一个自定义的函数,用于获取当前登录的用户,
如果用户未登录,则抛出一个错误,否则调用 next()
继续执行。
创建 Resolver
你可以从线筒中直接创建一个 Resolver:
const userResolver = userBobbin.resolver()
在上面的代码中,我们使用 userBobbin.resolver()
来创建一个 Resolver。
这个Resolver 将包含线筒中所有的查询、变更和字段。
自定义类型映射
为了适应更多的 Prisma 类型,我们可以拓展 GQLoom 为其添加更多的类型映射。
首先我们使用 PrismaWeaver.config
来定义类型映射的配置。这里我们导入来自 graphql-scalars 的 GraphQLDateTime
,当遇到 DateTime
类型时,我们将其映射到对应的 GraphQL 标量。
import { GraphQLDateTime } from 'graphql-scalars'
import { PrismaWeaver } from '@gqloom/prisma'
export const prismaWeaverConfig = PrismaWeaver.config({
presetGraphQLType: (type) => {
switch (type) {
case 'DateTime':
return GraphQLDateTime
}
},
})
在编织 GraphQL Schema 时传入配置到 weave 函数中:
import { weave } from "@gqloom/core"
export const schema = weave(prismaWeaverConfig, userResolver, postResolver)
默认类型映射
下表列出了 GQLoom 中 Prisma 类型与 GraphQL 类型之间的默认映射关系:
Prisma 类型 | GraphQL 类型 |
---|
Int @id | GraphQLID |
String @id | GraphQLID |
BigInt | GraphQLInt |
Int | GraphQLInt |
Decimal | GraphQLFloat |
Float | GraphQLFloat |
Boolean | GraphQLBoolean |
DateTime | GraphQLString |
String | GraphQLString |