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.
Getting started with Netlify Functions β Zero-config setup and our first functions
Eka γ» Jul 25 '20
Table of Contents
- Basic code and dependencies
- Option 1: No build
- Option 2: With build
- Option 3: With TypeScript
- Connecting Git repo to Netlify
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.
{
"name": "my-server",
"private": true,
"scripts": {},
"dependencies": {
"apollo-server-lambda": "^2.22.1",
"graphql": "^15.5.0"
},
"devDependencies": {
"encoding": "^0.1.13",
"netlify-cli": "^3.13.7"
}
}
- Dependencies:
- apollo-server-lambda
- We use
apollo-server-lambda
instead ofapollo-server
above. apollo-server-lambda is Apollo Server's AWS Lambda integration, and Netlify Functions uses AWS Lambda under the hood.
- We use
- graphql (...but of course)
- apollo-server-lambda
- 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');
const typeDefs = gql`
type Query {
hello: String
}
`;
const server = new ApolloServer({
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
netlify.toml:
[build]
command = "yarn"
functions = "functions"
publish = "static"
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 calledfunctions
. 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 calledstatic
. We only use the serverless functions here and we are not serving any web page, but this directory is required. Add an emptyindex.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
.
. /
βββ src/ # rename from functions to src
β βββ graphql.js
βββ static/
β βββ index.js
βββ netlify.toml
βββ package.json
Then change the command in netlify.toml
:
[build]
- command = "yarn"
+ command = "yarn build"
functions = "functions"
publish = "static"
We don't change the functions directory here, the build/destination directory is still functions
, while our server source code now lives in src
.
We are using netlify-lambda for the build step. Install it and add these commands to package.json
.
npm install --save-dev netlify-lambda
# or
yarn add -D netlify-lambda
{
"scripts": {
+ "start": "netlify-lambda serve src",
+ "build": "netlify-lambda build src"
},
"devDependencies": {
"encoding": "^0.1.13",
"netlify-cli": "^3.13.7",
+ "netlify-lambda": "^2.0.3"
},
// ... no change to rest of file
}
Notes:
- We add two commands,
serve
(optional; you may use Netlify Dev instead) andbuild
. In both commands, we tell netlify-lambda to build from thesrc
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
.
// src/graphql.js
- const { ApolloServer, gql } = require('apollo-server-lambda');
+ import { ApolloServer, gql } from "apollo-server-lambda";
Pros:
- We can have more complex Lambda functions with transpiled modern features, imports, etc.
Cons:
- As a general rule, more dependencies = longer build and higher possibility of errors or incompatibilities. Only use the build step if necessary.
Option 3: With TypeScript
We can use TypeScript with Netlify functions to take advantage of its type-checking and Intellisense features.
First, install the dependencies.
npm install --save-dev @babel/preset-typescript typescript @types/node @types/aws-lambda
Then create a Babel config file and a TypeScript config file in our root directory.
.babelrc
{
"presets": [
"@babel/preset-typescript",
[
"@babel/preset-env",
{
"targets": {
"node": true
}
}
]
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-object-assign",
"@babel/plugin-proposal-object-rest-spread"
]
}
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./functions",
"strict": false,
},
"exclude": [
"node_modules",
"functions"
]
}
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!
Serverless GraphQL Server on Netlify Functions, 3 Ways
Sample repo for my DEV.to post
Does what it says on the tin...
-
no-build
β graphql server function without build step -
with-build
β graphql server function with netlify-lambda build -
with-ts
β graphql server function with netlify-lambda build + TypeScript
Quick Start
If deploying from this repo, make sure you set the base directory to the directory you want to use (eg. no-build
). See: https://docs.netlify.com/configure-builds/get-started/#definitions
Else
Or you can clone this repo and start a Git repo manually/locally from the directory you want to use.
git clone https://github.com/ekafyi/hello-graphql-server.git
cd hello-graphql-server/no-build
npm install
git init
Thank you for reading, and stay tuned for more beginner GraphQL learning attempts. π€π½
References
Top comments (1)
nice going! hit me up if you have any other netlify functions issues, i wrote a lot of this stuff haha