DEV Community

Cover image for Using Netlify functions to host a GraphQL API with a React frontend
Akram Saouri
Akram Saouri

Posted on Edited on

Using Netlify functions to host a GraphQL API with a React frontend

This quick demo assumes you’re already familiar with Netlify as a deployment service, GraphQL as an API technology and React.

The starting point was a normal React project created with create-react-app then deployed on Netlify (you know…the usual)

Then I arrived to the point where I found out I needed an API to do some backend-related stuff, I (innocently) reached out to create a new repository, wrote some GraphQL resolvers/mutations, committed the whole thing but just when I was looking for a cheap vps to host it in, I stopped for a second then said to my self: why not do it on Netlify itself?

Narrator: he did not regret it.

The process was as straightforward as this:

  1. I’ve started by completely moving the backend files from their separate repo to inside src/ folder in the frontend repo:

  2. Then I’ve added netlify-lambda as a dev dependency to the project; this is a tool created by the netlify team and provides a runtime for lambda functions.

  3. I did not have a netlify.toml file at the time so I created one, and updated the content with this:

    [build]
      command = "yarn build" # the command you run to build this file
      functions = "built-lambda" # netlify-lambda builds to this folder AND Netlify reads functions from here
      publish = "build" # create-react-app builds to this folder, Netlify should serve all these files statically
    
    // This helps the `netfliy-lambda` do its job and helps Netlify figuring out where to look for your functions when deployed. 
    // Note: the `functions` field will probably be different in your project depending on where you decided to put the GraphQL functions
    
  4. The I’ve added two new scripts to my package.json:

    - ”start:lambda”:”netlify-lambda serve src/lambda”

    - ”build:lambda”:”netlify-lambda build src/lambda”

  5. before going all lambda crazy, the backend repository was using a normal apollo-server but now I needed to find a lambda compatible one, luckily apollo-server-lambda does exactly this and barely required any changes on the existing files, the graphql.js looked like this:

        const { ApolloServer } = require('apollo-server-lambda');
    
        const { typeDefs } = require('./lib/typeDefs');
        const { resolvers } =require('./lib/resolvers');
    
        const lambda = newApolloServer({
          typeDefs,
          resolvers,
          playground: true,
          introspection: true
        );
    
        exports.handler = lambda.createHandler({
          cors: {
            origin: '*',
            credentials: true
          }
        });
    
  6. The last piece now was to wire the GraphQl client with the Netlify function:

    On the frontend, I’m working with urql so I had to update the client initialisation to this:

  const client = createClient({
    url: process.env.NODE_ENV === 'development'
       ? 'http://localhost:9000/.netlify/functions/graphql'
       : '/.netlify/functions/graphql'
   })

The http://localhost:9000/.netlify/functions/graphql URL is the default one when running npm run start:lambda locally but when deploying to Netlify the functions are hosted on the same domain hence the check.

Note: This code is still valid for your favorite GraphQL client too.

Now once I published these new changes to Netlify, it detected that I’ve added a new function and did its magic:

Couple of notes

  • When working with databases, the standard approach of starting the database with the server does not work in this case since the lambda function is given a limited time to run on every request and will be shut down after the request is resolved, the solution is to open a database connection on every incoming request and cache it the between requests. I was able to do that (with mongdb in my case) using something like:
    let cachedDb
    if(cachedDb && cachedDb.serverConfig.isConnected()){
        context.db = cachedDb;
    } else {
        context.db = await connectDB();
        cachedDb = context.db;
    }
  • You can simulate a full working Netlify runtime locally using their Netlify Dev | Netlify tool, this comes handy when you want to debug you full wired app locally.

Relevant Links

Top comments (1)

Collapse
 
jgb profile image
Jean Bousiquot •

that sounds interesting. Is this project online? If so, where? I'd like to try it and compare the speed.