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.