Skip to main content

使用 JavaScript 在 GraphQL 解析器中自动补齐

问题

¥Problem

将 GraphQL 与 TypeScript 结合使用时,你始终会在 GraphQL 解析器中自动补齐 Prisma Client 实例,因为这样就可以输入 context 对象 - 无论人们首先使用 Nexus、TypeGraphQL 还是 SDL。这对自动补齐和防止不必要的错误有很大帮助。

¥When using GraphQL with TypeScript, you always get autocompletion for the Prisma Client instance in your GraphQL resolvers because then the context object can be typed – no matter if folks are using Nexus, TypeGraphQL or SDL first. This immensely helps with autocompletion and preventing unwanted errors.

不幸的是,当你使用纯 JavaScript 时,这需要付出更多的努力。假设我们有一个像这样的解析器:

¥Unfortunately, this needs a little more effort when you're working in plain JavaScript. Suppose we have a resolver like this:

filterPosts: (parent, args, ctx) => {
return ctx.prisma.post.findMany({
where: {
OR: [
{ title: { contains: args.searchString } },
{ content: { contains: args.searchString } },
],
},
})
}

现在,每当你键入 ctx. VS Code 时,都会在自动补齐中提供不必要的选项,这是不可取的。

¥Now whenever you type ctx. VS Code will provide unnecessary options in the autocomplete which is undesirable.

Unwanted autocomplete values by VSCode

VS Code 不知道 context 对象的类型,因此无法为其提供任何智能感知,这就是显示不需要的建议的原因。

¥VS Code doesn't know the type of the context object so it can't provide any intellisense for it, which is why unwanted suggestions are displayed.

解决方案

¥Solution

为了解决这个问题,你需要将名为 typedefJS 文档 注释添加到 "import" 中 PrismaClient 实例的正确类型。

¥To overcome this, you need to add a JSDoc comment named typedef to "import" the correct type of your PrismaClient instance.

// Add this to the top of the file

/**

* @typedef { import("@prisma/client").PrismaClient } Prisma
*/

注意:你可以了解有关 JSDoc 此处 的更多信息。

¥Note: You can learn more about JSDoc here.

最后,你需要输入解析器参数。为了简单起见,忽略 parentargs 参数。所以解析器现在应该如下所示:

¥Finally, you need to type your resolver arguments. For simplicity, ignore the parent and args parameters. So the resolver should now look like this:

/**

* @param {any} parent

* @param {{ searchString: string }} args

* @param {{ prisma: Prisma }} ctx
*/
filterPosts: (parent, args, ctx) => {
return ctx.prisma.post.findMany({
where: {
OR: [
{ title: { contains: args.searchString } },
{ content: { contains: args.searchString } },
],
},
})
}

这将告诉 VS Code context 有一个名为 prisma 的属性,类型是上面 @typedef 中定义的 Prisma

¥This will tell VS Code that the context has a property named prisma and the type is Prisma which was defined in the @typedef above.

瞧,纯 JavaScript 中的自动补齐功能。

¥And voilà, autocompletion in plain JavaScript.

The correct parameters for context are obtained

最终文件应类似于:

¥The final file should look something like:

/**

* @typedef { import("@prisma/client").PrismaClient } Prisma

* @typedef { import("@prisma/client").UserCreateArgs } UserCreateArgs
*/

const { makeExecutableSchema } = require('graphql-tools')

const typeDefs = `
type User {
email: String!
id: ID!
name: String
posts: [Post!]!
}

type Post {
author: User
content: String
id: ID!
published: Boolean!
title: String!
}


type Query {
feed: [Post!]!
filterPosts(searchString: String): [Post!]!
post(where: PostWhereUniqueInput!): Post
}

type Mutation {
createDraft(authorEmail: String, content: String, title: String!): Post!
deleteOnePost(where: PostWhereUniqueInput!): Post
publish(id: ID): Post
signupUser(data: UserCreateInput!): User!
}

input PostWhereUniqueInput {
id: ID
}

input UserCreateInput {
email: String!
id: ID
name: String
posts: PostCreateManyWithoutPostsInput
}

input PostCreateManyWithoutPostsInput {
connect: [PostWhereUniqueInput!]
create: [PostCreateWithoutAuthorInput!]
}

input PostCreateWithoutAuthorInput {
content: String
id: ID
published: Boolean
title: String!
}
`

const resolvers = {
Query: {
/**

* @param {any} parent

* @param {any} args

* @param {{ prisma: Prisma }} ctx
*/
feed: (parent, args, ctx) => {
return ctx.prisma.post.findMany({
where: { published: true },
})
},
/**

* @param {any} parent

* @param {{ searchString: string }} args

* @param {{ prisma: Prisma }} ctx
*/
filterPosts: (parent, args, ctx) => {
return ctx.prisma.post.findMany({
where: {
OR: [
{ title: { contains: args.searchString } },
{ content: { contains: args.searchString } },
],
},
})
},
/**

* @param {any} parent

* @param {{ where: { id: string }}} args

* @param {{ prisma: Prisma }} ctx
*/
post: (parent, args, ctx) => {
return ctx.prisma.post.findUnique({
where: { id: Number(args.where.id) },
})
},
},
Mutation: {
/**

* @param {any} parent

* @param {{ title: string, content: string, authorEmail: (string|undefined) }} args

* @param {{ prisma: Prisma }} ctx
*/
createDraft: (parent, args, ctx) => {
return ctx.prisma.post.create({
data: {
title: args.title,
content: args.content,
published: false,
author: args.authorEmail && {
connect: { email: args.authorEmail },
},
},
})
},
/**

* @param {any} parent

* @param {{ where: { id: string }}} args

* @param {{ prisma: Prisma }} ctx
*/
deleteOnePost: (parent, args, ctx) => {
return ctx.prisma.post.delete({
where: { id: Number(args.where.id) },
})
},
/**

* @param {any} parent

* @param {{ id: string }} args

* @param {{ prisma: Prisma }} ctx
*/
publish: (parent, args, ctx) => {
return ctx.prisma.post.update({
where: { id: Number(args.id) },
data: { published: true },
})
},
/**

* @param {any} parent

* @param {UserCreateArgs} args

* @param {{ prisma: Prisma }} ctx
*/
signupUser: (parent, args, ctx) => {
return ctx.prisma.user.create(args)
},
},
User: {
/**

* @param {{ id: number }} parent

* @param {any} args

* @param {{ prisma: Prisma }} ctx
*/
posts: (parent, args, ctx) => {
return ctx.prisma.user
.findUnique({
where: { id: parent.id },
})
.posts()
},
},
Post: {
/**

* @param {{ id: number }} parent

* @param {any} args

* @param {{ prisma: Prisma }} ctx
*/
author: (parent, args, ctx) => {
return ctx.prisma.post
.findUnique({
where: { id: parent.id },
})
.author()
},
},
}

const schema = makeExecutableSchema({
resolvers,
typeDefs,
})

module.exports = {
schema,
}