简体   繁体   中英

graphql-compose, trigger resolver of a nested field

I'm beginner in GraphQL and use it with Node and graphql-compose to create schemas.

I'm not sure if is the correct way to do that: AddressTC is reusable Type, when UserTC or another Type is created or updated, I want to trigger validation automatically of AddressTC . If you know a better way to do that, I'll take it.

// 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 something...
        // 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,
    resolve: async ({args: {data}, context}) => {
        // When address is puted or updated :
        // Make validation
        // Geocode {Lat,Lng} with map provider 
        // Save in DB           
    },
})

As your question, You can use a validation library such as joi , and inside your resolver apply your validation schema.

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
    },
})

I would suggest using a resolver wrapper. These are used for things like checking authentication/permissions but would apply to some sort of generic type validation that you are looking for.

I believe this piece of documentation will help you out for a high level understanding -
https://graphql-compose.github.io/docs/basics/what-is-resolver.html#wrapping-resolver

What you need to understand is how resolverParams work (rp). You can get a lot of information about what is going on in a query and do a lot with wrappers by investigating the rps.

This documentation is specific for a MongoDB implementation but the code can be altered to fit any DB. https://graphql-compose.github.io/docs/plugins/plugin-mongoose.html#access-and-modify-mongoose-doc-before-save

This would be the general layout of the wrapper function I mentioned. The code is abstracted from the MongoDB implementation mentioned above.

The function might look something like this:

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
}

The best way to implement this will be based on your application structure and how you are composing the global schema. I recommend reading through the resolver docs entirely to figure out what makes sense for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM