Instant GraphQL APIs for MongoDB with Grafbase
Jamie Barton7 min read • Published Oct 12, 2023 • Updated Oct 12, 2023
Rate this tutorial
In the ever-evolving landscape of web development, efficient data management and retrieval are paramount for creating dynamic and responsive applications. MongoDB, a versatile NoSQL database, and GraphQL, a powerful query language for APIs, have emerged as a dynamic duo that empowers developers to build robust, flexible, and high-performance applications.
When combined, MongoDB and GraphQL offer a powerful solution for front-end developers, especially when used at the edge.
You may be curious about the synergy between an unstructured database and a structured query language. Fortunately, Grafbase offers a solution that seamlessly combines both by leveraging its distinctive connector schema transformations.
In this tutorial, you’ll see how easy it is to get set up with MongoDB and Grafbase, simplifying the introduction of GraphQL into your applications.
You will need the following to get started:
- An account with Grafbase
- An account with MongoDB Atlas
- A database with data API access enabled
For the purposes of this tutorial, I’ve created a free shared cluster with a single database deployment. We’ll refer to this instance as your “Data Source” later.
Locate Data API Access from the Services menu inside the MongoDB Atlas dashboard and enable Read/Write access:
Once enabled, copy the URL Endpoint and save it somewhere. We’ll need it in the next section.
Now navigate to Data API > Users and click Create API Key. Give it a name and generate:
Copy the API Key and save it somewhere. We’ll need it in the next section.
Finally, navigate to the Cluster collections and click Add My Own Data to give your database and first collection a name.
Finally, make note of your Linked Data Source and Database you enabled the data API for.
Let’s begin by creating a new Grafbase project locally using the CLI.
We’ll be using the
npx
package runner to invoke the Grafbase CLI, which means we don’t need to install anything.1 npx grafbase init my-mongodb-api
This command will generate a new directory called
my-mongodb-api
that includes the initial config for Grafbase. You are safe to run this command inside an existing project, such as a frontend repo that you’re creating a GraphQL API for.You’ll be prompted to select the configuration format for Grafbase. Your choices here are TypeScript and SDL. The TypeScript SDK provides a better developer experience when working with the SDK. You don’t need to be using TypeScript in your frontend project to use this configuration format. It’s sole purpose is for the use of configuring your Grafbase back end.
Before we continue, open the file
grafbase/.env
and add the environment variables that we obtained above when we enabled the data API:1 MONGO_ATLAS_URL= 2 MONGO_API_KEY= 3 MONGO_DATASOURCE= 4 MONGO_DATABASE=
Now we’ve set up the data API and scaffolded a Grafbase project, we can invoke the Grafbase MongoDB connector with the credentials we obtained previously.
Open the file
grafbase/grafbase.config.ts
and update the contents with the following:1 import { config, connector, g } from '@grafbase/sdk' 2 3 const mongodb = connector.MongoDB('MongoDB', { 4 url: g.env(‘MONGO_ATLAS_URL’), 5 apiKey: g.env(‘MONGO_API_KEY’), 6 dataSource: g.env(‘MONGO_DATASOURCE’), 7 database: g.env(‘MONGO_DATABASE’) 8 }) 9 10 g.datasource(mongodb) 11 12 export default config({ 13 schema: g 14 })
In the code above, we establish a link to MongoDB's Data API by utilizing the environment variables we've incorporated. We then connect this link to the Grafbase Edge Gateway through the
g.datasource(mongodb)
call.The MongoDB connector empowers developers to organize their MongoDB collections in a manner that allows Grafbase to autonomously generate the essential queries and mutations for document creation, retrieval, update, and deletion within these collections.
Within Grafbase, each configuration for a collection is referred to as a "model," and you have the flexibility to employ the supported GraphQL Scalars to represent data within the collection(s).
It's important to consider that in cases where you possess pre-existing documents in your collection, not all fields are applicable to every document.
Let’s work under the assumption that you have no existing documents and want to create a new collection for
users
. Using the Grafbase TypeScript SDK, we can write the schema for each user model. It looks something like this:1 const address = g.type('Address', { 2 street: g.string().mapped('street_name') 3 }) 4 5 mongodb 6 .model('User', { 7 name: g.string(), 8 email: g.string().optional(), 9 address: g.ref(address) 10 }) 11 .collection('users')
This schema will generate a fully working GraphQL API with queries and mutations as well as all input types for pagination, ordering, and filtering:
userCreate
– Create a new useruserCreateMany
– Batch create new usersuserUpdate
– Update an existing useruserUpdateMany
– Batch update usersuserDelete
– Delete a useruserDeleteMany
– Batch delete usersuser
– Fetch a single user recorduserCollection
– Fetch multiple users from a collection
MongoDB automatically generates collections when you first store data, so there’s no need to manually create a collection for users at this step.
We’re now ready to start the Grafbase development server using the CLI:
1 npx grafbase dev
This command runs the entire Grafbase GraphQL API locally that you can use when developing your front end. The Grafbase API communicates directly with your Atlas Data API.
Once the command is running, you’ll be able to visit http://127.0.0.1:4000 and explore the GraphQL API.
Let’s test out creating users inside our MongoDB collection using the generated
userCreate
mutation that was provided to us by Grafbase.1 mutation { 2 mongo { 3 userCreate(input: { 4 name: "Jamie Barton", 5 email: "jamie@grafbase.com", 6 age: 40 7 }) { 8 insertedId 9 } 10 } 11 }
If everything is hooked up correctly, you should see a response that looks something like this:
1 { 2 "data": { 3 "mongo": { 4 "userCreate": { 5 "insertedId": "65154a3d4ddec953105be188" 6 } 7 } 8 } 9 }
You should repeat this step a few times to create multiple users.
Now we’ve created some users in our MongoDB collection, let’s try updating a user by
insertedId
:1 mutation { 2 mongo { 3 userUpdate(by: { 4 id: "65154a3d4ddec953105be188" 5 }, input: { 6 age: { 7 set: 35 8 } 9 }) { 10 modifiedCount 11 } 12 } 13 }
Using the
userUpdate
mutation above, we set
a new age
value for the user where the id
matches that of the ObjectID we passed in.If everything was successful, you should see something like this:
1 { 2 "data": { 3 "mongo": { 4 "userUpdate": { 5 "modifiedCount": 1 6 } 7 } 8 } 9 }
Deleting users is similar to the create and update mutations above, but we don’t need to provide any additional
input
data since we’re deleting only:1 mutation { 2 mongo { 3 userDelete(by: { 4 id: "65154a3d4ddec953105be188" 5 }) { 6 deletedCount 7 } 8 } 9 }
If everything was successful, you should see something like this:
1 { 2 "data": { 3 "mongo": { 4 "userDelete": { 5 "deletedCount": 1 6 } 7 } 8 } 9 }
Grafbase generates the query
userCollection
that you can use to fetch all users. Grafbase requires a first
or last
pagination value with a max value of 100
:1 query { 2 mongo { 3 userCollection(first: 100) { 4 edges { 5 node { 6 id 7 name 8 email 9 age 10 } 11 } 12 } 13 } 14 }
Here we are fetching the
first
100 users from the collection. You can also pass a filter and order argument to tune the results:1 query { 2 mongo { 3 userCollection(first: 100, filter: { 4 age: { 5 gt: 30 6 } 7 }, orderBy: { 8 age: ASC 9 }) { 10 edges { 11 node { 12 id 13 name 14 email 15 age 16 } 17 } 18 } 19 } 20 }
Using the same GraphQL API, we can fetch a user by the object ID. Grafbase automatically generates the query
user
where we can pass the id
to the by
input type:1 query { 2 mongo { 3 user( 4 by: { 5 id: "64ee1cfbb315482287acea78" 6 } 7 ) { 8 id 9 name 10 email 11 age 12 } 13 } 14 }
Every request we make so far to our GraphQL API makes a round trip to the MongoDB database. This is fine, but we can improve response times even further by enabling GraphQL Edge Caching for GraphQL queries.
To enable GraphQL Edge Caching, inside
grafbase/grafbase.config.ts
, add the following to the config
export:1 export default config({ 2 schema: g, 3 cache: { 4 rules: [ 5 { 6 types: 'Query', 7 maxAge: 60 8 } 9 ] 10 } 11 })
This configuration will cache any query. If you only want to disable caching on some collections, you can do that too. Learn more about GraphQL Edge Caching.
So far, we’ve been working with Grafbase locally using the CLI, but now it’s time to deploy this around the world to the edge with GitHub.
If you already have an existing GitHub repository, go ahead and commit the changes we’ve made so far. If you don’t already have a GitHub repository, you will need to create one, commit this code, and push it to GitHub.
Now, create a new project with Grafbase and connect your GitHub account. You’ll need to permit Grafbase to read your repository contents, so make sure you select the correct repository and allow that.
Before you click Deploy, make sure to insert the environment variables obtained previously in the tutorial. Grafbase also supports environment variables for preview environments, so if you want to use a different MongoDB database for any Grafbase preview deployment, you can configure that later.
Now, click Deploy and you will be taken to your project’s dashboard where you can retrieve your API Endpoint and API Key. That’s it! You have a GraphQL API deployed to the edge that is fully managed by Grafbase.
Since Grafbase generates a CRUD API for your MongoDB database and exposes it over HTTP, it’s worth talking about security for a moment.
Grafbase requires you, by default, to pass an
x-api-key
header with requests. It’s recommended to make requests to Grafbase from the server-side only, and not from the client with this API key. Exposing this x-api-key
header on the client should be avoided to prevent any unwanted malicious attacks.All requests we’ve made so far have been inside Pathfinder, which applies this header to all requests by default. Below is an example cURL request that shows how you can make this request outside Pathfinder with the header:
1 curl --request POST \ 2 --url http://api.cartql.com/ \ 3 --header 'Content-Type: application/json' \ 4 --header 'apiKey: YOUR_API_KEY' \ 5 --data '{"query":"query {\n\tmongo {\n\t\tuserCollection(\n\t\t\tfirst: 100\n\t\t\tfilter: { age: { gt: 30 } }\n\t\t\torderBy: { age: ASC }\n\t\t) {\n\t\t\tedges {\n\t\t\t\tnode {\n\t\t\t\t\tid\n\t\t\t\t\tname\n\t\t\t\t\temail\n\t\t\t\t\tage\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"}'
You can obtain your project’s production API key from your Grafbase Project Settings.
In this tutorial, we’ve walked through automatically creating a GraphQL API for collections in our MongoDB database. We also covered reducing trips to the MongoDB database by configuring GraphQL Edge Caching, so wherever your users are, they experience super fast responses.
You can continue your journey with GraphQL by learning how to integrate the GraphQL API we created in this tutorial into your existing web or mobile application using a GraphQL Client. Some popular clients include Apollo Client, URQL, and Houdini.
If you have questions or comments, continue the conversation over in the MongoDB Developer Community.