DEV Community

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

Posted on • Updated 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 Gérard Bousiquot

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