It's common to hear GraphQL described as a REST killer. Here to slash your REST services, to end your endpoints. But today, let's not focus on conflict. Let's consider where GraphQL and REST share something in common: HTTP requests.
That's right. If you know how to send HTTP requests, you already have the tools necessary to build a client application that communicates with any GraphQL API. We can get data using any method that sends an HTTP request. Let's build a tiny client by using fetch
, which will work in the browser:
const query = `
query {
Lift(id: "panorama") {
name
status
}
}
`;
const url = "https://snowtooth.moonhighway.com/";
const opts = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query })
};
fetch(url, opts)
.then(res => res.json())
.then(console.log)
.catch(console.error);
We'll send the fetch
request with some options. Then we'll convert the response to JSON. When we log the results to the console, we'll see the following:
results
{
"data": {
"Lift": {
"name": "Panorama",
"status": "HOLD"
}
}
}
You could even display your results on an HTML page by replacing the console.log
with some simple DOM manipulation:
fetch(url, opts)
.then(res => res.json())
.then(
({ data }) => `
<p>
Favorite Lift: ${data.Lift.name}
Status: ${data.Lift.status}
</p>
`
)
.then(text => (document.body.innerHTML = text))
.catch(console.error);
Mutations with fetch
Whenever we want to change data, we'll use a GraphQL mutation. Our API supports a mutation called setLiftStatus
. This mutation takes in the id
of the Lift
you want to change and the new status
for that lift. The options for LiftStatus
are a GraphQL enum, a restricted list of options for a specific field. These options are OPEN
, CLOSED
, and HOLD
. Start by defining the setLiftStatus
mutation string as a variable:
const mutation = `
mutation {
setLiftStatus(id: "panorama", status: CLOSED) {
name
status
}
}
`;
Then we want to use the same GraphQL endpoint. We'll adjust the body of the request to include the mutation:
var url = "https://snowtooth.moonhighway.com/";
var opts = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: mutation })
};
Finally, we can send the request and log the results:
fetch(url, opts)
.then(res => res.json())
.then(console.log)
.catch(console.error);
And our results will reflect that the Panorama lift status has indeed been changed by the mutation:
results
{
"data": {
"Lift": {
"name": "Panorama",
"status": "CLOSED"
}
}
}
graphql-request
There are other frameworks that can be used to send GraphQL operations to an API. One of the most minimal examples of this is graphql-request
. GraphQL Request wraps fetch
requests in a promise that can be used to make requests to the GraphQL server. It also handles the details of making the request and parsing the data for you. graphql-request
is maintained by the team at Prisma.
To get started with graphql-request
, you will need to install it:
npm install graphql-request --save
From there, you'll import and use the module as request
:
import { request } from "graphql-request";
const query = `
query {
Trail(id: "grandma") {
name
status
}
}
`;
request("https://snowtooth.moonhighway.com/graphql", query)
.then(console.log)
.catch(console.error);
The request function takes in url
and query
, makes the request to the server, and returns the data in one line of code. The data returned is, as expected, a JSON response of all of the users:
{
"Trail": {
"name": "Grandma",
"status": "OPEN"
}
}
Notice that the results here are a little bit different than the fetch
request. Instead of returning the data under the data
key, the request sent with graphql-request
returns just the object. If you wanted to use this data in your app, you would access that data off of the Trail
key.
Working with GraphQL on the client doesn't require a tremendous amount of setup. Start with one of these options to start seeing the benefits of GraphQL immediately.