Getting started with Netlify Functions — Zero-config setup and our first functions
Tags: serverless, jamstack, beginners
Published: 2020-07-25 16:55:44

**Serverless functions** are server-side functions that are hosted on a third-party service (in this case Netlify). In a nutshell, 

- We write our server-side functions
- We walk up to the third-party service with the functions and say, *“Oh hey, could you please run these for me on your server?”* 
- They say *“Sure!”*
- We can now call the functions to do whatever they are supposed to do and return whatever we need for our web application.

**Why?** With serverless functions, we get the benefits of a server without having our own server. Our “server-side” functions can even live in the same codebase as our website code. How convenient! 

**Should I use these?** In my opinion, this is ideal for small to mid-level projects. If your app has large traffic or other specific/unusual requirements, make sure to compare the costs and implications thoroughly before going serverless. 

**A misnomer?** Much like wireless speakers are _not without wires_, these functions definitely run on servers; we just don’t need to set up, configure, host, and manage them ourselves. 😁

<img alt="Finn from the cartoon Adventure Time doing a thumbs-up with the text Niiiiice" src="https://media.giphy.com/media/qPcX2mzk3NmjC/giphy.gif" />

In this series, we’re going to learn how Netlify functions work by building a web application with a REST API from scratch and deploy it. It’s inspired by Jason Lengstorf’s [“Introduction to Serverless Functions” workshop](https://frontendmasters.com/workshops/serverless-functions/), which was my first encounter with serverless functions.

This first post focuses on getting our local dev environment up and writing our first functions. Before we proceed, please note this post presumes the following.

- Basic knowledge in:
  - JavaScript and HTML
  - How a web application works (server, client/browser)
  - RESTful API
  - Git version control
- A (free) account on:
	- [Netlify](https://www.netlify.com/)
	- [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org/product)
- Latest node and npm (run `node -v && npm -v` on your CLI/Terminal to check)

---

**Table of Contents**

1. Installation
2. Start our development environment
3. Create our server(less) functions
  * 3a. Define our functions directory
  * 3b. Add function files to our functions directory
  * 3c. Write our first function
  * 3d. Write a function with query string parameters
  * 3e. Write a function that accepts a data body (payload)
  * 3f. Use secret environment variables in our function

---

## 1. Installation

### 1a. Install the global Netlify CLI package

Run on your command line: `npm install netlify-cli -g`

### 1b. Prepare your web app

Clone or initialize your project. You can clone this sample repo for a quick start.

```
git clone -b part-1 https://github.com/ekafyi/hello-serverless-netlify-functions.git
```

Netlify functions are written in standard (vanilla) JS and work with any modern JS libraries. The sample repo uses the [default Eleventy installation](https://www.11ty.dev/docs/getting-started/). We're not going to write any web code yet, but feel free to use a starter from your favourite library instead.

Don’t forget to `cd` to the project directory and install the project’s dependencies by running `yarn` or `npm install`.

## 2. Start our development environment

Run `netlify dev` (or `ntl dev` for short) to start our local development environment. It’s going to serve our web app (in my case Eleventy), our web server, and a Browsersync interface.

![ntl dev serving 3 URLs in the CLI](https://dev-to-uploads.s3.amazonaws.com/i/59a1dvlx7y8rs4otzdsy.png)

- Our web app runs on port `8080`
	- This our web app’s dev server that we usually start with `npm run start` or `npm run watch` (or the `yarn` equivalent).
- Browsersync UI runs on port `3001`
- Our web server runs on port `8888`

If you cloned the sample repo, you should see this page on both ports `8080` and `8888`.

![web app home page](https://dev-to-uploads.s3.amazonaws.com/i/7c7kw23aobtkwpxpj5oo.png)

### What kind of sorcery is this? 🧙🏼‍♀️✨ 

[Netlify Dev](https://www.netlify.com/products/dev/) is an integrated local development environment that automatically:

> * Detects and runs your site generator
> * Makes environment variables available
> * Performs edge logic and routing rules
> * Compiles and runs cloud functions

It has a “project detector” that checks our project `package.json` and configures the dev setup automatically. In most cases, it “just works”—no need to do anything. 

However, if you use a less popular library and/or have a custom setup, you can declare your setup by creating a `netlify.toml` file in your project root that contains your dev setup.

```toml
# netlify.toml dev block example
[dev]
  command="yarn dev"
  port=5000
```

**Learn more:**
- [Zero Config, yet Technology Agnostic: How Netlify Dev Detectors Work](https://www.netlify.com/blog/2019/04/24/zero-config-yet-technology-agnostic-how-netlify-dev-detectors-work/)
- List of built-in detectors https://github.com/netlify/cli/tree/master/src/detectors
- Netlify Dev documentation https://github.com/netlify/cli/blob/master/docs/netlify-dev.md

**Note:** If you run extra servers for custom processes (eg. to watch CSS with Sass or PostCSS), you still need to run them separately in addition to `ntl dev`, OR modify your `package.json` so they are included in the default dev command.

Now we’ve got our web app running, let’s write our first functions!

## 3. Create our server(less/-side) functions

📝 **Note:** [Netlify Docs](https://docs.netlify.com/functions/overview/) has in-depth explanation on the functions discussed here.

### 3a. Define our functions directory

I create a new directory called `functions` in my project root. (You can use any name; make sure to specify the right name below.)

Create a file called `netlify.toml` in your project root (if you don’t have it already) and add `functions=""` inside the `[dev]` block, where the value is the name of our serverless functions directory (in my case, `functions`).

```toml
# netlify.toml
[dev]
  command="npm run build"
  publish="_site"
  functions="functions"
```

### 3b. Add function files to our functions directory

Next, create a JS file in our functions directory that exports a `handler` method. (I use the ES6 syntax here, hence `() => { … }` instead of `function() { … }`. Feel free to use whichever you’re most comfortable with.)

```js
// functions/hello.js
exports.handler = () => {
  // ... later
}
```

The filename without extension represents a serverless function endpoint in this format: `{SERVER_URL}/.netlify/functions/{FUNCTION_NAME}`.

So, if we have `functions/hello.js`, we send our request to `http://localhost:8888/.netlify/functions/hello`. 

We can have as many files as we want.

### 3c. Write our first function

Our function should return a JSON response. This is the most simple function possible. 

```js
// functions/hello.js
exports.handler = async () => {
  return {
    statusCode: 200,
    body: "Hello there!",
  };
};

// GET localhost:8888/.netlify/functions/hello
// → Hello there!
```

Notes:
- `async` means this is an asynchronous function, which I can call from my web app code with a _promise_. If you’re not familiar with these, read [“Introducing asynchronous JavaScript“ on MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing).
- List of [HTTP response status codes](https://httpstatuses.com/) (maybe don’t return [418](https://httpstatuses.com/418)).

### 3d. Write a function with query string parameters

The handler method has the `event` and `context` parameters, and an optional `callback` parameter. The `event` object contains information about the request. It looks like this.

```json
{
  "path": "/.netlify/functions/echo",
  "httpMethod": "GET",
  "headers": { ... },
  "queryStringParameters": {},
  "body": undefined,
  "isBase64Encoded": false
}
```

- `path` = path parameter
- `httpMethod` = request’s method (eg. `GET`, `POST`, `PUT`)
	- As per the suggested [RESTful API naming practices](https://restfulapi.net/resource-naming/), GET `/.netlify/functions/users` should get the users data and POST `/.netlify/functions/users` should create a new user. We can do this in serverless functions by getting the `httpMethod` value.
- `headers` = request headers
	- This contains HTTP Headers data such as `Accept-Language`, `User-Agent`, etc.
- `queryStringParameters`  = self-explanatory (more on this below)
- `body` = request payload
- `isBase64Encoded` = a boolean flag to indicate if the applicable request payload is Base64-encode

Now let’s modify our `hello` function to return a greeting based on the `name` parameter in the request.

```js
// functions/hello.js
exports.handler = async (event) => {
  const { name } = event.queryStringParameters;
  return {
    statusCode: 200,
    body: `Hello ${name || 'stranger'}!`,
  };
};

// GET localhost:8888/.netlify/functions/hello
// → Hello stranger!

// GET localhost:8888/.netlify/functions/hello?name=Eka
// → Hello Eka!
```

- We use the `event` parameter and get the `name` value from `event.queryStringParameters`.
	- Note: `const { name } = event.queryStringParameters` is shorthand for `const name = event.queryStringParameters.name`.
- If we have multiple query string parameters in the request, we can access them all as objects in `queryStringParameters`.
	- eg. GET `localhost:8888/.netlify/functions/hello?name=Eka&timeOfDay=morning`, we can get the `name` and `timeOfDay` values from `event.queryStringParameters`.
 
We are not discussing the `context` and `callback` parameters in this post, but you can read about them in the links below.

**Learn more:**
- [Build serverless functions with JavaScript](https://docs.netlify.com/functions/build-with-javascript) — Netlify Documentation
- [HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) — MDN Docs

### 3e. Write a function that accepts a data body (payload)

POST requests are basically similar to GET requests, except they may contain data—eg. user-inputted form data—in the request body. We can access it in `event.body` and do something with it, such as send it to some external API or database.

```js
// functions/newsletter.js
exports.handler = async (event) => {
  const { body } = event; // Request body data

  // Basic example of sending the data to an external API
  fetch(`https://someserver.com/v1/some/endpoint`, {
    method: "POST",
    headers: {
      "Content-type": "application/json",
      // Add credentials as required by the external service
    },
    body: body, // Send the data
  })
    .then((response) => {
      // Do stuff and returns 200 response...
    })
    .catch((error) => {
      // Do stuff and returns 4xx / 5xx response...
    });
};

// POST localhost:8888/.netlify/functions/newsletter
// → 200 OK
```

Remember that these functions _are_ regular JavaScript code. `body` data are sent in JSON-safe string format; request `body` must be [parsed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) if we want to access it as JS objects, and contrariwise JS objects must be [stringified](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) to send in response `body`. 

### 3f. Use secret environment variables in our function

We briefly discussed sending our data to an external service in the example above. Today, most web apps retrieve and/or send data to and from external APIs, usually with some kind of _secret_ (token or key) in our request header to validate our app.

We _should not_ make this request from the client side (the browser), because everyone can open the browser dev tools and find your credentials 🙀, which then can be used for (eg.) deleting all your data or posting egregious content on your behalf.

With serverless functions, we can have “server-side” code to handle this operation while keeping our credentials private. 

Create `.env` in your project root (if it does not exist yet) and add your secret there.

```env
SPOTIFY_CLIENT_ID=xxxxxxxxx
SPOTIFY_CLIENT_SECRET=xxxxxxxxx
```

The env data is available in our functions in `process.env` like so.

```js
// functions/spotify.js
exports.handler = async (event) => {
  // Example of Spotify API "Client Credentials" authorization
  const getToken = async () => {
    const result = await fetch("https://accounts.spotify.com/api/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        Authorization: "Basic " + btoa(process.env.SPOTIFY_CLIENT_ID + ":" + process.env.SPOTIFY_CLIENT_SECRET),
      },
      body: "grant_type=client_credentials",
    });
    const data = await result.json();
    return data;
  };

  getToken()
    .then((response) => {
      // Do stuff ...
    })
    .catch((error) => {
      // Do stuff ...
    });
};
```

Make sure `.env` is in your `.gitignore`  so it does not get pushed to the repository.

## That’s all for now…

You can see the sample repo below and the [demo site here](https://hello-serverless-netlify-functions-one.netlify.app/).

{% github ekafyi/hello-serverless-netlify-functions %}

We have set up our project’s local development environment, written our first serverless functions, but you may notice we haven’t done anything with our web app. We’re going to do it in the next post, so stay tuned!

---

## Bonus Track

With serverless functions, you can send secrets in your requests securely. Here is my favourite (but unfortunately unreleased) version of “Secrets” by Strawberry Switchblade. 

{% youtube knqqmGtmbhA %}