How To Connect To A REST Service

How to use a REST API as a data source for your GraphQL API

You can use StepZen to easily connect to a REST service. Follow along to see how to do it.

Get Set Up

First of all, you'll need a StepZen account.

You'll also need the StepZen CLI to upload, deploy and test your schema on StepZen. You can install the StepZen CLI via npm.

npm install stepzen -g

Create a project folder to work in and change directory to that folder within your command line/console.

Plan and Create the GraphQL Type

We'll be using the Cat API to get a breed by ID.

Taking a look at the JSON with the Cat API returns, we can see that we'll need our breed.graphql file to have a type like:

type Breed {
  id: String!
  name: String!
  temperament: String!
  life_span: String!
}

There's a lot more information on the Cat API, like natural, rare, rex, origin, etc. and etc. But since id, name, temperament, and lifespan is the only information we want to come back from the API right now, this is all we need to define on our type. That's the beauty of GraphQL!

Ok, so that's our type, but what will our query look like?

Let's start with:

type Query {
  breedById(id: String!) : Breed
}

This defines the query that will return the information id, name, temperament, and lifespan for the id included in the query through the Breed type.

But how will StepZen know which REST API we're using and what keys to use?

Enter the @rest directive!

type Query {
  breedById(id: String!): Breed
    @rest(
    endpoint: "https://api.thecatapi.com/v1/breeds/search?q=$id"
    configuration: "cat_config"
    resultroot: "[]"
    )
}

The @rest connector is a custom StepZen directive that connects you to an api.

endpoint - this tells StepZen what endpoint to call. You can see the variable $id is set with a dollar sign in the url. configuration - this refers to your configuation setting in config.yaml. More on that below. resultroot - helps 'peel the top layer' off the JSON. Since the Cat API returns the data object directly inside an array, we say that the resultroot is "[]".

Add Your index.graphql

You'll also need to make sure you've got a file named index.graphql in your working directory. It tells StepZen what files hold your types and queries:

schema @sdl(files: ["breed.graphql"]) {
	query: Query
}

Add Your config.yaml

Since we are using the Cat API, we will need to make sure to send your Authorization via the config.yaml (make sure not to commit your config.yaml, put it in .gitignore).

configurationset:
  - configuration:
      name: cat_config
      Authorization: Apikey MY_PERSONAL_ACCESS_TOKEN

Query the API

Now you're ready to run stepzen start. You should see your query editor pop up in the browser!

Now, run this query by copying/pasting and hitting the play button:

query MyQuery {
  breedById(id: "abys") {
    id
    life_span
    name
    temperament
  }
}

Your result will look like:

REST

Work With Nested JSON

Let's take a closer look at the first result from the JSON formatted data returned by the Cat API:

[
  {
    "adaptability": 5,
    "affection_level": 5,
    "alt_names": "",
    "cfa_url": "http://cfa.org/Breeds/BreedsAB/Abyssinian.aspx",
    "child_friendly": 3,
    "country_code": "EG",
    "country_codes": "EG",
    "description": "The Abyssinian is easy to care for, and a joy to have in your home. They’re affectionate cats and love both people and other animals.",
    "dog_friendly": 4,
    "energy_level": 5,
    "experimental": 0,
    "grooming": 1,
    "hairless": 0,
    "health_issues": 2,
    "hypoallergenic": 0,
    "id": "abys",
    "image": {
      "height": 1445,
      "id": "0XYvRd7oD",
      "url": "https://cdn2.thecatapi.com/images/0XYvRd7oD.jpg",
      "width": 1204
    },
    "indoor": 0,
    "intelligence": 5,
    "lap": 1,
    "life_span": "14 - 15",
    "name": "Abyssinian",
    "natural": 1,
    "origin": "Egypt",
    "rare": 0,
    "reference_image_id": "0XYvRd7oD",
    "rex": 0,
    "shedding_level": 2,
    "short_legs": 0,
    "social_needs": 5,
    "stranger_friendly": 5,
    "suppressed_tail": 0,
    "temperament": "Active, Energetic, Independent, Intelligent, Gentle",
    "vcahospitals_url": "https://vcahospitals.com/know-your-pet/cat-breeds/abyssinian",
    "vetstreet_url": "http://www.vetstreet.com/cats/abyssinian",
    "vocalisation": 1,
    "weight": {
      "imperial": "7  -  10",
      "metric": "3 - 5"
    },
    "wikipedia_url": "https://en.wikipedia.org/wiki/Abyssinian_(cat)"
  }
]

As you can see, there are cases where the data is nested inside objects, like the image url:

"weight": {
  "imperial": "7  -  10",
  "metric": "3 - 5"
},

How would we get that data? We'll use a parameter on the @rest directive setters:

type Breed {
    id: String!
    name: String!
    temperament: String!
    life_span: String!
    origin: String!
    imperial_weight: String
}

type Query {
    breedById(id: String!): Breed
        @rest(
        endpoint: "https://api.thecatapi.com/v1/breeds/search?q=$id"
        configuration: "cat_config"
        setters: [
				{ field: "id", path: "id" }
				{ field: "name", path: "name" }
        { field: "origin", path: "origin" }
				{ field: "temperament", path: "temperament" }
				{ field: "life_span", path: "life_span" }
        { field: "imperial_weight", path: "weight.imperial" }
			]
        )
}

Note that StepZen performs a 'smart' inspection on these setters so that we don't need the resultroot anymore.

Connecting Two REST APIs

We can see the country the cat belongs to under 'origin'. What if we wanted details on that country, coming from another API? Let's use the Rest Countries API to set up a query first.

Here's our country.graphql:

type Country {
    name: String!
    region: String!
    subregion: String! 
    population: Int!
}

type Query {
  countryByName(country: String!): Country
    @rest(
    endpoint: "https://restcountries.eu/rest/v2/name/$country?fullText=true"
    configuration: "countries_config"
    resultroot: "[]"
    )
}

And we'll need to add it to our index.graphql:

schema @sdl(files: ["breed.graphql", "country.graphql"]) {
	query: Query
}

Now a query like this:

query MyQuery {
  countryByName(country: "aruba") {
    name
    population
    region
    subregion
  }
}

Returns this:

{
  "data": {
    "countryByName": {
      "name": "Aruba",
      "population": 107394,
      "region": "Americas",
      "subregion": "Caribbean"
    }
  }
}

Now, how do we get that information to come back from our breedById query? Enter another custom StepZen directive, @materializer!

We add this line:

@materializer (query: "countryByName", arguments: [{name: "country", field: "origin"}])

query tells StepZen what query to use, and the arguments fed in are name, or what parameter countryByName will use, and field, or what the value of that parameter will be as supplied by the breedById query.

You can put it right under the closing bracket:

type Breed {
  id: String!
  name: String!
  temperament: String!
  life_span: String!
  origin: String!
  imperial_weight: String
  catCountry: Country
    @materializer (query: "countryByName", arguments: [{name: "country", field: "origin"}])
}

type Query {
  breedById(id: String!): Breed
    @rest(
      endpoint: "https://api.thecatapi.com/v1/breeds/search?q=$id"
      configuration: "cat_config"
      setters: [
        { field: "id", path: "id" }
        { field: "name", path: "name" }
        { field: "origin", path: "origin" }
        { field: "temperament", path: "temperament" }
        { field: "life_span", path: "life_span" }
        { field: "imperial_weight", path: "weight.imperial" }
      ]
    )
}

And now this query:

query MyQuery {
  breedById(id: "abys") {
    catCountry {
      name
      population
      region
      subregion
    }
    id
    imperial_weight
    life_span
  }
}

Returns:

{
  "data": {
    "breedById": {
      "catCountry": {
        "name": "Egypt",
        "population": 91290000,
        "region": "Africa",
        "subregion": "Northern Africa"
      },
      "id": "abys",
      "imperial_weight": "7  -  10",
      "life_span": "14 - 15"
    }
  }
}

For more on @materializer, see ourdocs on linking types.

Handling JSON with Special Values

JSON supports field names with special characters like #.

last.fm is an API that uses JSON names like #text.

To ensure that StepZen can parse the data, make sure to put backticks around the field in your setters path, as below:

type lastFMTracks {
  Name: String
  Artist: String
  Album: String
  Image: String
  NowPlaying: Boolean
  URL: String
}
​
type Query {
  lastFMTracksByUserId(id:ID!): [LastFmTracks]
    @supplies(query:"tracksByUserId")
    @rest(
      setters: [
        {field: "Artist", path: "recenttracks.track[].artist.`#text`"}
        {field: "Album", path: "recenttracks.track[].album.`#text`"}
        {field: "Name", path: "recenttracks.track[].name"}
        {field: "Image", path: "recenttracks.track[].image[2].`#text`"}
        {field: "NowPlaying", path: "recenttracks.track[].`@attr`.nowplaying"}
        {field: "URL", path: "recenttracks.track[].url"}
      ]
      endpoint: "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=$id;&api_key=$apikey;&format=json"
      configuration: "LastFm" 
  )
​
}

That way, you can retrieve JSON with symbols.

This site uses cookies: By using this website, you consent to our use of cookies in accordance with our Website Terms of Use and Cookie Policy.