DEV Community

Kehinde Adeleke
Kehinde Adeleke

Posted on

General thoughts: Apollo's GraphQL's Course

This is a public journal that would include stuff I think are necessary for me to remember.

Previously I have been tweeting about it, but then Twitter is a private owned platform and anything could happen to my account.

Now onto the good stuff:

  1. On the Client side, the useQuery hook returns an object with 3 useful properties. They are destructured. The first, loading, indicates whether the query has completed and results have been returned. The second, error is an object that contains any errors that the operation has thrown. The third, data contains the results of the query after it has completed.

To set variables in our Query, we declare them in the second parameter of the useQuery hook, inside the options object.

Code Example using React JS:

import { useQuery, gql } from '@apollo/client'

export const GET_TRACK = gql`
  query getTrack($trackId: ID!) {
    track(id: $trackId) {
      id
      title
      author {
        id
        name
        photo
      }
      thumbnail
      length
      modulesCount
      numberOfViews
      modules {
        id
        title
        length
      }
      description
    }
  }
`

const Track = (props) => {
   const {loading, error, data} = useQuery(GET_TRACK,{
       variables: {prop.id}
    })

return(
    //render UI
  )
}


Enter fullscreen mode Exit fullscreen mode
  1. I had to create a schema for the module type for a track, get the data from the dataSource and structure the resolver to return the required field.

I'd say the hardest part was the resolver but with some thinking, I was able to figure it out.

What I learned is that: Your resolver function should follow your schema defined. Not just willy nilly stuff.

First the schema:

  type Module {
    id: ID!
    "the title of the particular module"
    title: String!
    length: Int
    "the url of the particular module"
    videoUrl: String!
    "the content of the particular module"
    content: String!
  }
Enter fullscreen mode Exit fullscreen mode

and then defining the Query that would return the required module when supplied with an argument

type Query {
    "Query to get tracks array for the homepage grid"
    tracksForHome: [Track!]!
    "a track id would return a details about a specific track"
    track(id: ID!): Track
    "a module would return details about a specific module"
    module(id: ID!): Module!
  }
Enter fullscreen mode Exit fullscreen mode

the dataSource for the current application is a RESTAPI so I had to use a method to get the module from that.

I did that by writing:


getModule(moduleId) {
    return this.get(`module/${moduleId}`)
  }

Enter fullscreen mode Exit fullscreen mode

and finally the resolver. This part got me so confused because of

  1. the parent parameter
  2. the args parameter

I still haven't gotten the grasp of the arguments resolver functions need so it took me a bit to think through my Schema and how the Query is going to be structured.

After that, I solved it and wrote the following resolver function

 module: (_, { id }, { dataSources }) => {
      return dataSources.trackAPI.getModule(id)
    }

//nested in a query parent
Enter fullscreen mode Exit fullscreen mode

I just read about Mutations and how they can serve as entry points into our GraphQL Api especially if we need to write data to our server.

A fill in the gap exercise on the Apollo website resulted in this helpful paragraph.

"Queries and Mutations are both types of GraphQL operations. Queries are read operations that always retrieve data. Mutations are write operations that always modify data. Similar to Query fields, fields of the Mutation type are also entry points into a GraphQL API."

Mutations can be easy or complex depending on the return value required and the fields being modified.

For example, a simple mutation can just have one return value. Like this:


type Mutation{
   addSpaceCat(name: String!): SpaceCat
}

Enter fullscreen mode Exit fullscreen mode

This is because once we call the addSpaceCat mutation, we want the client to get the updated value of the SpaceCat we just mutated.

Sometimes, though, one mutation should return multiple values.

type Mutation{
   assignMission(missionId: ID!, spaceCatId: ID!): ???
}
//what should we return
Enter fullscreen mode Exit fullscreen mode

and so because we need to return both, we need to create a new type. This type would include

  1. code: an Int that refers to the status of the response similar to an HTTP status code.
  2. success: a Boolean flag that indicates whether all the updates the mutation was responsible for succedded.
  3. message: a String to display information about the result of the mutation on the client side. This is useful for us if the mutation is only, well partially successful and a generic error message won't tell the whole story.

with that, we then create a new type for our mutation respose.

type AssignMissionResponse {
   code: Int!
   Success: Boolean!
   message: String!
   spacecat: Spacecat
   mission: Mission
}
Enter fullscreen mode Exit fullscreen mode

Regarding GraphQL Mutation:

We use hooks to send requests to our GraphQL API from a React client. To send a mutation, we use the useMutation hook. This returns an array where the first element is the mutate function used to trigger the mutation. The second element is an object with more information about the mutation, such as loading, error and data. This hook takes in a GraphQL operation as the first parameter. It also takes in an options object as the second parameter, where properties like variables are set.

Discussion (0)