Developing a Firebase Function Pt 2 - Environment Variables and HTTP Methods
Tags: firebase, googlecloud, node
Published: 2021-07-12 00:46:25

In my [last article](https://dev.to/irby/developing-a-firebase-function-1dgd), I demonstrated how you can initialize a Firebase Functions project and how to deploy. In this lesson, I will explain how you can configure environment variables for your API and how to perform other HTTP methods such as PUT and POST.

# Environment Variables

It is generally good practice to make use of environment variables when developing an API, especially when you want to source control your API code but not expose sensitive information such as database credentials. Fortunately, Firebase Functions allow for an easy way to get and set environment variables for your function.

To set an [environment variable](https://firebase.google.com/docs/functions/config-env), run the following code from your command line:

```
$ firebase functions:config:set [scope].[value]="something"
```

For example, if you wanted to assign a value of `hello` to a variable called `myvariable` in the scope of `test`, run the following code:

```
$ firebase functions:config:set test.myvariable="hello"
```

To fetch the environment variables, run the following code

```
$ firebase functions:config:get
```

This will return a JSON object containing the environment variables for the project

```json
{
  "test": {
    "myvariable": "hello"
  }
}
```

One thing to note is that setting the environment variable from the command line can introduce some quirks. For instance, if we try to set the variable to "hello, world!" we get a weird error:

```
$ firebase functions:config:set test.myvariable="hello, world!"
bash: !": event not found
``` 

The reason for this error is that an exclamation mark character is [a special character to bash](https://serverfault.com/questions/208265/what-is-bash-event-not-found), so to get around this error we can substitute double quotes for single quotes

```
$ firebase functions:config:set test.myvariable='hello, world!'
```

Now let's use our environment variables within our code. Let's review our code we wrote in the previous lesson.

```javascript
const functions = require('firebase-functions');

// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions

exports.helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});

```

Within our response, let's return the `myvariable` environment variable we've setup. To retrieve all the environment variables in our project, we can write `functions.config()` within our code, and then we can access the environment variable by specifying the path to the variable. Since `myvariable` is part of the `test` scope, our code should look like this

```javascript
const functions = require('firebase-functions');

// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions

exports.helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send(functions.config().test.myvariable);
});

```

Let's run the emulator and see if we can get this value when we view our function from the browser.

```
$ firebase emulators:start
```

However, when visiting the API endpoint for helloworld, the following error appears:

![alt text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jj46e511ptgn3zhyr1ws.png)
 

The reason for this error is that, although the environment variables are defined, the emulator is unable to retrieve the environment variables. In order for the emulator to retrieve the environment variables, a file containing a copy of the environment variables will need to be created. To do so, run the following script:

```
$ firebase functions:config:get > .runtimeconfig.json
```

This script will write the environment variables to a file called `.runtimeconfig.json`. This is a file that the emulator is able to read from. Let's restart the emulator and see if we can read our environment variable now:

![alt text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98uw1nj38yoaz7uxkwb0.png)

Note that each time you add/update your environment variables, you will need to run the `firebase functions:config:get > .runtimeconfig.json` script. In addition, once the emulator is started it caches the values within the runtimeconfig file, so you'll need to restart the emulator to have the new environment variables picked up.



**NOTE: Because the .runtimeconfig.json file contains environment variables (and potentially sensitive information), we will want to make sure we ignore this file from version control, i.e. adding it to .gitignore**




This should cover the basics on getting environment variables configured and retrieved by the API. For some more information about environment variables, please consult [Firebase's documentation](https://firebase.google.com/docs/functions/config-env). For now, let's move onto how the API can support more HTTP methods besides just GET.


# More HTTP Methods

Currently, our application only supports a GET operation. How about support for more HTTP methods such as POST or PUT? Fortunately, Firebase Functions does allow support for this. To do so, you can use Express routers to specify the operations and have the `functions.https.onRequest()` method accept the Express router configuration.

Here is some sample code to illustrate this, borrowed from  [this StackOverflow post](https://stackoverflow.com/questions/42995433/how-firebase-cloud-functions-handle-http-post-method)  

```javascript
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const app = express();

// Automatically allow cross-origin requests
app.use(cors({origin: true}));

// Defines my GET method at the helloWorld endpoint
app.get('/', (request, response) => {
  response.end('Here is my GET request!');
});

// Defines my POST method at the helloWorld endpoint and prints out an environment variable
app.post('/', (request, response) => {
  response.end('Here is my POST request! ' + functions.config().test.myvariable);
});

// Defines my PUT method at the helloWorld endpoint
app.put('/', (request, response) => {
  response.end('Here is my PUT request!');
});

// Expose Express API as a single Cloud Function
exports.helloWorld = functions.https.onRequest(app);

```

With the emulators running, when we visit the API endpoint for helloWorld in the brower, we see the 'Here is my GET request!' message.

![alt text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/azyd1janbxnjgws2deun.png)

To test the POST endpoint, open up a command line tool and use CURL:

```
$ curl -X POST http://localhost:5001/irbytestproject/us-central1/helloWorld
Here is my POST request! hello, world!
```

Similarly, to test the PUT endpoint:

```
$ curl -X PUT http://localhost:5001/irbytestproject/us-central1/helloWorld
Here is my PUT request!
```


# Deploying our API

Now that we have our environment variables configured and multiple HTTP methods supported, we are ready to deploy our function to Firebase. As a reminder from the first part of this lesson, the command to deploy our Firebase function:

```
$ firebase deploy
```

Once the deployment has completed, we can use CURL to test our POST endpoint to ensure it gets the environment variable correctly:

```
$ curl -X POST https://us-central1-irbytestproject.cloudfunctions.net/helloWorld 
Here is my POST request! hello, world!
```

Excellent! Our API seems to be working as expected. 

# Conclusion 

For now, this should wrap up the basics on developing a Firebase function. If you are unfamiliar with Node.js or Express, I recommend [checking out this course by Maximilian Schwarzmüller](https://www.udemy.com/course/angular-2-and-nodejs-the-practical-guide/) which goes over connecting a Node application to a MongoDB database, setting up API controllers, and more.

Hope you've enjoyed the article!