auto
Navigate back to the homepage

How to run a GraphQL Server using Firebase Cloud Functions

Ricky Marcon
October 21st, 2019 · 2 min read

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. To setup a serverless, auto-scaling GraphQL API, we can use Firebase Cloud Functions to seamlessly deploy and test our server!

Prerequisites

This tutorial assumes you have an existing Firebase project with Cloud Functions enabled.

Implementation

To serve our GraphQL API, we are going to take advantage of the ability to pass an Express.js app to the onRequest() argument for an HTTP function (see example below). For more info, read Call functions via HTTP requests.

1import * as express from 'express';
2const app = express();
3
4// Expose Express API as a single Cloud Function:
5exports.graphql = functions.https.onRequest(app);

Step 1: Create Apollo GraphQL Express server

Start by creating a simple Apollo Express Server. Apollo Server is an implementation of GraphQL and integrates easily with several popular Node.js middleware libraries, including Express. For more info, read Integrating with Node.js middleware.

1// functions/graphql/server.js
2
3import * as express from 'express';
4import { ApolloServer } from 'apollo-server-express';
5
6import schema from './schema';
7import resolvers from './resolvers';
8
9function createGraphQLServer() {
10 const app = express();
11
12 const server = new ApolloServer({
13 typeDefs: schema,
14 resolvers,
15 // Enable GraphQL GUI
16 introspection: true,
17 playground: true
18 });
19
20 server.applyMiddleware({ app, path: '/', cors: true });
21
22 return app;
23}
24
25export default createGraphQLServer;

Step 2: Define your GraphQL schema

Construct a schema using the GraphQL schema language. The schema represents the structure of your data set and defines what data clients can query.

1// functions/graphql/schema.js
2
3import { gql } from 'apollo-server-express';
4
5const schema = gql`
6 type Query {
7 hello: String
8 }
9`;
10
11export default schema;

Step 3: Define a resolver

Resolvers tell Apollo Server how to fetch the data associated with a particular type. In this example, we haved hardcoded the hello field to return “Hello world!“. Though, you could just as easily make a database call to return a value instead.

1// functions/graphql/resolvers.js
2
3// Provide resolver functions for your schema fields.
4const resolvers = {
5 Query: {
6 hello: () => 'Hello world!'
7 }
8};
9
10export default resolvers;

Step 4: Connect cloud function handler

With the server, schema and resolvers ready, we can go ahead and hook up our Apollo Express Server to an HTTP function.

1// functions/index.js
2
3import { https } from 'firebase-functions';
4import createGraphQLServer from './graphql/server';
5
6const server = createGraphQLServer();
7
8// Graphql API
9// https://<region-name>-<project-name>.cloudfunctions.net/graphql
10const graphql = https.onRequest(server);
11
12export { graphql };

Step 5: Test server locally

To ensure everything is in working order, we can use the Firebase Emulator to run our function locally before deploying.

1firebase emulators:start --only functions

You should now have a local url like http://localhost:5001/<project-id>/<region>/graphql that you can open up to view the GraphQL GUI that gets shipped with Apollo.

graphql playground localhost


Limitations

This approach to running a serverless GraphQL API has one major drawback; You cannot use GraphQL subscriptions. GraphQL subscriptions are a way to push data from the server to clients that choose to listen to real time messages from the server. To do this, GraphQL uses websockets to maintain a long-lasting connection with a client. Currently, Google Cloud Functions don’t support subscriptions.

Be careful with polling

Polling provides near-real-time synchronisation with your server by causing a query to execute periodically at a specified interval (see example below).

1const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
2 variables: { breed },
3 skip: !breed,
4 pollInterval: 500,
5});

Polling is enabled by passing the pollInterval configuration option to the useQuery hook. By setting the pollInterval to 500, you’ll be making a fetch request to the server every 0.5 seconds. This can be problematic if our GraphQL API is running on a cloud function because it means we are triggering an invocation every 0.5 seconds.

In testing, I managed to trigger over a million invovations in less than 24 hours. Therefore, with a current flat rate of $0.4 per million invocations, the cost of polling using cloud functions is inefficient.


Conclusion

By design, Cloud Functions only handles one request per instance, ensuring each request has the full amount of compute and memory allocated to it. This may make rapid scaling slower with Cloud Functions. Therefore, I would not recommend using the above approach in a production environment.

For setting up a productiong grade GraphQL API server, I would containerise the above Apollo server and deploy it inside a fully-managed serverless container platform, such as Google App Engine (Flex) or Google Cloud Run.

More articles from Ricky Marcon

React Hooks

A collection of useful React hooks.

August 3rd, 2019 · 1 min read
© 2019 Ricky Marcon
Link to $https://stackoverflow.com/users/3998659/rickyLink to $https://twitter.com/ricky_marconLink to $https://www.linkedin.com/in/rickymarcon