No Idea How to Get Started With GraphQL? Make a Wrapper of a REST API

Lauren Pitruzzello
The Startup
Published in
13 min readSep 16, 2020
Image from https://me.me/i/tell-me-what-you-want-what-you-really-really-want-b11fd39ee8964aafb4644c50169913ae

Circa 2012 Facebook needed a data-fetching API that would keep up with developing needs of its mobile applications. This problem birthed GraphQL, a powerful query language and runtime environment for application data requests.

But GraphQL’s meteoric rise in popularity — evidenced by now numerous libraries and tutorials supporting its implementation — has led to a sea of content that can be hard to parse, especially if you’re a new developer who just started programming RESTful APIs. If you happen to be primarily interested in client side querying, you will quickly notice there are very few open GraphQL APIs available for practice. And, as its name implies, GraphQL structures data into a graph and it is not always easy to conceptualize a graph structure when you’re a new developer.

So how do you get started?

GraphQL commonly is implemented as a layer on top of existing APIs, be they REST endpoints, relational or non-relational databases, or even a combination of all three. While this can be a complex task for apps in production and at scale, it’s pretty simple to build a GraphQL layer on top of one API endpoint, creating a wrapper of sorts. This will allow you to use third-party data to learn how to build a simple GraphQL server and query GraphQL as a client, without needing to find an open source GraphQL API.

So let’s make a GraphQL weather app! We’ve got a lot to cover so feel free to come back to this table of contents if you need to navigate around.

Contents

Quick Pre-work

Getting Started — Back-end

  1. OpenWeather API
  2. Defining Our Schema
  3. Rest Data Source
  4. Resolvers
  5. Constructing the Server
  6. Launching the Server

Apollo Playground

Getting Started — Front-end

  1. Apollo Client
  2. Client Query
  3. useQuery Hook
  4. Zip Code Input Form
  5. Display Weather Data
  6. Final Touches

Conclusion

Quick Pre-work

We’re going to be starting with Node.js and Apollo Server for our application. Apollo is one of the most prominent platforms for GraphQL in Node.js and the Apollo Client library supports client-side querying in Typescript or Javascript.

Apollo is easy to use, wildly developer friendly and offers a lot of tools to get started quickly. It does though abstract away a bit of what GraphQL is on its own, so I’d suggest first doing this quick tutorial to see what a very simple “hello world” schema, resolver and query would look like in GraphQL.

Getting started — Back-end

First we need to make a new directory for this application and create a package.json. To get started, in your terminal run:

~ mkdir my-weather-app~ cd my-weather-app~ npm init --y

Add dependencies apollo-server and apollo-datasource-rest via your package manager of choice.

~ npm i --save apollo-server apollo-datasource-rest

Create a subfolder called server, change directory into this new server folder and add the files index.js, resolvers.js, schema.js and datasource.js.

We’re going to be using OpenWeather for our API. You can get an API key with their free tier for the purposes of this exercise.

Make sure you protect your API key if you plan to push to Github! You can add a file called secrets.js to your application, add the below to secrets.js and then include it in a .gitignore file at the root of your project directory. Add your node_modules to the .gitignore as well.

OpenWeatherAPI

We’re going to be using the OpenWeatherAPI to query by zip code. First, let’s see what information we get from an API call — feel free to use Postman for this get request, or a simple curl request will also work! Make sure you replace the below “insert api key” with your api key, and take a minute to look at data from different zip codes. In your terminal run:

~ curl http://api.openweathermap.org/data/2.5/weather\?zip\=10003,us\&units\=imperial\&appid\=YOUR_API_KEY | json_pp

You should see a response like the below.

~ curl http://api.openweathermap.org/data/2.5/weather\?zip\=10003,us\&units\=imperial\&appid\=YOUR_API_KEY | json_pp
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 455 100 455 0 0 3273 0 --:--:-- --:--:-- --:--:-- 3273
{
"coord" : {
"lat" : 40.73,
"lon" : -73.99
},
"main" : {
"humidity" : 83,
"temp" : 72.66,
"feels_like" : 75.67,
"temp_max" : 73.99,
"temp_min" : 72,
"pressure" : 1023
},
"clouds" : {
"all" : 1
},
"base" : "stations",
"sys" : {
"sunrise" : 1599561024,
"country" : "US",
"sunset" : 1599607013,
"type" : 1,
"id" : 4610
},
"wind" : {
"deg" : 120,
"speed" : 5.82
},
"dt" : 1599616264,
"visibility" : 10000,
"weather" : [
{
"id" : 800,
"icon" : "01n",
"description" : "clear sky",
"main" : "Clear"
}
],
"timezone" : -14400,
"cod" : 200,
"name" : "New York",
"id" : 0
}

A few things you might have observed:

  1. There are some duplicate terms that can be confusing (ie. “main” is both a key in the outermost data and a key within the “weather” object, and both contain data for the current weather for this zip code.
  2. Times are in epoch time, not usually the most user friendly.
  3. The data is quite nested, and it would probably be easier to parse the weather data if it was grouped together differently.

With GraphQL, we can map this data to a schema that makes sense for our needs and also convert the data to formats that make sense for our users before data is sent to the client.

Defining our schema

Let’s first make the schema for the Apollo Server. We’ll need to import the gql template literal tag provided by Apollo to construct the schema. This tag helps the program identify the schema as a GraphQL string instead of an ordinary string.

Schemas are written with types. The primary purpose of your GraphQL schema is to identify the roots for your data querying and then, if needed, create types for that root and any branches from it.

In our case, our one and only root will be the call for “weather”. It will require a zip code and return a type “Weather” that we will create next. Here is what the code will look like with just that query.

FYI — GraphQL uses # for comments, like #queries in the above.

You will need to note the type for each property of the schema and each parameter for the query. The “!” at the end of a type signifies that that this field is required, i.e. like our require zip code string in the above. These types might be standard data types — strings, floats, ints — or can be a reference to other schema types, such as the Weather type we will now create.

We have a lot of freedom with how we want to structure and name our data in our schema even though we are using a third party API. We don’t have to use the API’s inherent structure or names. We can pile all of the data we want into one type, or split it if that seems like a better structure for our data.

An example schema for the weather API is below, but feel free to move fields if you’d like to structure it differently. I chose to have a main “Weather” type with location and sunrise / sunset data and a separate “Current Weather” type for the present weather in a city.

REST Data Source

Next we’ll head to our datasource.js file. The Apollo platform has a REST data source class that allows you to easily fetch data from a REST endpoint for your GraphQL server. We’re going to create our own WeatherAPI class that extends the REST data source class.

The constructor has a property for the base URL of the REST API you will use. We will only be defining one API request, but this is helpful when using one class for multiple requests to a REST data source.

Below our constructor we will write an async function to get our data. The get request method comes from the REST data source class. The data from the async call will then be passed to the weather reducer we will create next.

Our reducer maps the data to the schema we created in our schema.js file and returns it.

Some things to note about the above reducer:

  1. We have a field in our reducer for each field in our schema — this is important.
  2. The reducer follows the shape of our schema — the main “weather” data is the root and it has a key for “current weather” that is an object with additional data fields.
  3. We can modify names of the data returned from our API. This allows us to follow the paradigms of GraphQL — constructing a data graph based on the shape of data we want and what we feel will be easiest for our client — instead of being stuck with the data formatting we get directly from the API.
  4. We can change the way data is displayed — for example, we can take the epoch time for sunrise and sunset and convert them to time strings.
  5. We don’t need to use every piece of data from the third-party API — we can be selective about what is needed for our app.

Resolvers

Finally, our resolvers. These help GraphQL know what to do based on the type of request we are making, or the root of our operation. For our weather app we will only be writing a query, but GraphQL also supports mutations (modifying the data in a GraphQL API) and subscriptions (only needed if your client needs realtime updates from your server as data changes).

We’re going to export our query resolver with the callback for the “weather” query. The callback has four possible arguments:

  1. Parent. This is only needed if your query requires the value of the resolver’s parent. This is not needed in our case so we use an _ to omit it.
  2. Arguments. We want to get weather for a specific zip code, and the async function we wrote in datasource.js requires a zip parameter, so we will need a zip argument.
  3. Context. This is where you pass anything the query might need in order to execute, in our case this is going to be the datasource we made in datasource.js and the query will be “resolved” using the WeatherAPI class and async function we made. Learn more about context on Apollo’s site.
  4. Info. This is where you would pass information about the operation’s execution state, but this is not a parameter we need for our resolver.

Our resolvers.js file should look like this. The datasources.weatherAPI call will come from the main server file that we will make next, and .getWeather is the async method we made in our datasource.js file.

If we were making multiple queries, our “Query” field would have multiple keys for different functions to call for different roots.

Constructing the Server

When working with Apollo, your server will be a new instance of the Apollo Server class. This class requires several parameters:

  1. Your schema (the typedefs we exported from schema.js)
  2. Your resolvers (exported from resolvers.js)
  3. Your datasources (exported from datasource.js). Note this field needs to be a callback and can have multiple datasources when called. We will only be calling a new instance of the WeatherAPI we made in our datasource file for the query “weather”, but if we were using multiple datasources we could list them all in this callback.
When our resolver looks for dataSources.weatherAPI it will create a new WeatherAPI instance.

The engine field above is for tracking your schema with Apollo Studio. This is a very helpful tool, but not required for this exercise and beyond its scope so feel free to omit. If you want to use Apollo Studio for practice, you can learn more here.

Launching the server

We are ready to launch our server! We’ll have our server listen and then log the server url, which will come from the instance of the Apollo Server.

Apollo Playground

One of the best tools from Apollo is the playground developers can use to see their schema and query their data.

Add the below to “scripts” in your package.json.

"start-server": "node server"

You can then run the below command from your root directory (your my-weather-app folder).

~ npm run start-server

You can now head to http://localhost:4000/ and should see the below interface:

On the left hand side you can run a query for the “weather” root we made. You will need to pass in a zip code since we required it in our schema.

query weather {
weather (zip:"06111") {
cityName
sunrise
currentWeather {
temp
tempLow
}
}
}

Hit the play button and you will see the result of your query on the right! You can also open the schema and docs tabs on the far right if you want to see the schema we made in our schema.js file. Practice adding and removing fields from the above query and changing the zip code to see different data.

Getting Started — Front-end

Now that we have our server working we can build a client application to query it and display our weather data.

I used create-react-app to make the initial set up a bit faster. You can run the below command in the root directory of this app (in your my-weather-app folder) to create a new client folder. Omit the typescript tag at the end if you prefer to use Javascript.

~ npx create-react-app client --typescript

The new react client app has its own package.json file. You will want to install the Apollo Client library while in the client directory so it is added to the dependency list for this package.json.

Your current file structure should look like the below.

my-weather-app/
client/
src/
App.tsx
index.tsx
(...more files from create-react-app)
package.json
server/
datasource.js
index.js
resolvers.js
schema.js
package.json
secrets.js
.gitignore

To install the Apollo Client library, run the following in your terminal.

~ cd client
~ npm i --save @apollo/client

Apollo Client

First we need to create an instance of the Apollo Client. Create a file client.tsx (or client.js) in your new client folder and paste the below (remove the ApolloClient<NormalizedCacheObject> type if using Javascript).

The above code creates a new cache for our client queries, meaning subsequent requests for the same data will be much faster. The client instance also instructs our new client application to query http://localhost:4000/, the url for our server data, when making requests.

We then we need to pass that client to Apollo’s provider to ensure our entire app can access it. Modify the index.tsx / index.js file that came with create-react-app to the below.

Client Query

We’re now going to make the frontend of our weather app. Open the App.tsx file and paste in the below to start (this will look the same for Typescript and Javascript).

A little about the above code:

  1. This probably looks familiar — it’s pretty close to the same query format we used in the Apollo Playground. Feel free to go back and use the playground and make sure you are getting the data you want for your client application. Modify the query above so you can display what you’d like to see in your app.
  2. We again are using the gql template literal tag from Apollo for our query (though now imported from ‘@apollo/client’).
  3. We’re calling this query “GET_WEATHER,” but it could be called whatever makes sense for you.

useQuery Hook

We’re going to now use that string in our main app file and the useQuery hook that Apollo Client provides to call it. We will have two variables on state — zip and nextZip — so that we can create a simple form to change zip codes and see the weather for different cities.

The useQuery hook will first take in our GET_WEATHER query and then an object with the variables we will be passing to it, in this case a default zip code.

The useQuery hook returns either a loading state, an error or the data for our query. Having access to all three is helpful when later creating a loading and error message in our app.

The frame of your App.js / App.tsx file should look like this.

Zip Code Input Form

We can first make a small form with an input field for a user to change the zip code. We’ll also want to make two event handlers that will reset nextZip when the input field is changed and reset the zip variable when the form is submitted. That form will look something like the below.

Display Weather Data

Now we will add in our data! You can definitely choose different fields than I did below and order it differently. Each of our fields will be preceded by “data.weather.” I do recommend making the rendering for your data conditional on data and adding in loading and error messages based on what is returned from the useQuery hook.

We’re now ready to look at our app! You will need to have two terminal windows open, one for the server from the root directory and one from our client folder. In the root directory, you should run the start-server command we wrote above. In the client folder, the “start” command will run the react-scripts start command and launch our client app at http://localhost:3000/.

So from your my-weather-app folder:

~ npm run start-server

And from your client folder within my-weather-app:

~ npm run start

You should see something like the below in your browser at http://localhost:3000/. You should be able to input different zip codes and see data for your hometown, current city, Beverley Hills 90210, and everywhere in between! You also can try putting in a random number to see your error message pop up.

Our new GraphQL weather app!

Final Touches

Now we just need to give it a facelift! I added Material-UI components and JSS, but feel free to style to your heart’s content. Here’s my final App.tsx.

And the final view!

Conclusion

GraphQL is an amazing query language and runtime and the Apollo platform makes developing with GraphQL a relatively seamless experience. There is a bit of a learning curve to pick up syntax and organization, but building a wrapper around a third party API can be a fun way to practice using GraphQL for your server and client applications.

You can find the full repo for this app here. Happy coding!

--

--

Lauren Pitruzzello
The Startup

Fullstack software engineer, amateur photographer and nachos connoisseur. laurenpitruzzello.com