I am using graphQL, sequelize and nodejs with apollo client.
The data which I am sending from the UI is a nested structure of the following form:
Grandparent
Parent
Child
So a grandparent can have one or more parents and a parent can have one or more children.
I am trying to update my SQLite database, created using sequelize, using one GraphQL mutation. I am able to create a mutation with a flat structure:
schema.gql file contents ->
type Mutation Family(
grandParentName: String,
grandParentHeight: String,
Parent: String,
)
but I want something along the lines of
type Mutation CreateNestedFamily(
grandParentName: String,
grandParentHeight: String,
Parent: CreateParent(
parentName: String,
parentHeight: String
child: CreateChild(
childName: String,
childHeight: String
)
)
)
But I dont know how to achieve this and the graphql documentation around nested mutations is very limited.
Any help would be appreciated!
The way you've phrased you problem is a little strange, but i'll do my best to point you in the right direction.
There's no such thing as a nested mutation in GraphQL, but you can construct input types that allows an arbitrary nesting of data that you pass to a single mutation.
I'll step back from your approach and take a specific problem "I want to create a person and their descendants"
input PersonInput {
name: String!
height: String!
children: [PersonInput!]
}
type Mutation {
createPerson(person: PersonInput!): SomeOutputType
}
Hopefully you can see the recursive nature of this structure. It'd be more difficult to impose a limit on this, i.e to only allow 3 levels deep.
Related
So I'm using apollo-graphql,
I have a subgraph named Course, in this subgraph I have a type Course which looks like this:
type Course {
_id: ID
title: String
}
Now I have a second subgraph named progress, in this one I extend the Course type and add a field:
extend type Course #key(fields: "_id") {
_id: ID #external
totalProgression: Int
}
And now I want to query it like this:
Courses {
_id
title
totalProgression
}
(Courses is a query which returns all courses)
This query works, but the problem is, it's way too slow. And that's because for each course, it is going to make a db request to get the totalProgression. If I add another nested field from another subgraph, it is going to take even longer.
My question here is, is there any way I could make this faster? For example could I make one db request to get all the totalProgression and then putting them in the courses? Without having to merge my subgraphs.
My only solution for now is to get the totalProgressions inside the Courses resolvers, but that would kill the point of making another subgraph. Yes I could juste merge all my subgraphes, but that's not how graphql is supposed to work.
I'm trying to validate a GET request on Nest.js that has multiple queries using Joi. I understand how to use UsePipes and validate a single object on a single param. However I now have an endpoint that has the multiple queries, here is my controller:
#Get(':cpId/search')
#UsePipes(new JoiValidationPipe(queryDTOSchema))
async getSomethingByFilters(
#Param('cpId') cpId: string,
#Query('startDate') startDate?: number,
#Query('endDate') endDate?: number,
#Query('el') el?: string,
#Query('fields') fields?: string,
#Query('keyword') keyword?: string,
#Query('page') page?: number,
#Query('limit') limit?: number,
)...
And UsePipes is now validating the same schema against each of the queries, and I don't understand how to validate each single query separately.
Is there a way to validate each query separately? I couldn't find any references and the only solution I can think of would be to transform all those queries into a single object, which is undesirable in this case.
You can pass a pipe as the second argument of a query-decorator:
#Query('startDate', new JoiValidationPipe(joi.number())) startDate?: number
But I personally would prefer validation as one query object, as it is easier to read and handle:
#Get(':corporatePartnerId/search')
async getEmployeesByFilters(
#Param('corporatePartnerId', new JoiValidationPipe(joi.string())) corporatePartnerId: string,
#Query(new JoiValidationPipe(QueryDtoSchema)) query: QueryDto,
)
Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm
I am currently trying to resolve a simple recipe list that has a reference to ingredients.
The data layout looks like this:
type Ingredient {
name: String!
amount: Int!
unit: Unit!
recipe: Recipe
}
type Recipe {
id: Int!
name: String!
ingredients: [Ingredient]!
steps: [String]!
pictureUrl: String!
}
As I understand it, my resolvers should look like this:
The first one resolves the recipes and second one resolves the ingredient field in the recipe. It can (from my understanding) use the argument provided by recipe. In my recipe object, the ingredient is referenced by id (int), so this should be the argument (at least that's what I think).
var root = {
recipe: (argument) => {
return recipeList;
},
Recipe: {
ingredients: (obj, args, context) => {
//resolve ingredients
}
},
These resolvers are passed to the app like this:
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
rootValue: root,
}));
However, my resolver does not seem to be called. I would like the ingredients to be resolved on the fly when queried in my query.
The endpoint works, but as soon as I query for ingredients, an error with this message "message": "Cannot return null for non-nullable field Ingredient.name.", is returned.
When trying to log the incoming arguments in my resolver, I can see that it is never executed. Unfortunately, I can't find examples on how to do this with express-graphql when using it like I am.
How do I write seperate resolvers for nested types in express-graphQL?
Only resolvers for queries and mutations can be defined through root, and even then this is bad practice. I'm guessing you're building your schema using buildSchema, which is generally a bad idea since the generated schema will only use default resolvers.
The only way to define resolvers for a field like ingredients when using plain GraphQL.js is to not use buildSchema. Instead of generating your schema from a string, you would define it programatically (i.e. defining a GraphQLSchema and all the types it uses).
Doing the above is a huge pain, especially if you already have your schema defined in a string or document. So the alternative option is to use graphql-tools' makeExecutableSchema, which lets you inject those resolvers into your type definitions like you're trying to do. makeExecutableSchema returns a GraphQLSchema object, so you can use it with your existing code (you don't have to change your middleware to apollo-server if you don't want to).
A semi-noob node guy here. In my jade templates ... I'd really love to be able to have this interface:
if currentUser.isMemberOfGroup(name)
-// dosomething
However, determining a user's membership involves interfacing with mongoose ... which is always an async'd affair. Using mongoose docs as an example I would WANT to write something along these lines (pardon me, this is written in coffee):
userSchema.methods.isMemberOfGroup = (name) ->
Club.findOne(name: name).populate({
path: 'members',
model: 'User',
match: { _id: #id }
}).exec (err, club) ->
club.members.length > 0
Is there a way I can get this method to return a bool instead of the immediate return from the exec function? What is the "right" way to design such an interface?
Here's the rest of the Club model:
clubSchema = Mongoose.Schema
name: String
hashTag: String
members: [{ type: Mongoose.Schema.Types.ObjectId, ref: 'User' }]
Does Jade support async functions (it seems it doesn't https://github.com/visionmedia/jade/issues/641)? If not, you'll have to load the value before you render your template.
This is in fact generally a good practice as it allows separation of concerns (the view should not know about the database). By the way, if you need to do a lot of async conditional logic have a look at https://github.com/olalonde/boolasync (a module I wrote). Also, if you need to do multiple database calls, I strongly recommend you use async.series or async.parallel to make your code more readable (don't go nest more than a few levels deep).