I'm using GraphQL.
I'm able to pass one argument in a field. But I would like to know how to pass multiple arguments to a field.
This is my code:
GraphlQL Object type: Price availability
const priceAvailability = new GraphQLObjectType({
name: "priceAvailability",
description: "Check price and availability of article",
fields: () => ({
articleID: {
type: GraphQLString
},
priceType:{
type:GraphQLString
},
stockAvailability: {
type: StockAvailabilityType,
resolve(parentValue, args) {
// stuff to get the price and availability
return (data = getStockAvailability.getStockAvailability(
parentValue.isbn, parentValue.omgeving
));
}
}
})
});
The root query
const RootQuery = new GraphQLObjectType({
name: "RootQuery",
fields: () => ({
price: {
type: new GraphQLList(priceAvailability),
args: [{
articleID: {
type: new GraphQLList(GraphQLString),
description:
'List with articles. Example: ["artid1","artid2"]'
},
priceType: {
type: new GraphQLList(GraphQLString) ,
description:
'PriceType. Example: "SalePrice","CurrentPrice"'
}]
},
resolve: function(_, { articleID , priceType}) {
var data = [];
// code to return data here
return data;
}
}
})
});
Schema
module.exports = new GraphQLSchema({
query: RootQuery
});
This is the query I use in GraphiQL to test:
{
query: price(articleID:"ART03903", priceType:"SalePrice" ){
stockAvailability {
QuantityAvailable24hrs
QuantityAvailable48hrs
}
}
}
I can get the articleID via parentValue.articleID, but I have issues with getting parentValue.priceType.
Also GraphiQL tells me that priceType does not exists:
Unknown argument “priceType”. On field “price” of type “RootQuery”
args for a field takes an object instead of an array. Try:
args: {
articleID: {
type: new GraphQLList(GraphQLString),
description: 'List with articles. Example: ["artid1","artid2"]'
},
priceType: {
type: new GraphQLList(GraphQLString) ,
description: 'PriceType. Example: "SalePrice","CurrentPrice"'
},
}
Related
I have a rather tricky question about GraphQl and multi-tenancy.
Let's assume there are 3 tables, OWNER, HOUSE and TENANTS. I will describe them in Sequelize and GraphQl pseudo code:
Owner table (has multiple houses and multiple tenants)
const OWNER = sequelize.define('owner', {
ownerId: type: Sequelize.INTEGER,
name: type: Sequelize.STRING
}
OWNER.associate = models => {
models.owner.hasMany(models.house, {foreignKey: {name: 'ownerId', field: 'ownerId'}})
models.owner.hasMany(models.tenant, {foreignKey: {name: 'ownerId', field: 'ownerId'}})
}
House table (belongs to owner and has multiple tenants)
const HOUSE = sequelize.define('house', {
houseId: type: Sequelize.INTEGER,
ownerId: type: Sequelize.INTEGER,
name: type: Sequelize.STRING
}
HOUSE.associate = models => {
models.house.belongsTo(models.owner, {foreignKey: {name: 'ownerId', field: 'ownerId'}})
models.house.hasMany(models.tenant, {foreignKey: {name: 'houseId', field: 'houseId'}})
}
Tenant table (belongs to owner and house)
const TENANT = sequelize.define('tenant', {
tenantId: type: Sequelize.INTEGER,
ownerId: type: Sequelize.INTEGER,
houseId: type: Sequelize.INTEGER,
name: type: Sequelize.STRING
}
TENANT.associate = models => {
models.tenant.belongsTo(models.owner, {foreignKey: {name: 'ownerId', field: 'ownerId'}})
models.tenant.belongsTo(models.house, {foreignKey: {name: 'houseId', field: 'houseId'}})
}
The owner graphql object
const OwnerType = new GraphQLObjectType({
name: 'Owner',
fields: () => ({
ownerId: { type: GraphQLInt },
name: { type: GraphQLString },
houses: {
type: GraphQLList(HouseType),
resolve(owner) {
return owner.getHouse()
}
},
houseById: {
type: HouseType,
args: <args is not defined>
resolve(owner) {
return <???>
}
},
})
})
Here are a few simple GraphQL queries:
ownerById = {
type: OwnerType,
args: {
ownerId: { type: GraphQLInt },
},
resolve(parents, args){
return models.owner.findOne({ where: args })
}
}
houses = {
type: GraphQLList(HouseType),
resolve(parents, args){
return models.house.findAll()
}
}
houseById = {
type: HouseType,
args: {
houseId: { type: GraphQLInt },
},
resolve(parents, args){
return models.house.findOne({ where: args })
}
}
tenants = {
type: GraphQLList(TenantType),
resolve(parents, args){
return models.tenant.findAll()
}
}
These client queries work:
{
ownerById(ownerId: 1) {
ownerId
name
house {
houseId
name
}
}
}
{
houseById(houseId: 2) {
houseId
name
tenant {
tenantId
name
}
}
}
What I need to make multi-tenancy to work is something like that:
{
ownerById(ownerId: 1) {
ownerId
name
houseById(houseId: 2) {
houseId
name
tenant {
tenantId
name
}
}
}
}
Is there a way to archive this or is that out of scope what GraphQl can do?
If yes, how would the graphql object houseById query look like?
Thanks in advance.
Unless I'm missing something, it seems like your resolver for houseById would not be that different from the resolver for the houses field on the same type.
houseById: {
type: HouseType,
args: {
houseId: { type: GraphQLInt },
},
async resolve(owner, { houseId }) {
const houses = await owner.getHouses({ where: { id: houseId } })
return houses[0]
}
},
For a HasMany association, the getter for the target model resolves to an array of instances. So we need to grab that array first and then return just the first item in it, since our field represents a single object and not a list. If you don't want to use async/await, you can also do:
return owner.getHouses({ where: { id: houseId } })
.then(houses => houses[0])
It would also be worth mentioning that this sort of pattern for a schema defies convention. Rather than having a houses field, a houseById field, a houseBySomeOtherArg field, etc., consider exposing a single houses field with one or more arguments like id, name or whatever filter criteria you want to provide. Your field can then just filter the houses based on whatever arguments are passed in, or return all results if no filter arguments were provided.
I've just started to learn GraphQL recently and have decided to implement it in a react based polling app where users can create and vote on polls.
I've created a mongoose model that looks like this https://github.com/luckyrose89/Voting-App/blob/master/backend/models/poll.js.
I'm facing an issue with adding upvotes to a poll option while writing Graphql mutations. So far my schema looks like this:
const AnswerType = new GraphQLObjectType({
name: "Answer",
fields: () => ({
id: { type: GraphQLID },
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionType = new GraphQLObjectType({
name: "Question",
fields: () => ({
id: { type: new GraphQLNonNull(GraphQLID) },
question: { type: GraphQLString },
answer: { type: GraphQLList(AnswerType) }
})
});
const AnswerTypeInput = new GraphQLInputObjectType({
name: "AnswerInput",
fields: () => ({
option: { type: GraphQLString },
votes: { type: GraphQLInt }
})
});
const QuestionTypeInput = new GraphQLInputObjectType({
name: "QuestionInput",
fields: () => ({
question: { type: new GraphQLNonNull(GraphQLString) },
answer: { type: new GraphQLNonNull(GraphQLList(AnswerTypeInput)) }
})
});
const Mutation = new GraphQLObjectType({
name: "Mutation",
fields: {
addPoll: {
\\\\ code here
},
deletePoll: {
\\\\\ code here
},
upvotePoll: {
type: QuestionType,
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
resolve(parent, args) {}
}
}
});
So I've defined my types and I can add and delete polls and access a single poll(I've skipped my queries section here). But I don't understand how to access a single poll's AnswerType object without retrieving unnecessary data and use it to write my upVote mutation.
I hope someone can guide me with this
I'm trying to create a dynamic GraphQLObjectType with graphQl, something like this:
export const Project = (data) => {
return new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: {
type: GraphQLString
},
type: {
type: GraphQLString
},
author: {
type: User,
resolve: (root, args, req) => {
...
}
}
})
})
};
I call this model on my query in this way:
getProjectById: {
type: Project(structure),
args: {
id: { type: GraphQLString }
},
resolve(source, args, req) {
const projectService = new ProjectService(req);
return projectService.getProjectById(args.id)
}
}
the problem is that doing this I get this error:
Schema must contain unique named types but contains multiple types
named "Project"
where is the error? do you have some advice? many thanks
The call Project(structure) in turn calls new GraphQLObjectType({name: 'Project',...}) . If you invoke Project(structure) more than once, you try to declare multiple GraphQLObjectTypes with the same name (which makes no sense).
If you would create/declare GraphQLObjectType dynamically, you have to generate a unique name property. E.g. like this:
// assuming data.name is unique
export const Project = (data) => {
return new GraphQLObjectType({
name: `Project${data.name}`,
...
})
}
I am trying to create a Node.js graphql server in Typescript. I am using Express and express-graphql. I have some issues with how to structure my mutation when I want to create a new User.
My goal is to be able to use a mutation like this:
mutation {
user {
create(
data: {
name: "Foo Bar"
}
) {
id,
name
}
}
}
Here is my User types:
import {
GraphQLObjectType,
GraphQLNonNull,
GraphQLBoolean,
GraphQLString,
GraphQLInputObjectType
} from 'graphql';
export const UserType = new GraphQLObjectType({
name: 'User',
description: 'A user of the application',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
description: 'The id of the user',
},
name: {
type: new GraphQLNonNull(GraphQLString),
description: 'The name of the user',
}
})
});
export const UserInputType = new GraphQLInputObjectType({
name: 'UserInputType',
description: 'User payload definition',
fields: () => ({
name: { type: new GraphQLNonNull(GraphQLString) }
})
});
Here is my attempt at defining the mutation on the server:
// ../user/user-mutations.ts
export const userMutations = {
user: {
type: new GraphQLObjectType({
name: 'CreateUser',
fields: {
create: {
type: UserType,
args: {
data: {
type: new GraphQLNonNull(UserInputType),
}
},
resolve: async (rootValue, { data }) => {
return Object.assign(data, {
id: '123'
});
}
}
}
})
}
};
My errors/output:
{
"errors": [
{
"message": "Cannot convert undefined or null to object",
"locations": [
{
"line": 36,
"column": 3
}
],
"path": [
"user"
]
}
],
"data": {
"user": null
}
}
Question 1: Is this way of structuring a mutation not optimal? Should I rather do something like:
mutation {
createUser(
name: "Foo Bar"
) {
id,
name
}
}
Question 2: If my first structure is fine, how can I fix the structure of my mutation on the server to create my user and return the values requested?
Edit: Here is my top level schema:
import { userQueries } from '../user/user-queries';
export const queries = {
...userQueries
};
import { userMutations } from '../user/user-mutations';
export const mutations = {
...userMutations
};
const rootQuery = new GraphQLObjectType({
name: 'RootQuery',
fields: queries
});
const rootMutation = new GraphQLObjectType({
name: 'RootMutation',
fields: mutations
});
export const schema = new GraphQLSchema({
query: rootQuery,
mutation: rootMutation
});
I have some GraphQL types:
const Atype = new GraphQLObjectType({
name: 'Atype',
fields: {
data: { type: ADataType },
error: { type: GraphQLString },
}
})
and
const Btype = new GraphQLObjectType({
name: 'Btype',
fields: {
data: { type: BDataType },
error: { type: GraphQLString },
}
})
It looks redundant because only data fields are different...
How can I solve it in more elegant way in GraphQL ?
I created a new Type named Mixed just to solve similar issue., Mixed works as mongoose Mixed type, If you're familiar with it.
Create a new file named GraphQLMixed.js or name it whatever you want and place this code inside it.
import { GraphQLScalarType } from 'graphql';
const GraphQLMixed = new GraphQLScalarType({
name: 'Mixed',
serialize: value => value,
parseValue: value => value,
parseLiteral: ast => ast.value
});
export default GraphQLMixed;
Now, Based on your syntax I assume you're using express-graphql, So wherever you want to use this type, Do this
const GraphQLMixed = require('path/to/file/GraphQLMixed');
const Atype = new GraphQLObjectType({
name: 'Atype',
fields: {
data: { type: GraphQLMixed },
error: { type: GraphQLString },
}
})
const Btype = new GraphQLObjectType({
name: 'Btype',
fields: {
data: { type: GraphQLMixed },
error: { type: GraphQLString },
}
})
Hope this works and helps.