GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.
In this article, we’ll look at how to make basic queries to a GraphQL API.
Defining the API
We define an API by defining the types and fields for those types and provide functions for each field on each type.
For example, if we have the following type:
type Query {
person: Person
}
Then we have to create a function for the corresponding type to return the data:
function Query_person(request) {
return request.person;
}
Making Queries
Once we have a GraphQL service running, we can send GraphQL queries to validate and execute on the server.
For example, we can make a query as follows:
{
person {
firstName
}
}
Then we may get JSON like the following:
{
"person": {
"firstName": "Joe"
}
}
Queries and Mutations
Queries are for getting data from the GraphQL server and mutations are used for manipulating data stored on the server.
For example, the following is a query to get a person’s name:
{
person {
name
}
}
Then we may get the following JSON from the server:
{
"data": {
"person": {
"name": "Joe"
}
}
}
The field name
returns a String
type.
We can change the query as we wish if we want to get more data. For example, if we write the following query:
{
person {
name
friends {
name
}
}
}
Then we may get something like the following as a response:
{
"data": {
"person": {
"name": "Joe",
"friends": [
{
"name": "Jane"
},
{
"name": "John"
}
]
}
}
}
The example above has friends
being an array. They look the same from the query perspectively, but the server knows what to return based on the type specified.
Arguments
We can pass in arguments to queries and mutations. We can do a lot more with queries if we pass in arguments to it.
For example, we can pass in an argument as follows:
{
person(id: "1000") {
name
}
}
Then we get something like:
{
"data": {
"person": {
"name": "Luke"
}
}
}
from the server.
With GraphQL, we can pass in arguments to nested objects. For example, we can write:
{
person(id: "1000") {
name
height(unit: METER)
}
}
Then we may get the following response:
{
"data": {
"person": {
"name": "Luke",
"height": 1.9
}
}
}
In the example, the height
field has a unit
which is an enum type that represents a finite set of values.
unit
may either be METER or FOOT.
Photo by Giorgio Tomassetti on Unsplash
Fragments
We can define fragments to let us reuse complex queries.
For example, we can define a fragment and use it as follows:
{
leftComparison: person(episode: FOO) {
...comparisonFields
}
rightComparison: person(episode: BAR) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
In the code above, we defined the comparisonFields
fragment which has the list of fields we want to include in each query.
Then we have the leftComparison
and rightComparison
queries which include the fields of the comparisonFields
fragment by using the ...
operator.
Then we get something like:
{
"data": {
"leftComparison": {
"name": "Luke",
"appearsIn": [
"FOO",
"BAR"
],
"friends": [
{
"name": "Jane"
},
{
"name": "John"
}
]
},
"rightComparison": {
"name": "Mary",
"appearsIn": [
"FOO",
"BAR"
],
"friends": [
{
"name": "Mary"
},
{
"name": "Alex"
}
]
}
}
}
Using variables inside fragments
We can pass in variables into fragments as follows:
query PersonComparison($first: Int = 3){
leftComparison: person(episode: FOO) {
...comparisonFields
}
rightComparison: person(episode: BAR) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends(first: $first) {
name
}
}
Then we may get something like:
{
"data": {
"leftComparison": {
"name": "Luke",
"appearsIn": [
"FOO",
"BAR"
],
"friends": [
{
"name": "Jane"
},
{
"name": "John"
}
]
},
"rightComparison": {
"name": "Mary",
"appearsIn": [
"FOO",
"BAR"
],
"friends": [
{
"name": "Mary"
},
{
"name": "Alex"
}
]
}
}
}
as a response.
The operation type may either be a query, mutation, or subscription and describes what operator we’re intending to do. It’s required unless we’re using the query shorthand syntax. In that case, we can’t supply a name or variable definition for our operation.
The operation name is a meaningful and explicit name for our operation. It’s required in multi-operation documents. But its use is encouraged because it’s helpful for debugging and server-side logging.
It’s easy to identify the operation with a name.
Conclusion
GraphQL is a query language that lets us send requests to a server in a clear way. It works by sending nested objects with operation type and name along with any variables to the server.
Then the server will return us the response that we’re looking for.
Operation types include queries for getting data and mutations for making changes to data on the server.