GraphQL - Schema and Resolvers
I have been working on an assignment for which I needed to implement GraphQL using Apollo and Express on the backend.
I’m writing this post to clarify and summarize my understanding of how the different components of GraphQL - Schema, Query, Mutations, Resolvers connect to each other.
Index
What is GraphQL?
GraphQL is a query language that allows to fetch data from the backend. It has certain advantages over RESTful APIs by making querying for deeply nested data easier as well as avoiding overfetching of data.
A GraphQL query allows the client to specify exactly the data they want and receive no more from the backend.
Components of a GraphQL setup on backend
Schema
The schema is defined as a string using the GraphQL Schema Definition Language.
The schema is used to define
-
What kind of queries can be made from the frontend to fetch data? (A query is the equivalent of a GET request and is used to fetch data from the backend)
{ type todo(id: String): ToDo }
This defines a query type
todo
which will take anid
of typeString
and return data of typeToDo
. The data typeToDo
is defined later in the article. This is like a function signature of the query that can be made from the frontend to fetch data. -
What kind of mutations can be made from the frontend to manipulate data? (A mutation is the equivalent of a POST, PUT or DELETE request and is used to modify data on the backend)
{ type add_todo(id: String, title: String, completed: Boolean): ToDo }
The above defines a mutation type
add_todo
which will takeid
,title
andcompleted
variables and return data of typeToDo
. This is like a function signature of a mutation that can be made from the frontend to modify data -
What are the different data types that will be returned from the backend in response to the queries and the mutations?
{ type ToDo { id: String, title: String, completed: Boolean } }
The above defines a custom data type
ToDo
which is referred in the query and mutation types as the return data type
Resolvers
While the Schema was used to define the function signatures for the queries and the mutations allowed from the frontend, the resolvers are used to define the functions themselves to query data from the source database.
Resolvers are used to define
-
Functions to execute the queries and mutations defined in the schema. The interaction with the database or the appropriate data store is defined in the resolvers
-
Functions to resolve fields within data types. For instance if a
ToDo
type refers to aUser
type{ type ToDo { id: String, title: String, completed: Boolean, user: String } type User { id: String, name: String } }
Assuming that the
user
field inToDo
type stores anid
for the User like a foreign key, when we query forToDo
we can also fetch the corresponding user. This is achieved by defining a resolver function foruser
field inside theToDo
data type.