How to create a GraphQL API with Firebase Functions and Firestore

Shane Neubauer
9 min readJan 31, 2021

--

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 called name, and returns a string.
  • The name parameter is optional here, which you can see from the lack of a ! after String
  • 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 called text, 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 the todos 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.

--

--