Relay flavoured graphql mutation validation errors - graphql

I've greated a simple relay flavoured mutation that works just fine. I've got a simple client side component that commits the mutation with the required data.
My question is how do I send back multiple errors to the client? Currently I can simply throw an error (or reject a promise) in mutateAndGetPayload and I will receive an error on the client side, but this currently only works with a string message. Should I simply reject the promise with a JSON string of an errors array? Or is there a better way?
const createCashAccountMutation = mutationWithClientMutationId({
name: 'CreateCashAccount',
inputFields: {
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'Cash account name'
},
code: {
type: GraphQLString,
description: 'Optional code'
},
businessId: {
type: new GraphQLNonNull(GraphQLString),
description: 'Business ID'
},
currencyId: {
type: new GraphQLNonNull(GraphQLString),
description: 'Currency ID'
},
isActive: {
type: new GraphQLNonNull(GraphQLInt)
}
},
outputFields: {
name: {
type: GraphQLString,
resolve: (payload) => payload.name
},
code: {
type: GraphQLString,
resolve: (payload) => payload.code
},
businessId: {
type: GraphQLString,
resolve: (payload) => payload.businessId
},
currencyId: {
type: GraphQLString,
resolve: (payload) => payload.currencyId
},
isActive: {
type: GraphQLString,
resolve: (payload) => payload.isActive
}
},
mutateAndGetPayload: async (options) => {
throw 'wtf';
return options;
}
});
Update 1.
I've come up with the following example:
const graphQLCashAccount = new GraphQLObjectType({
name: 'cashAccount',
fields: {
name: {
type: GraphQLString,
resolve: (payload) => payload.name
},
code: {
type: GraphQLString,
resolve: (payload) => payload.code
},
businessId: {
type: GraphQLString,
resolve: (payload) => payload.businessId
},
currencyId: {
type: GraphQLString,
resolve: (payload) => payload.currencyId
},
isActive: {
type: GraphQLString,
resolve: (payload) => payload.isActive
}
}
});
const graphQLErrors = new GraphQLList(new GraphQLObjectType({
name: 'errors',
fields: {
key: {
type: GraphQLString,
resolve: (payload) => payload.key
},
message: {
type: GraphQLString,
resolve: (payload) => payload.message
}
}
}));
const graphQlInput = new GraphQLInputObjectType({
name: 'data',
fields: {
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'Cash account name'
},
code: {
type: GraphQLString,
description: 'Optional code'
},
businessId: {
type: new GraphQLNonNull(GraphQLString),
description: 'Business ID'
},
currencyId: {
type: new GraphQLNonNull(GraphQLString),
description: 'Currency ID'
},
isActive: {
type: new GraphQLNonNull(GraphQLInt)
}
}
});
const createCashAccountMutation = mutationWithClientMutationId({
name: 'CreateCashAccount',
inputFields: {
data: {
type: graphQlInput
}
},
outputFields: {
data: {
type: graphQLCashAccount,
resolve: (payload) => payload.data
},
errors: {
type: graphQLErrors,
resolve: (payload) => payload.errors
}
},
mutateAndGetPayload: async (options) => {
const payload = {
errors: [{ key: 'asd', message: 'asd failed' }],
data: options
};
return payload;
}
});
This will actually resolve the transaction and simply return 2 fields, a data field and an errors field. One of them will be populated.
Is this a better approach? I'm stumped on how I should apply the update in Relays fatquery though.
Update 2.
Relay Mutation client side example.
export class NewCashAccountMutation extends Relay.Mutation {
getMutation () {
return Relay.QL`mutation {
createCashAccount
}`;
}
getVariables() {
return { data: this.props.cashAccount };
}
getFatQuery() {
return Relay.QL`
fragment on CreateCashAccountPayload {
data, errors
}
`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
data: this.props.cashAccount.id,
},
}];
}
}

Related

Return the wrong response if type of is number in the graphql

I tried to run the basic query in the graphql. But the below query is always return the no data if the typeof id is number. incase typeof id is string it returns the correct respone.
{
movie(id: 1) {
id
name
}
}
respone
{
"data": {
"movie": null
}
}
attached my code below
const graphql = require("graphql");
const _ = require("lodash");
const {
GraphQLObjectType,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLInt,
GraphQLList,
} = graphql;
const movies = [
{ name: "book1", genre: "Thriller", id: 1, directorId: 1 },
{ name: "book2", genre: "Horror", id: 2, directorId: 1 },
{ name: "book3", genre: "Historical", id: 3, directorId: 2 },
{ name: "book4", genre: "Thriller", id: 4, directorId: 1 },
{ name: "book5", genre: "Science Fiction", id: 5, directorId: 2 },
{ name: "book6", genre: "Horror", id: 6, directorId: 3 },
{ name: "book7", genre: "Romance", id: 7, directorId: 1 },
];
const directors = [
{ name: "directors1", age: "26", id: 1 },
{ name: "directors2", age: "36", id: 2 },
{ name: "directors3", age: "46", id: 3 },
];
const MovieType = new GraphQLObjectType({
name: "Movie",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
genre: { type: GraphQLString },
director: {
type: DirectorType,
resolve: (parent, args) => {
return _.find(directors, { id: parent.directorId });
},
},
}),
});
const DirectorType = new GraphQLObjectType({
name: "Director",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
movies: {
type: MovieType,
resolve: (parent, args) => {
return _.find(movies, { directorId: parent.id });
},
},
}),
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
movie: {
type: MovieType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
console.log(args)
return _.find(movies, { id: args.id });
},
},
director: {
type: DirectorType,
args: {id: {type: GraphQLID}},
resolve(parent, args) {
return _.find(directors, {id: args.id})
}
},
movies: {
type: new GraphQLList(MovieType),
resolve() {
return movies;
},
},
directors: {
type: new GraphQLList(DirectorType),
resolve() {
return directors;
},
},
},
});
module.exports = new GraphQLSchema({
query: RootQuery,
});
not sure what I have missed.

What is the correct way to nest data within a graphql?

I have an address in my database that I've put into a location hash. The hash contains separate keys for streetAddress, city, state, and zipCode. I've nested the data like so in my graphql schema file:
location: {
streetAddress: {
type: String,
required: true,
unqiue: true
},
city: {
type: String,
required: true
},
state: {
type: String,
required: true
},
zipCode: {
type: Number,
required: true
}
}
And I've implemented the schema type like this:
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
id: {type: GraphQLID},
phoneNum: { type: GraphQLString },
location: {
streetAddress: { type: GraphQLString },
city: { type: GraphQLString },
state: { type: GraphQLString },
zipCode: { type: GraphQLInt }
}
...
However, I get an error message saying that the output type is undefined when I try to do a query in graphql:
"message": "The type of RestaurantType.location must be Output Type but got: undefined."
I believe I understand where the error is coming from; I'm assuming that it expects location to have a type as well. What would be the correct syntax for doing this/fixing this error message?
As you guessed, you cannot have nested fields like that. You need to create a separate type for every object in your schema. First create the type:
const Location = new GraphQLObjectType({
name: 'Location',
fields: () => ({
streetAddress: { type: GraphQLString },
city: { type: GraphQLString },
state: { type: GraphQLString },
zipCode: { type: GraphQLInt }
}),
})
Then use it:
const Restaurant = new GraphQLObjectType({
name: 'Restaurant',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
location: { type: Location },
}),
})
or if you don't need to reuse the type, you can define it inline like this:
const Restaurant = new GraphQLObjectType({
name: 'Restaurant',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
location: {
type: new GraphQLObjectType({
name: 'Location',
fields: () => ({
streetAddress: { type: GraphQLString },
city: { type: GraphQLString },
state: { type: GraphQLString },
zipCode: { type: GraphQLInt }
}),
})
},
}),
})

Unknown column 'getContent.movie_id' in 'field list

my whole schema
const Films = new GraphQLObjectType({
name: 'films',
interfaces: () => [MovieStream],
fields: () => ({
movie_id: {
type: GraphQLString,
},
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
Films._typeConfig = {
sqlTable: "films",
uniqueKey: 'id',
}
const MovieStream = new GraphQLInterfaceType({
name: 'MovieStream',
fields: () => ({
id: {
type: GraphQLID,
},
movie_id: {
type: GraphQLString,
},
})
})
MovieStream._typeConfig = {
sqlTable: "movie_streams",
uniqueKey: 'id'
}
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
getContentList:{
type: new GraphQLList(Films),
args: {
id: {
type: GraphQLInt
},
permalink: {
type: GraphQLString
},
language: {
type: GraphQLString
},
content_types_id: {
type: GraphQLString
},
oauth_token:{
type: GraphQLString
}
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo,{}, sql => {
return FilmDb.query(sql).then(function(result) {
return result[0];
});
} ,{dialect: 'mysql'});
},
}
})
})
module.exports = new GraphQLSchema({
query: QueryRoot
})
I have again modified my code still got the error
{
"errors": [
{
"message": "Unknown column 'getContent.movie_id' in 'field list'",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getContentList"
]
}
],
"data": {
"getContentList": null
}
}
My previous post Is it possible to fetch data from multiple tables using GraphQLList
Please check and tell me where i am wrong??? I have already add the field still it does not access the field of that object type

Is left join only one way to fetch data using join monster?

const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
getContentDetails: {
type: new GraphQLList(Films),
args: {
id: {
type: GraphQLInt
},
permalink: {
type: GraphQLString
},
studio_id: {
type: GraphQLString
},
content_types_id: {
type: GraphQLString
}
},
where: (filmTable, args, context) => {
return ` ${filmTable}.permalink = "${args.permalink}" and ${filmTable}.content_types_id = "${args.content_types_id}"`
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo, {}, sql => {
return FilmDb.query(sql).then(function(result) {
return result[0];
});
},{dialect: 'mysql'});
}
}
})
})

graphql mutation & couchbase

I'm starting working with graphQL with couchbase, and I have a small problem when I run a mutation, what I'm doing is save 2 object:
key: email#email.it
{
type: 'credential',
user_id: 'xxxx-xxxx-xxxx-xxxx',
password: 'jknelkjf'
}
key: xxxx-xxxx-xxxx-xxxx
{
type: 'user',
name: 'aaaa',
surname: 'bbbb'
}
this is my mutation function:
export default {
createUser: {
type: User,
args: {
email: { type: GraphQLString },
password: { type: GraphQLString },
name: { type: GraphQLString },
surname: { type: GraphQLString }
},
resolve(source, args, req) {
const _id = uuid();
return new Promise((resolve, reject) => {
bcrypt.hash(args.password, 10)
.then(hash => req.db.insert({}, args.email, {
password: hash,
user_id: _id,
type: "credential"
}))
.then(cred => req.db.insert({}, cred.user_id, {
name: args.name,
surname: args.surname,
type: "user"
}))
.then(user => {
return resolve(user);
})
.catch(err => {
return reject(err);
});
});
}
}
}
and these are my schemas
export const UserCredential = new GraphQLObjectType({
name: 'UserCredential',
fields: () => ({
user_id: { type: GraphQLString },
type: { type: GraphQLString },
id: { type: GraphQLString },
password: { type: GraphQLString }
})
});
export const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
name: { type: GraphQLString },
surname: { type: GraphQLString },
type: { type: GraphQLString },
id: { type: GraphQLString },
credential: {
type: UserCredential,
resolve: (root, args, req) => {
const query = `SELECT META(credential).id,credential.* FROM {{bucket}} as credential WHERE credential.type="credential" AND credential.user_id=$1`;
return new Promise((resolve, reject) => {
req.db.runQuery(null, query, [root.id])
.then((res) => {
return resolve(res[0]);
})
.catch((err) => {
return reject(err);
})
});
}
}
})
});
the problem is that when I run the mutation the result is the user object with the credential field null, but if I add a timeout on the resolve of the mutation I get the user with the credential object...the insert function is something like this:
db.insert(result => {
return db.find(result.id)
})
do you have any advice?

Resources