graphql-compose,触发嵌套字段的解析器

问题描述

我是 GraphQL 的初学者,将它与 Node 和 graphql-compose 一起使用来创建模式。 我不确定这样做是否正确: AddresstC 是可重用的类型,当创建或更新 UserTC 或其他类型时,我想自动触发 AddresstC 的验证。如果你知道更好的方法,我会接受。

// userType.js
// In this example we'll use UserTC but this can another type which also use AdresstC.

const UserTC = schemaComposer.createObjectTC({
    name: 'User',fields: {
        id: 'String',name: 'String',slug: 'String',actived: 'Boolean',registered: 'Boolean',address: AddresstC,}
});

const UserITC = UserTC.getInputTypeComposer()

UserTC.addResolver({
    kind: 'mutation',name: 'create',args: {
        data: UserITC
    },type: UserTC,resolve: async ({args: {data},context}) => {
        // Need to trigger validation and geocode of AddresstC
        // do someting...
        // save in database
    },})

// addresstype.js
// reusable Address Type

const AddresstC = schemaComposer.createObjectTC({
    name: 'Address',description: 'Type of address',fields: {
        street: 'String',number: 'String',postcode: 'String',city: 'String',comment: 'String',country: 'String',quality: QualityETC
    }
});

const AddressITC = AddresstC.getInputTypeComposer()

AddresstC.addResolver({
    kind: 'mutation',name: 'validation',args: {
        data: AddressITC
    },type: AddresstC,context}) => {
        // When address is puted or updated :
        // Make validation
        // Geocode {Lat,Lng} with map provider 
        // Save in DB           
    },})

解决方法

作为您的问题,您可以使用诸如 joi 之类的验证库,并在您的解析器中应用您的验证架构。

import joi from 'joi'

const UserTC = schemaComposer.createObjectTC({
    name: 'User',fields: {
        id: 'String',name: 'String',slug: 'String',actived: 'Boolean',registered: 'Boolean',address: AddressTC,}
});

const UserITC = UserTC.getInputTypeComposer()

/* Create it here,or in the address file and import it here.
   Personally,I place the validation schemas in separate files
*/
const addressTCSchema = Joi.object().keys({
    street: Joi.string().required(),number: Joi.number().required(),city: Joi.string().required(),postcode: Joi.string().required(),comment: Joi.string().required(),country: Joi.string().required(),})

const userTCSchema = Joi.object({
    id: Joi.number().integer().required(),name: Joi.string()
        .pattern(new RegExp('^[a-zA-Z]')).required(),slug: Joi.string()
        .pattern(new RegExp('^[a-zA-Z]')).required(),actived: Joi.boolean().required(),registered: Joi.boolean().required(),address: addressTCSchema
})

UserTC.addResolver({
    kind: 'mutation',name: 'create',args: {
        data: UserITC
    },type: UserTC,resolve: async ({args: {data},context}) => {
        // Need to trigger validation and geocode of AddressTC
        const { error } = userTCSchema.validate(data);
        if (error) {
            // return or send 400 bad request
        }
        // do someting...
        // save in database
    },})
,

我建议使用解析器包装器。 这些用于检查身份验证/权限之类的事情,但也适用于您正在寻找的某种通用类型验证。

我相信这篇文档将帮助您进行高层次的理解 -
https://graphql-compose.github.io/docs/basics/what-is-resolver.html#wrapping-resolver

您需要了解的是 resolverParams 是如何工作的 (rp)。通过调查 rps,您可以获得很多关于查询中发生的事情的信息,并可以使用包装器做很多事情。

本文档专门针对 MongoDB 实现,但可以更改代码以适合任何数据库。 https://graphql-compose.github.io/docs/plugins/plugin-mongoose.html#access-and-modify-mongoose-doc-before-save

这就是我提到的包装函数的总体布局。代码是从上面提到的 MongoDB 实现中抽象出来的。

该函数可能如下所示:

function addressValidationWrapper(resolvers){
  Object.keys(resolvers).forEach((k) => {
    resolvers[k] = resolvers[k].wrapResolve(next => async rp => {
      // check if has address field/type
         // validate address
            // if address valid - resolve
            // else throw error
      return next(rp)
    })
  })
 return resolvers
}

实现这一点的最佳方法将基于您的应用程序结构以及您如何组成全局架构。我建议您完全通读解析器文档,以找出对您有意义的内容。