Extend any GraphQL API with data from other APIs

Extend any GraphQL API with data from other APIs

The Grafbase Edge Gateway gives developers the power to write custom resolvers, connect APIs, add edge caching, auth, permissions, and much more.

In this guide we'll explore extending the Contentful GraphQL API with custom Edge Resolvers.

Let's imagine we use Contentful to store a list of beautiful properties we want to rent out. As a developer, we want to show the name and location of each of our properties as well as the current temperature so potential guests can feel those holiday vibes.

You'll need an account with Contentful to follow along with this guide.

Let's begin by making a new model for Property from inside the Contentful app:

Create a content model

Next, make sure to add fields for:

  • Name — Short text field type
  • Location — Location field type

Model fields

Now save the content model.

We'll next move to the Content section to add our very first Property entry.

Content tab

You'll want to give your Property a name, in this case, we'll use the name Grand Hotel Central and search for the location in Barcelona using the embedded Google Maps.

Content editor

Once you've found the location, click Publish.

Publish entry

Repeat the steps above for every location you want to store inside Contentful. You could extend the model to contain images, ratings etc.

From within your Contentful Space, go to Settings > API Keys > New API Key and give the access token a name.

API Access

Now click save and reveal the Content Delivery API access token value and save it for later along with the Space ID.

We're now ready to create a Grafbase Edge Gateway that we can run locally, and later deploy to the edge.

We'll create a new project in the directory contentful-grafbase but you can also run the command below inside of an existing project, just remove the directory name contentful-grafbase if you do.

npx grafbase init contentful-grafbase --template graphql-contentful

This will scaffold a new project that uses the GraphQL connector using the Grafbase SDK. The file grafbase/grafbase.config.ts should look something like this:

import { config, connector, graph } from '@grafbase/sdk' const g = graph.Standalone() const contentful = connector.GraphQL('Contentful', { url: g.env('CONTENTFUL_API_URL'), headers: headers => { headers.set('Authorization', `Bearer ${g.env('CONTENTFUL_API_TOKEN')}`) }, }) g.datasource(contentful) export default config({ graph: g, })

Now open the file grafbase/.env and add the following values (make sure to use the same SPACE_ID value from the previous step):

CONTENTFUL_API_URL=https://graphql.contentful.com/content/v1/spaces/SPACE_ID CONTENTFUL_API_TOKEN=

We're now ready to try fetching data from Contentful using the Grafbase Edge Gateway.

Run the following command:

npx grafbase dev

Now open http://127.0.0.1:4000 and execute a GraphQL query to fetch all properties:

{ contentful { propertyCollection { items { name location { lat lon } } } } }

You should see in the response something like:

{ "data": { "contentful": { "propertyCollection": { "items": [ { "name": "Grand Hotel Central", "location": { "lat": 41.38497, "lon": 2.177765 } } ] } } } }

Viola! We are now merging the Contentful GraphQL API with the Grafbase Edge Gateway.

We'll now create a field resolver for weather that returns the current temperature from the OpenWeather API.

Grafbase automatically prefixes the connected APIs with the namespace we set inside grafbase/grafbase.config.ts. This means that the type Property we created earlier inside Contentful will now be referenced as ContentfulProperty in the Grafbase Edge Gateway.

Inside grafbase.config.ts we can extend the Contentful type and add our new weather: Float field:

g.extend('ContentfulProperty', { weather: { returns: g.float().optional(), resolver: 'contentful/property/weather', }, })

Now create the file resolvers/contentful/property/weather.ts and add the following:

export default function Resolver() { // ... }

Before we can make a request to fetch the current weather, we need to get the location of the Property entry.

Make sure to update the resolver and name the first arg root. Then we can destructure from root the value location:

export default function Resolver(root) { const { location } = root }

It's now time to create an API key with OpenWeather so we can get the current weather data.

Get OpenWeather API key

Now add your OpenWeather API key to the file grafbase/.env:

OPENWEATHER_API_KEY=

You can now use the environment variable inside the resolver using process.env:

export default function Resolver(root) { const { location } = root const apiKey = process.env.OPENWEATHER_API_KEY }

It's now time to piece together the location.lat and location.lon values from Contentful with an API request to OpenWeather.

OpenWeather API returns the current weather in the following JSON structure:

{ "coord": { "lon": -16.5193, "lat": 9.6257 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" } ], "base": "stations", "main": { "temp": 301.42, "feels_like": 304.95, "temp_min": 301.42, "temp_max": 301.42, "pressure": 1012, "humidity": 74, "sea_level": 1012, "grnd_level": 1012 }, "visibility": 10000, "wind": { "speed": 2.97, "deg": 181, "gust": 3.58 }, "clouds": { "all": 95 }, "dt": 1685459581, "sys": { "sunrise": 1685429076, "sunset": 1685474573 }, "timezone": -3600, "id": 0, "name": "", "cod": 200 }

The response temperatures are by default Kevlin, but we can change this to Celsius by adding &units=metric to the URL.

Now let's put all of this together by adding the request to the resolver function:

export default function Resolver(root) { const { location } = root const apiKey = process.env.OPENWEATHER_API_KEY if (!location) return null return fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${location.lat}&lon=${location.lon}&units=metric&appid=${apiKey}`, ) .then(res => res.json()) .then(({ main }) => main.temp) }

For the purposes of this guide we'll only return main.temp from the resolver.

We're now ready to test everything out. Make sure you have the Grafbase development server running:

npx grafbase dev

Now go to Pathfinder and run the same GraphQL query before but this time add weather to the list of returned fields:

{ contentful { propertyCollection { items { name location { lat lon } weather } } } }

You should see a response that looks something like the below, including the current temperature:

{ "data": { "contentful": { "propertyCollection": { "items": [ { "name": "Grand Hotel Central", "weather": 30.43, "location": { "lat": 41.38497, "lon": 2.177765 } } ] } } } }

Note: You must also request for location.lat and location.lon in the query, we'll make this optional in the future.

Earlier we hard coded &units=metric into the URL but this isn't good for people who are expecting the temperature in other formats.

Edge Resolvers also allow you to configure arguments that can be passed to fields, we'll do this for the units and set the default to metric.

To do this, we will:

  • Add the enumeration definition Unit
  • Add args to weather with a default value

Inside grafbase.config.ts you should update it to include the new enum and args:

const unit = g.enum('Unit', ['standard', 'metric', 'imperial']) g.extend('ContentfulProperty', { weather: { args: { unit: g.enumRef(unit).default('metric') }, returns: g.float(), resolver: 'contentful/property/weather', }, })

Now inside the resolver code you can fetch the unit value from the second argument, and update the URL used by fetch to now include the unit value &units=${unit}:

export default function Resolver(root, { unit }) { const { location } = root const apiKey = process.env.OPENWEATHER_API_KEY if (!location) return null return fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${location.lat}&lon=${location.long}&units=${unit}&appid=${apiKey}`, ) .then(res => res.json()) .then(({ main }) => main.temp) }

You can now execute the following GraphQL query and pass any of the enums defined to the weather field unit argument:

{ contentful { propertyCollection { items { name location { lat lon } weather(unit: imperial) } } } }

That's it! This guide has been a quick deep dive into how you can extend the Contentful GraphQL API with your own GraphQL types and resolvers using the Grafbase Edge Gateway.

Remember that you can do this with any GraphQL API or REST API using OpenAPI.

Get Started

Build your API of the future now.