How to create a GraphQL API with Firebase Functions and Firestore
A complete walk-through, from zero to live API, suitable for beginners
Almost all web apps need some kind of backend. And, when it comes to building your API, it’s no secret that GraphQL is one of the best technologies around right now.
When you’re just getting started, it can be a bit daunting though. How do you create your first API, and then make it live — without too much fuss?
Let me show you how to get your API up and running in just a few minutes for free.
You can even use this in production in the beginning, though you’ll probably choose to upgrade before long. More on that later.
Prerequisites
This tutorial assumes that you already have a recent version of NodeJS and NPM installed.
Set up
Project
First thing you’ll need is a Firebase project. Head to the Firebase Console and click the Add Project button, then follow the prompts. I called mine graphql-api
.
Firebase will give you back a project ID, which should look something like your project name, possibly with some digits at the end. Take note of this. (mine looks like graphql-api-d03e3
)
Firebase Tools
Install the Firebase Tools to your computer, which we will use to initialize our local project and deploy our API:
npm install -g firebase-tools
Create the Firestore database
While you’re in the Firebase Console, there’s one more thing we need to do before we can really get started: Create the database. Don’t worry — it’s just a few clicks.
In the Firebase Console, go to the Cloud Firestore section, and you should see a big button called Create database. Go ahead and click it.
You’ll want to start in production mode (it means that the access is locked down, which is perfect) and next choose a location that is closest to you.
Get started
Initialize your workspace
Let’s set up the new project on our local machine:
mkdir firebase-graphql-api
cd firebase-graphql-api
npm init -y
npm install --save graphql-yoga
What’s this doing?
- Creating a directory for the project to live
- Initializing NPM
- Installing the NodeJS packages we’ll need: GraphQL-Yoga for the GraphQL service
Initialize Firebase
It’s time to set up the Firebase config in your project directory.
To get started, run firebase init
while in your project directory.
When prompted, choose the project ID you already created, and then select Firestore
and Functions
as the services you want. Then, feel free to accept all the default options until it’s finished.
Once the script is finished, you should notice a new directory in your project called functions
. Great! This is where we’ll put our code in a moment.
GraphQL Directory
We’ll create a new directory, inside the new functions
directory that was created automatically in the last step, to keep our GraphQL-related files
mkdir functions/graphql
Let’s get coding
Firebase Functions entry file
You should find that there’s a JavaScript file already created for you by the Firebase init script, called functions/index.js
This is the file that Firebase Functions will look for in the beginning for the function code. This is where we’ll begin.
Let’s dive straight in there to get started. Make it look like this:
What’s going on?
- We’re importing Firebase Admin, because we need to initialize it for our project
- We’re importing our GraphQL server files (which we haven’t created yet)
- We’re exporting the Firebase Function, to which we give our GraphQL server
GraphQL server file
Let’s get started with our GraphQL server. This one will pull together everything that we’ll create in a moment and set up a GraphQL server that listens for connections, which we’ll then give to Firebase Function to host for us.
Let’s create a new file called functions/graphql/index.js
and make it look like this:
What’s going on here?
- First we import some important things, like our schema (definition of data and methods) and our resolvers (the actual methods)
- Then we initiate a server by passing in those things, plus a couple of options
- Introspection means that a client can ask the server which methods it supports, and the server will share it. Later you should probably turn this off to be a bit more secure
- Playground means that it will host a nice web UI that allows you to test easily. Eventually you’ll probably want to turn this off as well, but it’s very handy for this tutorial, and for development in general.
- Then the server gives us an
express
compatible version of the instance, which is perfect to hand over to Firebase Functions.
GraphQL schema file
Next we’ll create a schema file, which will define the methods and data structures for the GraphQL server.
Go ahead and create a file called functions/graphql/schema.js
and make it look like this:
What’s going on here?
- We’re creating one query method, called
hello
, which takes an optional string parameter calledname
, and returns a string. - The name parameter is optional here, which you can see from the lack of a
!
afterString
- The string that is returned from the server is not optional, though, which you can tell from the presence of the
!
GraphQL resolvers file
Lastly we’ll set up our resolvers file, which is where most of the action happens. This is where we’ll create the main logic for the methods we create in our API.
Let’s create a new file called functions/graphql/resolvers.js
and make it look like this:
What’s going on here?
- We are creating the query function that matches the signature that we defined in the schema file.
- The function is very simple. It receives a
name
parameter, and returns a message back eg. Hello Sally - If the
name
parameter is not defined, then it sends back a default Hello World
Checkpoint #1: Testing the API
In a moment we’ll add some interesting logic which interacts with Firestore, but I think it’s a good idea to do a quick test to see if we’re on track.
Deploy
Let’s deploy our API live to Firebase, and give it a test. Run the following command to deploy to Firebase
firebase deploy
After a successful deployment, a URL should be shared in your terminal, looking something like https://us-central1-your-project-12345.cloudfunctions.net/graphql
Open this URL in your browser, and if everything is working, you should see a GraphQL Playground interface like this.
Note: If you get an error saying Failed to fetch schema. Please check your connection
check the address in the top of the interface, and ensure it has /graphql
at the end.
Query
To run a query, enter the following code on the left side of the interface and hit the big button to run it
{
hello
}
If everything worked, you should see this:
Testing with a parameter
Let’s try another test, but this time we’ll send through the name
parameter
Try this query (and don’t forget to put your name in there)
{
hello(name: "Your Name")
}
Hopefully you’ll see a result like this:
Writing data
Now that we’ve got a functional API, let’s make it a bit more useful by reading and writing data from Firestore.
Schema definition
Before anything else, we need to define how our new method should behave, and the data it should expect.
We’re going to create a little method to add accept a property, and then add some data to a collection
Let’s update the functions/graphql/schema.js
file like this
What’s going on here?
- We’ve just added our first Mutation — this is an operation which is designed to change (or “mutate”) things, rather than just “query” them.
- Our mutation, called
addTodo
will accept one string parameter calledtext
, and will then return another string (we’ll send back the Database ID)
Resolver method
Now that the schema is defined, and GraphQL knows what to expect from our function, let’s create the function!
Head back to the functions/graphql/resolvers.js
file and add a new function. Here’s what I think it should look like:
What’s going on here?
- We’ve created a little function matching the schema signature from above, which does a simple write operation with Firestore.
- It takes the
text
value, and adds a document to thetodos
collection - Then it returns the document ID as a string to the client
Test
So let’s go ahead and deploy our changes with firebase deploy
and then open up the GraphQL Playground again so we can test.
Here’s the command you can try out to test if it’s working:
mutation {
addTodo(text: "test")
}
If it worked, you should see a result like this, which includes the Firestore ID for the record:
Reading Data
Now that we’ve written some data to Firestore, it would be great to read it again! Let’s create a new query method for that.
Schema definition
Once again, let’s set it up in the functions/graphql/schema.js
file first:
What’s going on here?
- Jumping ahead a little bit to line 11, we’ve now created a new “type” called
Todo
. This is simply a way of defining the structure of data we want to send or receive. - We’ve created a new Query method signature for a method called getTodos
- This method will return a list of
Todo
type. You can tell it’s a list from the square brackets surrounding the type name[Todo]
. If we were just returning one, then we would leave off the brackets.
Resolver method
And of course now let’s get back to the functions/graphql/resolvers.js
file to write the method:
What’s going on here?
- Once again, our new method is created to match the signature defined in the schema file.
- It does a simple query of all the document records in the
todos
collection, and returns all the data from them
Test
You know the drill by now! Let’s deploy it again with firebase deploy
and test it using the following query
{
getTodos {
text
}
}
In this query, we are also specifying which fields we want to read from the todo objects. In this case, we just want the text
field… which is good because that’s the only field that exists! In the real world, you’ll have many more data fields, and you can choose just a subset of them for your query, according to your needs.
You should see something like this, if all went well
Go ahead and create a few more, and query a few more times as well.
Congratulations!
Congratulations! You’ve got your GraphQL API built, and deployed live. It wasn’t too tough, right?
So, now that you’ve got your very own live GraphQL API, there’s a few things that should really happen next.
Authentication, Nesting, Performance, Safety
First of all, this API is not secured in any way. Anyone can come along and write to your database, and we even provided them a nice interface to do it!
In a real scenario, you may want to tighten security by authenticating requests, to ensure someone is logged in and allowed to execute them. You may also want to turn off the introspection
and playground
options (just set them to false
and redeploy), to at least make it less easy to mess around.
There’s also a good chance that you’ll want to eventually do some things that are a little more complex than what we achieved here. For example, one of the great things about GraphQL is the ability to query related data objects in a nested query.
And, while this is a great way to get started with your API, eventually you may choose to migrate away from Firebase Functions to a more robust server. If you’re just prototyping, or launching an MVP, then Firebase Functions are probably a great option! But keep in mind it may not be a long-term solution for performance and cost reasons. (You may have already noticed that it’s not always very fast)
If you’d like to learn how to do any of these things (or more), you should check out my online course From Zero to Production with GraphQL. I cover all of these topics and many more, including user authentication, integrating with React, NextJS, with easy-to-follow video.
And of course, the full source code from this tutorial can be found here: https://github.com/sneub/firebase-graphql-api
Follow me on Twitter to stay up to date with recent articles, news, and knowledge.
Subscribe to my blog for writing on personal development, entrepreneurship and business.