Accessing MongoDB using a GraphQL interface

MongoDB Atlas is a powerful, multi-cloud application data platform used by millions of developers. Millions of developers also recognize that GraphQL is a fantastic way to access data, because it provides a single, flexible API from which they can access all their databases and data APIs. So a natural next question is: can we use GraphQL to access data stored in MongoDB?

Yes, MongoDB gives you two options:

  1. Work with MongoDB Atlas’s GraphQL API. It is a super easy way of getting a working GraphQL API on top of MongoDB. Many developers would like to customize the GraphQL API, and that does take additional custom code.
  2. Work with MongoDB Atlas’s Data API. The Data API is REST-like and super flexible, which means you can create your own hosted GraphQL API server, with the Data API serving as the layer communicating to the database. However, this requires writing and deploying a lot of code, as well as managing infrastructure.

The question is: can we have the best of both worlds? Can we get the ease of the built-in GraphQL API with the flexibility of the hand-coded GraphQL API? Can we get our custom MongoDB GraphQL API without having to write and maintain code? And furthermore, can we add other data sources and APIs to our GraphQL API just as easily?

In this article we’ll show you that the answer is yes! We’ll build out a GraphQL API on StepZen using the MongoDB Atlas Data API without writing any code. We’ll customize and even mix in some other services into our custom GraphQL API.

Let's get started.

Enable Data API in MongoDB

MongoDB Atlas provides an easy way to host and manage your data in the cloud. If you do not already have a MongoDB Atlas account, follow the getting started documentation here to create an account and deploy a free cluster. Then, follow the instructions on the sample data page to load the cluster with some sample datasets.

Navigate to the Data API page to enable the Data API:

Mongo DB Atlas Data API image

Then click the "Create API Key" button to create an API Key. Copy it somewhere safe, as you'll need it later. You will also need to refer to the "URL Endpoint" listed here as well.

Click on the green “Test Your API” button to validate the API is working with your new key. Pick the cluster, database and collection. We will be using the listingsAndReviews collection in the sample_airbnb database, but of course you can use anything. Make sure the API call returns data.

Test Your API image

Now that you have enabled the Data API and verified access, we can start to build our GraphQL API using StepZen.

Signup for StepZen

Go to StepZen Signup to create your StepZen account.

First, we need to install the StepZen CLI and log in:

  1. Install the StepZen CLI

    npm install -g stepzen

  2. Login using the account and admin key which you can find on the My Account page.

    stepzen login

Now you are ready to configure and deploy your GraphQL API.

First, create a working directory. Give it any name you like, and cd into that directory.

~/demo
➔ mkdir mongodb

~/demo
➔ cd mongodb

~/demo/mongodb
➔

Now, you need to define the GraphQL schema and configuration for your StepZen GraphQL API. Create a file called index.graphql and paste the following into it:

scalar MongoFilter
scalar MongoProjection
scalar MongoSort

type Query {
  mongo(
    dataApikey: String!
    dataSource: String!
    collection: String!
    database: String!
    filter: MongoFilter
    projection: MongoProjection
    sort: MongoSort
    limit: Int
    skip: Int
  ): JSON
    @rest(
      endpoint: "https://data.mongodb-api.com/app/<Data API App ID>/endpoint/data/beta/action/find"
      method: POST
      headers: [{ name: "api-key", value: "$dataApikey" }]
    )
}

Important! Edit the endpoint URL to match the URL Endpoint listed on your Data API page. The Data API endpoint contains a unique id specific to each cluster. You will replace <Data API App ID> with something that looks like "data-xqgfu". For example:

endpoint: "https://data.mongodb-api.com/app/data-xqgfu/endpoint/data/beta/action/find"

Let’s take a minute to review what’s in this file. Unlike a typical GraphQL schema definition, there are no types (yet… you will be adding those in a few moments). There is a single query defined that takes a few parameters and returns JSON. That query has a StepZen directive, @rest, which instructs the StepZen server to make an http request to the given endpoint using the POST method. That’s all it takes!

Save that file, and in your terminal run the stepzen start CLI command. The command will prompt you to name the endpoint of your StepZen GraphQL API, and then deploy it:

Deploying api/mongo to StepZen... done in 4.6s 🚀

Your API url is  https://*****.stepzen.net/api/mongo/__graphql

You can test your hosted API with cURL:

curl https://*****.stepzen.net/api/mongo/__graphql \
   --header "Authorization: Apikey {{insert yours from https://stepzen.com/account}}" \
   --header "Content-Type: application/json" \
   --data '{"query": "your graphql query"}'

or explore it with GraphiQL at  http://localhost:5001/api/mongo

Watching ~/demo/mongodb for GraphQL changes...

The start command instructs the StepZen CLI to validate and upload your schema and deploy it. Just like that, your API is live on StepZen's scalable service and ready to call. The CLI continues to watch the directory for changes to your schema files, uploading and deploying any changes as they happen. It also runs a local proxy to your deployed schema with a GraphiQL client, so it’s easy for you to run queries and test your deployed GraphQL API.

Click the localhost link in your terminal or open a browser and navigate to the link. Copy and paste the following query and run it:

query MyQuery(
  $dataApikey: String!
  $datasource: String!
  $database: String!
  $collection: String!
) {
  mongo(
    dataApikey: $dataApikey
    collection: $collection
    database: $database
    dataSource: $datasource
    limit: 10
  )
}

Be sure to set the query variables appropriately, e.g.:

{
  "database": "sample_airbnb",
  "dataApikey": "YOUR_API_KEY",
  "datasource": "Cluster0",
  "collection": "listingsAndReviews"
}

You should see results like the following:

Simple JSON Results

At this point you have a working GraphQL endpoint, deployed on StepZen, protected by an API key. However, it’s not quite done yet, since we cannot select the fields we want in typical GraphQL fashion. Recall that our schema did not have any types defined, and our query simply returned JSON. Let’s add the ability to select types now. StepZen has published a tool that will take sample JSON and create GraphQL schema to match, available at https://json2sdl.com. You will use that now to create the types for your schema.

  1. Copy the entire output of the query from the GraphiQL window.
  2. Paste the output into the JSON2SDL tool, and click ‘Convert’.
  3. Copy the Output SDL from the JSON2SDL tool and paste it into the bottom of the index.graphql file you created in the first step.
  4. Since we really just want to return the documents from the database, we're going to clean up the types a bit.

    • Delete the types Root, Data, and Mongo, and change the mongo query to return [Document] instead of JSON.
    • Because the Data API returns all documents under a documents object, we will need to add one additional attribute to the StepZen @rest directive called resultroot. This attribute tells StepZen where to look for the results in the API response:
    resultroot: "documents[]"

Your index.graphql file should look like this:

scalar MongoFilter
scalar MongoProjection
scalar MongoSort

type Query {
  mongo(
    dataApikey: String!
    dataSource: String!
    collection: String!
    database: String!
    filter: MongoFilter
    projection: MongoProjection
    sort: MongoSort
    limit: Int
    skip: Int
  ): [Document]
    @rest(
      endpoint: "https://data.mongodb-api.com/app/data-xqgfu/endpoint/data/beta/action/find"
      method: POST
      headers: [{ name: "api-key", value: "$dataApikey" }]
      resultroot: "documents[]"
    )
}

type Location {
  coordinates: [Float]
  is_location_exact: Boolean
  type: String
}
... continued ...

Save the file, and the StepZen CLI will redeploy your schema. Once it completes, reload the local proxy browser, and now you can select fields to return in your query. For example, try running the following query:

query MyQuery(
  $dataApikey: String!
  $datasource: String!
  $database: String!
  $collection: String!
  $filter: MongoFilter
) {
  mongo(
    dataApikey: $dataApikey
    collection: $collection
    database: $database
    dataSource: $datasource
    filter: $filter
    limit: 10
  ) {
    name
    beds
    bedrooms
    address {
      market
    }
  }
}

with the following variables:

{
  "database": "sample_airbnb",
  "dataApikey": "YOUR_API_KEY",
  "datasource": "Cluster0",
  "collection": "listingsAndReviews",
  "filter": { "address.market": "Istanbul" }
}

Now we are truly able to run use our StepZen GraphQL API to run queries against MongoDB Atlas using the Data API. Next, we'll customize our StepZen API further.


Customizing the GraphQL API

We've seen how easy it is to get a GraphQL endpoint from the MongoDB Data API. You did not have to write any resolvers at all! Further customization of the GraphQL API is just as easy. As examples, we'll make two additional changes now:

  1. Change type names and field names using setters argument for an @rest call.
  2. Mix in any external API (for example, that might keep some relevant but current data that would not be in MongoDB).

Change type names and field names

  1. Let us say that you don't want the reviews object to be visible. Easy! Just delete the field from type Document. That field will no longer be available to end users, and StepZen will not map it from the REST API.
  2. If you want to rename the field beds to number_of_guests, change the field name in type Document to number_of_guests, and add a parameter setters to the @rest call, as follows:
type Query {
  mongo(
    dataApikey: String!
    dataSource: String!
    collection: String!
    database: String!
    filter: MongoFilter
    projection: MongoProjection
    sort: MongoSort
    limit: Int
    skip: Int
  ): [Document]
    @rest(
      endpoint: "https://data.mongodb-api.com/app/data-xqgfu/endpoint/data/beta/action/find"
      method: POST
      headers: [{ name: "api-key", value: "$dataApikey" }]
      resultroot: "documents[]"
      setters: [{ field: "number_of_guests", path: "beds" }]
    )
}

Mix in an external API

The MongoDB listing database only lists country codes, names etc. But each country has a rich set of other information, that we can easily use to enhance the returned data. So we will use REST Countries to enhance it.

First create a new GraphQL endpoint for REST Countries

Add the following lines to the bottom of the index.graphql that contains your MongoDB query:

type Query {
  restCountry(country_code: String!): JSON
    @rest(endpoint: "https://restcountries.com/v2/alpha/$country_code")
}

Once the StepZen CLI uploads your changes (if you've killed it, just run stepzen start again in your working directory), you will see that you have a new query available in your GraphQL API:

query RestCountries {
  restCountry(country_code: "TR")
}

Now we'll repeat the steps we did previously to generate the GraphQL types for the countries API. Take the output of the query and convert it using the JSON2SDL tool. Copy the SDL into the bottom of index.graphql. Delete type Root and type Data and change the query restCountry to return RestCountry as opposed to jSON.

type Query {
  restCountry(country_code: String!): RestCountry
    @rest(endpoint: "https://restcountries.com/v2/alpha/$country_code")
}

So now we have two, fully functional GraphQL queries in our API, mongo and restCountry.

Link the two APIs

With this country information now available, it is trivial in StepZen to add it to the data being returned from mongo. Add the following code to the bottom of index.graphql and save the file:

extend type Address {
  country_info: RestCountry @materializer(query: "restCountry")
}

That is it!! We've just told StepZen to make RestCountry available under a field named country_info in the Address type. Now you can run a query like:

query MyQuery(
  $dataApikey: String!
  $datasource: String!
  $database: String!
  $collection: String!
  $filter: MongoFilter
) {
  mongo(
    dataApikey: $dataApikey
    collection: $collection
    database: $database
    dataSource: $datasource
    filter: $filter
    limit: 10
  ) {
    name
    number_of_guests
    bedrooms
    address {
      market
      country_info {
        capital
        population
      }
    }
  }
}

with variables like:

{
  "database": "sample_airbnb",
  "dataApikey": "YOUR_API_KEY",
  "datasource": "Cluster0",
  "collection": "listingsAndReviews",
  "filter": {}
}

And you should see results like:

{
  "data": {
    "mongo": [
      {
        "name": "Ribeira Charming Duplex",
        "number_of_guests": 5,
        "bedrooms": 3,
        "address": {
          "market": "Porto",
          "country_info": {
            "capital": "Lisbon",
            "population": 10305564
          }
        }
      },
      {
        "name": "Horto flat with small garden",
        "number_of_guests": 2,
        "bedrooms": 1,
        "address": {
          "market": "Rio De Janeiro",
          "country_info": {
            "capital": "Brasília",
            "population": 212559409
          }
        }
      },
      {
        "name": "Ocean View Waikiki Marina w/prkg",
        "number_of_guests": 1,
        "bedrooms": 1,
        "address": {
          "market": "Oahu",
          "country_info": {
            "capital": "Washington, D.C.",
            "population": 329484123
          }
        }
      },
... etc.

Summarizing what we did

  1. We took MongoDB's Data (REST) API and instantly created a GraphQL endpoint from it using StepZen.
  2. We easily changed the names of the types, fields etc. using simple declarative constructs.
  3. We added an external API (REST Countries) to the same GraphQL schema in StepZen, creating additional functionality.
  4. We linked the two together with just 4 lines of code!

You now have one GraphQL API that accesses data from two separate sources in a single API call, demonstrating how powerful a declarative, configuration-driven GraphQL API can be. Next, I would recommend learning how to securely store your configuration data (like API keys) and how to structure your project so it’s easy to maintain.

We hope you liked what you saw. There's much more to learn about what StepZen can do. We'd love to hear about what you're doing with MongoDB and StepZen! Visit our Discord Community and let us know.