Graphql with nested mutations? - graphql

I am trying to figure out how to mutate a nested object with graphql mutations, if possible. For instance I have the following schema:
type Event {
id: String
name: String
description: String
place: Place
}
type Place {
id: String
name: String
location: Location
}
type Location {
city: String
country: String
zip: String
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String): Event
}
schema {
query: Query
mutation: Mutation
}
How can I add the place information inside my updateEvent mutation?

Generally speaking, you should avoid thinking of the arguments to your mutations as a direct mapping to object types in your schema. Whilst it's true that they will often be similar, you're better off approaching things under the assumption that they won't be.
Using your basic types as an example. Let's say I wanted to create a new event, but rather than knowing the location, I just have the longitude/latitude - it's actually the backend that calculates the real location object from this data, and I certainly don't know its ID (it doesn't have one yet!). I'd probably construct my mutation like this:
input Point {
longitude: Float!
latitude: Float!
}
input PlaceInput {
name
coordinates: Point!
}
type mutation {
createEvent(
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
updateEvent(
id: ID!
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
)
A mutation is basically just a function call, and it's best to think of it in those terms. If you wrote a function to create an Event, you likely wouldn't provide it an event and expect it to return an event, you'd provide the information necessary to create an Event.

If you want to add a whole object to the mutation you have to define a graphql element of the type input. Here is a link to a small cheatsheet.
In your case it could look like this:
type Location {
city: String
country: String
zip: String
}
type Place {
id: String
name: String
location: Location
}
type Event {
id: String
name: String
description: String
place: Place
}
input LocationInput {
city: String
country: String
zip: String
}
input PlaceInput {
id: ID!
name: String!
location: LocationInput!
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String, place: PlaceInput!): Event
}
schema {
query: Query
mutation: Mutation
}

Related

GraphQl One-Direction Relationship

I'd like my user to have a list of workouts (they can be active, completed, todo). Do I have to make a link from Workout to user in order to make this work? Is there no other way? Having a link back seems weird because I'd have a loop user>workout>user>workout...
I'm not sure if this is supposed to be done in graphql, I'm new to graphql. I currently have the following schema:
type User {
email: String #unique
fullName: String
birthDate: Date
weight: Int
height: Int
country: String
trainingFrequency: Int
trainingGoal: String
isOnboarded: Boolean
workouts: [Workout]
}
type Workout {
name: String!
state: State!
}
enum State {
ACTIVE
TODO
COMPLETED
}
type Query {
allUsers: [User!]!
findUserByEmail(email: String!): User
}
With Fauna, a two way relationship is always created for you when you use the #relation directive. In a one-to-many relationship a reference will be stored in all of the many-side Documents (Workout type in your case). Traversing the graph from the one-side (User type) to the many-side is made possible with an Index that is automatically generated for you.
Yes, you can query (nearly) infinitely cyclically, but in practice, there is no benefit to it.
Make sure you include the #relation directive.
type User {
email: String #unique
fullName: String
birthDate: Date
weight: Int
height: Int
country: String
trainingFrequency: Int
trainingGoal: String
isOnboarded: Boolean
workouts: [Workout] #relation
}
type Workout {
name: String!
state: State!
owner: User! #relation
}

GraphQL query with multiple nested resolvers and mapping fields to arguments

From GraphQL Client's perspective, how do I perform a query with multiple nested resolvers where the fields from the parent are passed as arguments to the child resolver?
Here is a minimal example:
GraphQL Schema:
type Author {
id: ID!
name: String!
}
type Book {
id: ID!
title: String!
releaseDate: String!
}
type Query {
// Returns a list of Authors ordered by name, 'first' indicates how many entries to return
getAllAuthors(first: Int!): [Author]!
// Returns a list of Books ordered by releaseDate, 'first' indicates how many entries to return
getBooksByAuthorId(first: Int! authorId: ID!): [Book]!
}
Is it possible to write a query to get all authors and their last released book? Something around the lines:
query GetAuthorsWithLastBook($first: Int!) {
getAllAuthors(first: $first) {
authorId: id
name
lastBook: getBooksByAuthor(1, authorId) {
title
}
}
}
In the example above, I attempted to alias getAllAuthors.id as authorId and pass the alias down as argument to getBooksByAuthor(...) but that didn't work.
The key aspect of the problem is that I don't know the authorIds beforehand. I could fetch the authors first and build a query to fetch their last book but that will result in multiple queries and that is something I would like to avoid.
Update
A Java Kickstarter example is available here: https://www.graphql-java-kickstart.com/tools/schema-definition/
yes, on the graphql definition, you need to add lastBook in the Author
type Author {
id: ID!
name: String!
lastBook: [Book]
}
Next up u need to write the resolver for the lastBook
const resolvers = {
Query: {
Author {
lastBook: (parent, args) {
const userId = parent.id;
return getBooksByAuthor(userId, 1);
},
}
}
};

How to combine multiple field in GraphQL Object

I am trying my first Graphql Schema design. Is it possible that single field inside object type refer to a complex object?
enum KeyPrefix {
WS
WK
SI
ENT
}
input generalKey {
keyPrefix:KeyPrefix!
key:Int!
}
type Item
{
pk: generalKey!
data: String!
name: String!
}
It gives me error as below.
The type of Item.pk must be Output Type but got: generalKey!
input is a keyword reserved for describing input to a GraphQL query. Your schema should look like this:
enum KeyPrefix {
(your values)
}
type PrimaryKey {
prefix: KeyPrefix!
key: Int!
}
type Item {
pk: PrimaryKey!
data: String!
name: String!
}
When you define a Query in your GraphQL schema, you will want to use an input, like so:
input PrimaryKeyInput {
prefix: KeyPrefix!
key: Int!
}
type Query {
getItemByPrimaryKey(input: PrimaryKeyInput!): Item
}
This will allow a client to ask for an Item using the same fields you have on PrimaryKey.

Get list of types from operation variables

I have an operation and I want to get a list containing its variables and types.
Eg. given the operation
query getResource($id: ID!, $title: String) {
resource(id: $id, title: $title) {
id
title
content
}
}
I want to get something like this
[{type: "ID", required: true, name: "id"}, {type: "String", required: false, name: "title"}]
Is this easily attainable?
Edit: I guess what I want to do is to go from a VariableDefinitionNode to an object containing all the information I want, such as type, defaultValue, required, name.
You should be able to implement this if you change your schema (assuming you can generate this data on the server into this shape)
type Query {
resource ($id: ID!, $title: String): [Variable]
}
type Variable {
type: String!
required: Boolean!
name: String!
...
}
This is a very generalised syntax however, returning an array of values like that. If the structure of your resources is known, you would be better off defining that and removing the name parameter as it becomes redundant. (I'm thinking about lookups inside your client-side application)
type Query {
resource ($id: ID!, $title: String): Resource
}
type Resource {
id: Variable!
title: Variable
content: Variable
}
type Variable {
type: String!
required: Boolean!
...
}

GraphQL no object type?

I've read everything, understood no solution and concrete explanation (even here: Apollo / GraphQl - Type must be Input type)
I want to create an object System that contains Suns. So I do:
type System {
_id: ID
name: String! #unique
diameter: Int
suns: [Sun]
}
type Sun {
_id: ID
name: String
diameter: Int
}
type Mutation {
createSystem(name: String!, diameter: Int, suns: [Sun]): System!
}
And I write in playground:
mutation {
createSystem(name:"new system", suns: [{ name: "John" }]) {
name
suns
}
}
But I got a terminal error: "Error: The type of Mutation.createSystem(suns:) must be Input Type but got: [Sun]."
I understand that Sun isn't received as an object. How to declare it as an object?
Thank you very much for your answers
The GraphQL spec. does not allow using type (i.e. output type) as the input argument.It only allows the input arguments to be enum , Scalar and Input . That means you have to create a SunInput
input SunInput {
_id: ID
name: String
diameter: Int
}
You need to make a custom "Type" for sun with its own resolver.
suns: { type: new GraphQLList(SunType) } // just an example
mutation {
createSystem(name:"new system", suns-names: "John") {
name
}
}
It will have resolver that writes a new system to the database called new system that adds a sun of "SunType" to the a database collection with the name of "Sun" for example.

Resources