Scalable APIs with GraphQL and Netlify Functions

Ibrahima Ndaw

JavaScript enthusiast, Full-stack developer⚛️ & blogger📝

In this tutorial, we will be exploring serverless functions by learning how to use them with GraphQL to build scalable APIs.

What is serverless?

A serverless function is a way for adding a backend to your app without managing a server, virtual machine, or docker container. Behind every serverless function invocation is a server hosted and managed by a cloud provider such as AWS, Google Cloud, Netlify, Vercel, and so on. The providers are abstracting away a lot of the complexity so you can focus on just what your function needs to do. Serverless functions are basically functions-as-a-service.

What we’re building

In this tutorial, we will be using Netlify Functions, which are built on top of AWS Lambda Functions. We'll be building a Project Tracker with GraphQL, serverless functions, and Airtable. We will be using The Airtable API to create and retrieve projects. Let's get started!

Setting up

Before we get into creating a new lambda function, we need to sign up for an Airtable account to be able to use it as a DB. After you create an account, open your command-line interface and running the following:

npm install netlify-cli -g

This command will install Netlify CLI, which we will us to create a new serverless function. Next we need to set up our project, execute the following:

mkdir functions && touch netlify.toml

The command will first create a new folder named functions then will create a new file netlify.toml at the root. Netlify uses this file to configure the resources you’ll be using, in this case functions. With this in place, add the following code below to netlify.toml.

// netlify.toml

[build]
    functions = "functions"

These two lines tell Netlify that our serverless functions live in a folder named functions. Now, open your CLI and make sure to be in the folder that contains the functions folder.

netlify functions:create serverless-graphql

The Netlify CLI will prompt you with options to choose between several ready to use templates. Choose the one named >[apollo-graphql] GraphQL function using Apollo-Server-Lambda!. It’s the starter for the serverless function that uses Apollo GraphQL. Once the project is initialized, cd into the functions folder, then install the Airtable SDK.

npm install airtable

Next, create a .env file at the root of the project and replace the variables with your credentials.

// .env

AIRTABLE_API_KEY=<your-api-key>
AIRTABLE_BASE_ID=<your-table-base-id>
AIRTABLE_TABLE_NAME=<your-table-name>
  1. AIRTABLE_API_KEY is your API key on your Airtable account.
  2. AIRTABLE_BASE_ID is the id of your DB table (see here).
  3. AIRTABLE_TABLE_NAME is the name given to your table.

With our setup and configuration complete, we can dive into coding our serverless function.

Building the serverless function with GraphQL

First, structure the project like so:

functions
├── graphql.js
├── utils
|  └── airtable.js
└── package.json

As you can see, the logic to interact with Airtable is holden by the file airtable.js. The entry point of the serverless function is graphql.js.

Connecting with Airtable

Let’s add the code below to the file airtable.js.

// utils/airtable.js

const Airtable = require('airtable')

const { AIRTABLE_API_KEY, AIRTABLE_BASE_ID, AIRTABLE_TABLE_NAME } = process.env

const base = new Airtable({ apiKey: AIRTABLE_API_KEY }).base(AIRTABLE_BASE_ID)

const table = base(AIRTABLE_TABLE_NAME)

const getAllProjects = async () => {
  const allProjects = await table.select({}).firstPage()
  return allProjects.map(({ id, fields }) => transformResponse(id, fields))
}

const addProject = async ({ project }) => {
  const { name, description, date } = project
  const createProject = await table.create([
    {
      fields: {
        name,
        description,
        date,
        status: 'In progress',
      },
    },
  ])
  const { id, fields } = createProject[0]
  return transformResponse(id, fields)
}

const transformResponse = (id, fields) => ({
  id,
  name: fields.name,
  description: fields.description,
  date: fields.date,
  status: fields.status,
})

exports.getAllProjects = getAllProjects
exports.addProject = addProject

Airtable allows us to connect our app to Airtable with the credentials passed in as arguments to it. After that, we initialize the DB table with the table constant.

Next, we retrieve all projects from Airtable using the function getAllProjects(). To add a new project, we rely on the method addProject(), which receives the object to add as a parameter. Finally, we use the method table.create() to persist the data on the DB.

Now, we have the functions needed to add and fetch the projects from Airtable. Let’s use them in the file graphql.js to perform the queries.

Creating the API with GraphQL

// graphql.js

const { ApolloServer, gql } = require('apollo-server-lambda')
const { getAllProjects, addProject } = require('./utils/airtable')

const typeDefs = gql`
  type Project {
    id: ID
    name: String
    description: String
    date: String
    status: String
  }
  input ProjectInput {
    name: String
    description: String
    date: String
  }
  type Query {
    getProjects: [Project]
    addProject(project: ProjectInput): Project
  }
`

const resolvers = {
  Query: {
    getProjects: () => {
      try {
        const allRecords = getAllProjects()
        return allRecords
      } catch (error) {
        throw new Error(error)
      }
    },
    addProject: (_, args) => {
      try {
        const createProject = addProject(args)
        return createProject
      } catch (error) {}
    },
  },
}

const server = new ApolloServer({
  typeDefs,
  resolvers,
})

const handler = server.createHandler()

module.exports = { handler }

If you have experience with Apollo Server, you should already notice that the library used here (apollo-server-lambda) is different from the one used for building servers. This package uses middleware to inject our lambda serverless function to Apollo Server.

Next , we import the functions getAllProjects and addProject from airtable.js. With this, we can define a new GraphQL Schema using gql. The query getProjects has to return an array of type Project. The method addProject expects an object of type ProjectInput as a parameter and should return a value that reflects the Project type.

Any GraphQL Schema has to have a GraphQl resolver that corresponds to it. That’s why, here, we have on the resolvers object, the functions getProjects() and addProject(). The first fetch all projects from Airtable and the second add a new object to the table.

Now, we have a schema and a resolver. We need to pass in the values to the constant server to let it handle the request when the endpoint /graphql is hit.

Testing the GraphQL API

With this step, the serverless app is ready to be tested in the browser. So, begin by browsing to the root of the project and running this command:

netlify dev

Our serverless function should be up and running and accessible here:

The app will land on the GraphQL Playground. It’s a neat tool built on top of GraphiQL. It’s a GraphQL IDE for sending queries or mutations, exploring the API docs, sending HTTP headers, and more.

http://localhost:8888/.netlify/functions/graphql

Now, add this code block below to create a new project.

mutation {
  addProject(project: { name: "My first project", description: "First project's description", date: "2020-12-11" }) {
    name
    description
    date
    status
  }
}

After sending the query, you should see this:

create-post

{
  getProjects {
    id
    name
    description
    date
    status
  }
}

Once all projects are fetched, the result should look like this:

get-all-posts

Next steps

Awesome! Our serverless function is looking nice. We’ve built a GraphQL API using serverless functions and Airtable. Let’s now deploy it to Netlify!

To do so, we first need to sign up here. Then, initialize our app with git. Make sure to be at the root of the project before executing the following.

git init

Add a .gitignore file at the root of the project.

touch .gitignore

Add this code block to ignore the files listed below when adding changes to git.

// .gitignore

/functions/node_modules
/functions/.env

Add and commit changes to git.

git add -A && git commit -m 'Ready to deploy on Netlify'

Create a new repository on Github and follow the steps to push your project. Next, go to Netlify and connect your Github repo. Move the package.json file to the root of the project, then create the environment variables on Netlify.

Deploy your serverless functions. We can now preview and interact with our app here: https://graphql-airtable.netlify.app/.netlify/functions/graphql

You can find the finished project in this Github repo. Thanks for reading!

Conclusion

Serverless is an exciting piece of technology. It allows us to build up a backend quickly without the hassle of managing a server, which brings us to the JAMStack. An architecture designed to make the web faster, more secure, and easier to scale with static sites and serverless functions.