Serverless GraphQL Server: Deploying to Netlify Functions, 3 ways
Eka
Posted on April 9, 2021
As a detour from my attempt at learning GraphQL resolvers and data sources, I'm going to try... deploying a server without data sources. 😬
There are multiple options for hosting our server.
Apollo Server official documentation has sections on deploying to Heroku, Lambda, Netlify, and Azure Functions. Firebase Cloud Functions is in the same category. All of them (at the time of writing) have free tiers.
For demos or starter templates, web IDEs with Node server like CodeSandbox and Glitch can get you started quickly with near-zero configuration. These are not ideal for production use if you are on a free account, since the server goes to sleep after x minutes of inactivity.
Finally, you can always self-host your server if you have the resources and knowledge.
This post focuses on the first option, which is a good choice for those who have no/little experience deploying servers but want a robust, scalable solution.
I'm going to discuss 3 ways to deploy our server on Netlify Functions. They are essentially similar and achieve the same objective, but with slight variations suited for different needs.
If you've never used or (?)heard of Netlify Functions at all, here is a post I wrote last year about serverless and Netlify Functions in general.
TL;DR? Check out the sample repo at the end of this post.
Basic code and dependencies
Let's start with our basic project setup.
package.json
Like any JS app, we need a package.json file for our project information, commands, and dependencies. We'll get to the commands later—now let's look at the dependencies.
We use apollo-server-lambda instead of apollo-server above. apollo-server-lambda is Apollo Server's AWS Lambda integration, and Netlify Functions uses AWS Lambda under the hood.
graphql (...but of course)
Dev dependencies:
encoding
netlify-cli
Our server app does not directly use the dev dependencies, but rather they enable Netlify to build the code. At the time of writing, without these, we'll get a build error bash: yarn: command not found.
graphql.js
Here is our server code. It has to be named graphql.js. To keep things simple, we use the example from Apollo documentation.
const{ApolloServer,gql}=require('apollo-server-lambda');consttypeDefs=gql`
type Query {
hello: String
}
`;constserver=newApolloServer({typeDefs,mocks:true,playground:true// enable GraphQL Playground IDE on prod env});exports.handler=server.createHandler();// Don't forget to add this!
netlify.toml
Next, we need a Netlify build configuration file called netlify.toml. Leave the values blank for now.
[build]command = ""functions = ""publish = ""
We can optionally add more options and/or configure additional environments, but these are the least we should have.
These options are also available from the Netlify web UI settings. In case of conflicting values between the configuration file and the Netlify UI settings, the config file wins.
Option 1: No build
This is the most simple setup we could possibly have.
. /
├── functions/
│ └── graphql.js
├── static/
│ └── index.js # can be empty but has to exist
├── netlify.toml
└── package.json
Our package.json file is identical to the basic example above.
Notes:
The only command that we run on build is yarn, ie. install the dependencies.
functions = "functions" means our function code exists in a directory called functions. You can use any name, eg. functions = "my-functions", just make sure the function code exists there.
publish = "static" means our static build files are in a directory called static. We only use the serverless functions here and we are not serving any web page, but this directory is required. Add an empty index.js file there. Again, you can replace this with any directory name.
We simply serve our code; it is not compiled. So we have to use CommonJS syntax like regular Node.js apps.
Pros:
Simple and straightforward. Ideal for simple servers.
Faster build time. The free plan gives us 300 build minutes per month, so faster buid time means saving money.
Cons:
Limited capability, eg. can't use ES6 imports or anything that requires compiling/transpiling.
EDIT: Netlify recently announced an upcoming new bundler that—among other features—supports using ES modules syntax. If you just want to use import, optional chaining, etc, you most likely won't need netlify-lambda build. It is opt-in now and will be launched as public default next May. Unfortunately I don't have time to play with it now... let me know if you have tried it! 😁
Option 2: With build
First, rename the directory containing our server file (graphql.js) from functions to src.
We add two commands, serve (optional; you may use Netlify Dev instead) and build. In both commands, we tell netlify-lambda to build from the src directory—where our function code is.
netlify-lambda knows where the source files are, but how does it know where to build the code a.k.a. the destination folder? It reads our netlify.toml file! 🎩
Finally, modify our server code to use ES6 import instead of the Node/CommonJS require.
Your tsconfig file does not have to look like this. Just make sure the outDir value matches the functions value in our netlify.toml.
Rename our server file from graphql.js to graphql.ts.
This part is optional: I add a resolver function with a custom function using TS syntax. Our hello query now takes an optional argument name, which the server will SHOUT!!!! back to us.
import { ApolloServer, gql } from "apollo-server-lambda";
+ const shout = (msg: string) => {
+ return `${msg.toUpperCase()}!!!!`
+ }
const typeDefs = gql`
type Query {
+ hello(name: String): String
}
`;
const resolvers = {
Query: {
+ hello: (_parent, args) => shout(`Hello ${args.name || 'serverless server'}`),
},
};
// ... no change to the rest of code
Pros:
Same as Option 2, plus TypeScript
Cons:
Same as Option 2
Connecting Git repo to Netlify
Sign up for a free Netlify account if you haven't got one. Push your code to a Git repository (Github, Gitlab, or Bitbucket).
Go to Netlify Dashboard on https://app.netlify.com and choose "New site from Git". Continuous Deployment is enabled by default, so Netlify deploys your site every time you push to your repo.
Once deployed, you can check the build process in the Deploys section on https://app.netlify.com/sites/YOUR-SITE-NAME/deploys. If your build fails (😿), the logs are available there.
When the build completes successfully, your serverless server function will appear in the Functions section under the name graphql.
Click to view its details and endpoint URL. If we enabled playground config in our server code above, we can interact with our server directly by accessing GraphQL IDE from the endpoint URL.
You can do various customizations (turn off automatic deploys, customize repo branch and base directory, and more) from Site settings > Build & deploy on https://app.netlify.com/sites/YOUR-SITE-NAME/settings/deploys.
Conclusion
Serverless cloud functions services like Netlify Functions enable us to get an API server live and running with no charge (to begin with), with little infrastructure and devOps knowledge.
You can find all the code above in this repo. Fork it and build something fun!