Connect a REST Service

Connect any REST API as data source for a GraphQL schema

The @rest directive is a custom StepZen directive for connecting to REST APIs. It can be applied to a GraphQL query so that the result of the query is populated with data coming from a REST API that returns a JSON result.

Configuration Properties

The available configuration properties are:

endpoint

This value is required. It determines the REST endpoint that will be used to populate the query result. Endpoint is a string value that can contain variables preceded by a $ which will be replaced by StepZen. These variables can match query arguments or configuration variables of the same name. For example, $username in the endpoint string would be replaced by the value of a username query argument.

configuration

This value is optional. It tells StepZen which configuration to use for this endpoint. StepZen configurations are stored in a config.yaml file and are given a name.

For example, a named configuration within config.yaml called github_config would be referenced by a configuration property of @rest as configuration: github_config. A configuration can contain things like API keys needed to connect to a REST API or other configuration values that may be needed to construct the endpoint URL.

resultroot

This value is optional. In cases where the data to populate the GraphQL type is not in the root object of the result from a REST API, you can utilize resultroot to specify the path StepZen should use as the root.

Let's look at an example. This is the structure of a response from the Contentful Delivery API:

{
  "fields": {
    "title": {
      "en-US": "Hello, World!"
    },
    "body": {
      "en-US": "Bacon is healthy!"
    }
  },
  "metadata": {
    ...
  },
  "sys": {
    ...
  }
}

In this example, fields contains all the data we would use to populate a type representing this content object. Therefore, we would set the resultroot to fields as shown in the example below.

contentfulPost(id: ID!): Post
    @rest(
      endpoint: "https://cdn.contentful.com/spaces/$spaceid/entries/$id"
      resultroot: "fields"
      configuration: "contentful_config"
    )

Important: The value of setters will be mapped from the resultroot. In this example, this makes the data under metadata and sys inaccessible.

In some cases, the data to populate the GraphQL type is located inside of an array of items of the result from a REST API. Therefore, you should set the resultroot inside of an array of items.

{
  "sys": { "type": "Array" },
  "skip": 0,
  "limit": 100,
  "total": 1256,
  "items": [
    {
      "fields": {
        /* list of fields for this entry */
      }
    }
  ]
}

In above example, Contentful returns all the entries and we need to set the value of the root to fields inside the array of items.

You can do this by adding an empty array notation in the resultroot as in the following example:

contentfulBlogs: [Blog]
    @rest(
      endpoint: "https://cdn.contentful.com/spaces/$spaceid/entries"
      resultroot: "items[].fields"
      configuration: "contentful_config"
    )

setters

This value is optional. Sometimes the name or structure of the content returned by a REST API doesn't exactly match the GraphQL type that the query will populate. In these cases, you can use setters to map the values returned by a REST API result to the appropriate fields within the returned GraphQL type.

setters takes an array of objects containing a path and a field. The path is the path to the value in the endpoint's JSON result. The field is the property in the GraphQL type returned by the query that the value of field should be set to.

To illustrate this concept, let's look at the following example JSON response:

{
    "id": 194541,
    "title": "The Title",
    "slug": "the-url-2kgk",
    "published_at": "2019-10-24T13:52:17Z",
    "user": {
      "name": "Brian Rinaldi",
      "username": "remotesynth",
    }
  }

If the corresponding Article GraphQL type has a field of published but not published_at, StepZen will not be able to automatically map the value returned by the REST API to the value in the GraphQL type.

To resolve this, we'd add a setter with the following values:

{ field: "published", path: "published_at" }

Setters are also useful for mapping values in nested objects, returned by a REST API.

In the example above, we cannot automatically map the value of user.name to a field in the GraphQL type. We have two options:

  • Create another type for User that has the corresponding fields and then use the resultroot property to user.
  • Flatten the values and add them to the Article type using the following setters:
{ field: "author", path: "user.name" }
{ field: "username", path: "user.username" }

Here's an example of a @rest directive in a file called article.graphql:

type Article {
  id: ID!
  title: String!
  description: String
  cover_image: String
  username: String!
  github_username: String!
}
type Query {
  myArticles(username: String!): [Article]
    @rest(
      endpoint: "https://dev.to/api/articles?username=$username"
      configuration: "dev_config"
      setters: [
        { field: "username", path: "user.username" }
        { field: "github_username", path: "user.github_username" }
      ]
    )
  }
}

Let's pull out the @rest directive to take a closer look:

@rest(
  endpoint: "https://dev.to/api/articles?username=$username"
  configuration: "dev_config"
  setters: [
    { field: "username", path: "user.username" }
    { field: "github_username", path: "user.github_username" }
  ]
)

endpoint, configuration, and setters are all parameters on the @rest directive. That is, they give StepZen the information it need to connect to the REST API.

  • endpoint sets the endpoint of the API.
  • configuration references the configuration file by its name value.
  • setters gives the names of the fields that will be used and their values from their paths.

filter

This value is optional. The filter parameter will filter results returned from the REST API based upon a condition so that the query will only returned the filtered results. It accepts any boolean operator (e.g. ==, !=, >. <, >=, <=).

For example, the following filter would return only the record for the email that matches the string:

newsletterList: [Subscriber]
@rest(
    endpoint: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    filter: "email==\"quezarapadon@quebrulacha.net\""
)

Note: Special characters, like the quotes above, need to be escaped with \. Using variables within a filter condition are not supported at this time.

headers

This value is optional. The headers parameter allows specifying headers as name and $variable where $variable is in the configuration or arguments.

@rest(
  headers: [
    { name: "Authorization" value: "Bearer $bearerToken" }
  ]

In this example, $bearerToken is a variable that can come from a configuration or a query parameter.

Optional Parameters

StepZen supports adding optional query arguments to a REST API call. StepZen will automatically append any optional query parameters that are not null, to the endpoint URL. All query arguments are auto-exploded (i.e. added to the endpoint URL) unless:

  • The argument is optional and null valued. If a value is not passed for the argument, it is not appended.
  • The argument is used in a path or header. For example, if argument id is used to construct an endpoint URL of https://dev.to/api/articles/$id.
  • The path has existing query parameters and the argument is required. In this case, if the query has a combination of required and optional arguments, only the optional arguments will be appended. For example, endpoint: "https://myEndpoint.net/endpoint?fromConfig=$configValue".

Let's look at an example. In the following query, both the username and tag parameters are optional.

articles(username: String, tag: String): [Article]
  @rest(
    endpoint: "https://dev.to/api/articles"
    configuration: "dev_config"
  )

The following cases explain how StepZen would construct the URL based on the parameters are passed:

ParametersStepZen URL Construction
username is passed but tag is nothttps://dev.to/api/articles?username=$username
tag is passed but username is nothttps://dev.to/api/articles?tag=$tag.
username and tag are passedhttps://dev.to/api/articles?username=$username&tag=$tag.

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.