API key management on Netlify

donnierich

Donato Riccio

Posted on July 8, 2023

API key management on Netlify

This is a tutorial aimed to help junior devs correctly manage their API keys (or other secrets) when publishing a website on the Netlify platform.

Pre-requisites

  • Basic knowledge of HTML, CSS, JavaScript
  • Basic knowledge of Vite, VueJS, and Axios
  • Basic knowledge of VisualStudio Code
  • Just a bit of experience with the CLI
  • A GitHub repository
  • How to deploy a website on Netlify
  • A website/project/web app already deployed on Netlify

What you will learn (table of contents)

How to install and use the Netlify CLI

This is one of the easiest steps of the entire tutorial.
From the Netlify documentation about the CLI (you can find it here) this is all you need to install the Netlify CLI. Yay! :)

npm install netlify-cli -g
Enter fullscreen mode Exit fullscreen mode

Now you can use the netlify command to manage your local website. You can also use the alias ntl to save some time :)

Login to Netlify

Now you need to authorize Netlify CLI by obtaining an access token. Just type the following command and authorize the application:

ntl login
Enter fullscreen mode Exit fullscreen mode

Link your local environment to Netlify

If you have already deployed your website on Netlify, now you just have to link both environments together.
You can do this using this command:

ntl link
Enter fullscreen mode Exit fullscreen mode

Run your build

With the following command, you can run your local build using the Netlify CLI to simulate the behavior of running the build on Netlify:

ntl dev
Enter fullscreen mode Exit fullscreen mode

How to set and use environment variables

If you are not familiar with the concept of .env file and environment variables you can refer to this over-simplified definition just to understand the basics:

An .env file is just a plain text file containing key/value pairs of variables.

env is short for environment, because the values defined here are the variables available for all our environment.

Set environment variables

You can set these variables by just writing them down inside your .env file:

MY_KEY=value
Enter fullscreen mode Exit fullscreen mode

NOTE: always remember to add your .env file to .gitignore or all the variables will be visible inside your repository!

Netlify UI makes the process to write and manage environment variables really easy. Just go to your site Control Panel > Site settings > Environment variables.
Write the key and the value, leaving the variable available for all the scopes (in this tutorial I will not cover the variable scopes).

NOTE: make sure to have the Sensitive variable policy set up on Require approval for untrusted deploys!
More on that here.

If you linked your local development environment to your Netlify site, you already have the environment variables available without the need to write the .env file!

You can verify this by executing the following command:

ntl env:list
Enter fullscreen mode Exit fullscreen mode

Use environment variables

To use the environment variables this is all you need:

const { SECRET_API_KEY } = process.env
Enter fullscreen mode Exit fullscreen mode

process.env gets all the environment variables and with a simple destructuring you can get the keys you want.

That's all for the environment variables!
Don't worry! In the next steps, I'll show you where to place this line of code :)

How to write serverless functions

A serverless function on Netlify is just a JS function that returns a promise. As per the Netlify documentation:
To create a synchronous function, use the following general syntax in your JavaScript function file to export a handler method

exports.handler = async function (event, context) {
  // your server-side functionality
};
Enter fullscreen mode Exit fullscreen mode

You can also use the ES Module syntax:

const handler = async function (event, context) {
  // your server-side functionality
};

export { handler };
Enter fullscreen mode Exit fullscreen mode

NOTE
Netlify provides the event and context parameters when the function is invoked.
More on that here

Where to place serverless functions

The default path used by Netlify for serverless functions is YOUR_BASE_DIRECTORY/netlify/functions.

NOTE
The function’s endpoint name is case-sensitive and determined by its filename or the name of its dedicated parent directory.

I.E. If you want an endpoint with the name hello you should save your function in one of the following ways:

  • netlify/functions/hello.js
  • netlify/functions/hello/hello.js
  • netlify/functions/hello/index.js

Because the endpoint name is case-sensitive if you name your function myFunction, your endpoint will look like this: netlify/functions/myFunction.js.
For the same reason if you name your function my-function, your endpoint will be: netlify/functions/my-function.js.

What to write inside the function

The easiest thing to do is to just make the function return a "Hello World!"
Using the basic example from the Netlify documentation the hello.js function will look like this:

const handler = async function (event, context) {
    // your server-side functionality
    return {
        statusCode: 200,
        body: JSON.stringify({ message: "Hello World" }),
    };
};

export { handler };
Enter fullscreen mode Exit fullscreen mode

This will return an object with a statusCode and a body properties. The statusCode with value 200 means the request was successful.
The body will contain a JSON formatted string representing the object we want to get from the function. In this example is just an object with a message property.

Let's proceed to the next step!

How to use serverless functions to communicate with the APIs

Time to use your function!
After a successful test call, you will add the code needed to call the API endpoint you want.

Call the function

The following code examples are taken from a VueJS project, using axios to call both the serverless functions and the APIs.

To call hello.js from App.vue the code is as simple as this:

axios.get('/.netlify/functions/hello')
    .then((response) => {
        // handle success
        console.log(response);
        this.helloResult = response.data.message;
    })
    .catch((error) => {
        // handle error
        console.log(error);
    });
Enter fullscreen mode Exit fullscreen mode

If you are a bit familiar with VueJS you'll know this code can be placed inside a function defined in the methods property or inside the mounted method.
helloResult is just a property of data and is used to memorize the value returned from the hello.js function.

NOTE
To get the "Hello World!" text you need to access the message property inside response.data.
The message property is the same property you defined in the previous step

Make sure to test your code in the browser (remember to run ntl dev or netlify dev in the terminal to get both the Netlify local environment and your project up and running as you did in the Run your build step)

If everything works on your local machine, is time to push to your repository and test again everything on your live website!

Read environment variables from the serverless function

You just tested the simplest function you could write and everything went smoothly! To make a secure call to the API you want you'll first need to read the API key from your environment variables.

Do you remember this step? This is where you find out where to place that line of code!

Go to the Set environment variables step and create a new variable called SECRET_API_KEY (spoiler: it will not be secret!) with the value you want.

On your local machine run the command to get the list of all environment variables:

ntl env:list
Enter fullscreen mode Exit fullscreen mode

If you can see the SECRET_API_KEY entry, you did everything right!

NOTE
You can also check the variables values by typing y when the terminal ask you: Show values?(y/N)

Now is the time to create a new function called almost-secret.js that'll give you the value of the SECRET_API_KEY env variable.

almost-secret.js code:

const handler = async function (event, context) {
    // your server-side functionality
    const { SECRET_API_KEY } = process.env
    return {
        statusCode: 200,
        body: JSON.stringify({ message: SECRET_API_KEY }),
    };
};

export { handler };
Enter fullscreen mode Exit fullscreen mode

Calling this function will give you the value of your SECRET_API_KEY variable... but, wait... why should you bring on your front-end such information?

You shouldn't! This is just an example to show how to read and use your env variables from a serverless function.
In the next example, you will see how to properly use a serverless function to call an API without exposing your API key.

Before that, let's push everything on the repository and check it on the live site.

Call QuizAPI passing the API key securely

In this last example, you will learn how to put it all together and make secure calls to every API you need.
The API used is from QuizAPI. In their documentation, they suggest attaching the API key to a request as an HTTP header:

X-Api-Key: YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

Register to QuizAPI and get your API key, then add that key to a new env variable on Netlify (es. QUIZ_API_TOKEN).
The last thing to do is to create a new function called quizapi.js. This function will call the questions endpoint (more on that here).

To make an HTTP call from a serverless function you need to import axios (you already installed the dependency in the previous steps).

NOTE
I will not cover how to use axios and how to add headers to a request since it's a pre-requisite for following this tutorial

Let's take a look at the quizapi.js function:

import axios from 'axios';

const handler = async function (event, context) {
    try {

        // your server-side functionality
        const { QUIZ_API_TOKEN } = process.env;

        const config = {
            params: {
                limit: 1
            },
            headers: {
                "X-Api-Key": QUIZ_API_TOKEN
            }
        };

        const response = await axios.get('https://quizapi.io/api/v1/questions', config)

        if (response.errors !== undefined && response.errors.length) {
            throw new Error(response.errors);
        }

        // handle success
        return {
            statusCode: 200,
            body: JSON.stringify({ response: response.data }),
        };

    } catch (error) {
        return {
            statusCode: 500,
            // Could be a custom message or object i.e. JSON.stringify(err)
            body: JSON.stringify({ response: error.message }),
        }
    }
};

export { handler };
Enter fullscreen mode Exit fullscreen mode

It's a little more complex than the other two, is it?
Don't worry, I will explain this function almost line by line.

The first line just imports axios inside the function:

import axios from 'axios';
Enter fullscreen mode Exit fullscreen mode

After the common code for all the Netlify serverless functions, you can spot the line taking care of retrieving the QUIZ_API_TOKEN env variable value:

const { QUIZ_API_TOKEN } = process.env;
Enter fullscreen mode Exit fullscreen mode

After that, there is the code used to add parameters and header to the axios request:

const config = {
    params: {
        limit: 1
    },
    headers: {
        "X-Api-Key": QUIZ_API_TOKEN
    }
};
Enter fullscreen mode Exit fullscreen mode

The limit property is a parameter taken from the QuizAPI documentation
The X-Api-Key property is where to place the value of QUIZ_API_TOKEN as explained by the QuizAPI documentation

The function will now make the call to the API, passing along parameters and headers:

const response = await axios.get('https://quizapi.io/api/v1/questions', config)
Enter fullscreen mode Exit fullscreen mode

If the API responds with an error, the function will throw an exception (this is why everything is wrapped inside a try/catch block):

if (response.errors !== undefined && response.errors.length) {
    throw new Error(response.errors);
}
Enter fullscreen mode Exit fullscreen mode

If there are no errors, the function will return an object already familiar to you:

return {
    statusCode: 200,
    body: JSON.stringify({ response: response.data }),
};
Enter fullscreen mode Exit fullscreen mode

In the response property, we add the value of response.data since the endpoint returns an array of strings that axios put right inside the data property (if you are familiar with axios you already know how to check this).

In case of errors the function will go to the catch block and return an object with a statusCode of 500 (generic server error) and the error message:

catch (error) {
    return {
        statusCode: 500,
        // Could be a custom message or object i.e. JSON.stringify(err)
        body: JSON.stringify({ response: error.message }),
    }
}
Enter fullscreen mode Exit fullscreen mode

To call this function from App.vue (or any other component) the code is almost the same as the one used before:

axios.get('/.netlify/functions/quizapi')
    .then((response) => {
        // handle success
        console.log(response);
        this.quizApiQuestion = response.data.response[0].question;
    })
    .catch((error) => {
        // handle error
        console.log(error);
    });
Enter fullscreen mode Exit fullscreen mode

Test the call on your local machine

If you didn't stop the local environment, go to your browser and check if you can get a question from the QuizAPI endpoint!
After that go further and push everything on your repo to also test the call from the live site.

Test the call on the live site

If you can get a question from QuizAPI from your live website, great!

If not, maybe you are facing this kind of error: 'Error: Cannot find module '/var/task/node_modules/axios/dist/node/axios.cjs'.

To solve this error create a file called netlify.toml and add this code inside it:

[functions]
  node_bundler = "esbuild"
Enter fullscreen mode Exit fullscreen mode

Commit, push, and test again!

Conclusion

When calling external API services from your front-end project always remember to keep hidden and secured your API keys (or other sensitive pieces of information).
One way to do this is to leverage the power of Netlify serverless functions that can act as API endpoints, read environment variables, and make calls to other APIs.

If this article was helpful or want to start a conversation, feel free to reach out in the comments or on LinkedIn
I'll be happy to receive any feedback or ideas for future articles and tutorials.

Resources

💖 💪 🙅 🚩
donnierich
Donato Riccio

Posted on July 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

API key management on Netlify
webdev API key management on Netlify

July 8, 2023