# Environment variables and secrets

> Which .env files Firenook reads for your functions and in what order, how secrets and params get their values, and the variables the emulator sets.

Your functions read their settings from `.env` files, secrets from
`.secret.local`, and the emulators' addresses from variables Firenook sets.
This page lists what Firenook reads, in which order, and what your code
sees.

## The .env files

Put the files in your codebase's folder, next to its `package.json`:

```ini
SHOP_NAME=Demo Shop
SUPPORT_EMAIL=help@example.com
```

Firenook reads these files, as the official CLI does. A later file wins
over an earlier one for the same key:

| File | What it is for |
| --- | --- |
| `.env` | Values for every project |
| `.env.<project id>`, such as `.env.demo-shop` | Values for one project, by its id |
| `.env.<alias>`, such as `.env.staging` | Values for one project, by its `.firebaserc` alias |
| `.env.local` | Values for the emulators only, never deployed |

The start says which files it loaded:

```text
firenook functions: Loaded environment variables from .env, .env.demo-shop, .env.local.
```

When `firebase.json` gives the codebase a `configDir`, the `.env` files are
read from that folder instead.

### The project id or its alias

Which of `.env.<project id>` and `.env.<alias>` is read depends on how the
project was chosen. With this `.firebaserc`:

```json
{
  "projects": {
    "default": "demo-shop",
    "staging": "demo-shop-staging"
  }
}
```

| You start with | Files read |
| --- | --- |
| `--project staging`, an alias | `.env`, `.env.staging`, `.env.local` |
| No `--project`, after `firebase use staging` in this folder | `.env`, `.env.staging`, `.env.local` |
| `--project demo-shop`, a project id | `.env`, `.env.demo-shop`, `.env.local` |
| No `--project`, so the `default` alias | `.env`, `.env.demo-shop` or `.env.default`, `.env.local` |

A project named by its id has no alias. Having both files for one project,
its id's and its alias's, stops the codebase loading, as on the official
emulator. Here with `.env.demo-shop-staging` and `.env.staging` and
`--project staging`:

```text
firenook functions: Failed to load function definition from source: FirebaseError: Can't have both dotenv files with projectId (env.demo-shop-staging) and projectAlias (.env.staging) as extensions.
```

Keep one of the two. Without `--project`, `.env.<project id>` and
`.env.default` count as such a pair.

The files use the same dialect as the official CLI: `KEY=value` lines, an
optional `export` in front, `#` comments, double quotes with `\n` escapes
that can span lines, and single quotes kept as written. A key starts with an
uppercase letter or an underscore and holds only uppercase letters, digits
and underscores. Keys that start with `X_GOOGLE_`, `FIREBASE_` or `EXT_`, and
names the runtime uses, such as `PORT`, `K_SERVICE` and `GCLOUD_PROJECT`, are
refused, and the codebase does not load:

```text
firenook functions: Failed to load function definition from source: FirebaseError: Failed to load environment variables from .env.
Validation failed
Failed to validate key FIREBASE_REGION: Key FIREBASE_REGION starts with a reserved prefix (X_GOOGLE_ FIREBASE_ EXT_)
```

## When you change a file

Firenook watches the `.env` files and `.secret.local`, but a change does not
reload your functions, as in the official CLI. Your functions keep the old
values until the next reload. Press `r` in the terminal to reload, or use
**Reload** in the console. Until you do, the codebase's page in the console
names the file that changed after the last load.

## Secrets

Declare a secret with `defineSecret` and bind it to the functions that use
it:

```js
const {onRequest} = require("firebase-functions/https");
const {defineSecret} = require("firebase-functions/params");

const stripeKey = defineSecret("STRIPE_KEY");

exports.checkout = onRequest({secrets: [stripeKey]}, (request, response) => {
  // stripeKey.value() is the value from .secret.local
});
```

Give it a value in `.secret.local`, in the codebase's source folder (never
in `configDir`):

```ini
STRIPE_KEY=sk_test_123
```

Firenook never contacts Secret Manager. The official emulator tries your
production secrets first when `.secret.local` lacks one; Firenook leaves a
missing secret empty, and the SDK logs a warning when your code reads it:

```text
No value found for secret parameter "STRIPE_KEY". A function can only access a secret if you include the secret in the function's dependency array.
```

`firenook init` writes a `functions/.gitignore` with `*.local`, which keeps
`.env.local` and `.secret.local` out of Git.

## Params

Params declared with `defineString`, `defineInt`, `defineBoolean`,
`defineList` and the like take their values from the same `.env` files. Call
`.value()` in your function to read one:

```js
const {onRequest} = require("firebase-functions/https");
const {defineString, defineInt} = require("firebase-functions/params");

const greeting = defineString("GREETING", {default: "hello"});
const pageSize = defineInt("PAGE_SIZE", {default: 20});

exports.settings = onRequest((req, res) => {
  res.json({shop: process.env.SHOP_NAME, greeting: greeting.value(), pageSize: pageSize.value()});
});
```

Firenook never asks for a value. It resolves params as the official emulator
does when no terminal is attached:

- A value in an `.env` file wins.
- Otherwise the param takes its `default`, in `.value()` and in a function's
  options, such as `region`, `memory` or `timeoutSeconds`. The start names
  the params that took theirs:

  ```text
  firenook functions: Params no env file sets take their default: GREETING, PAGE_SIZE.
  ```

  and the function answers with them:

  ```console
  $ curl http://127.0.0.1:5001/demo-shop/us-central1/settings
  {"shop":"Demo Shop","greeting":"hello","pageSize":20}
  ```

- A param with no value and no default stops its codebase loading, with the
  official question and where to put the answer. Here
  `defineString("API_URL")` has neither:

  ```text
  firenook functions: Failed to load function definition from source: FirebaseError: Question "Enter a string value for API_URL:" does not have a default and cannot be answered in non-interactive mode. Firenook never prompts: set the value in .env, .env.demo-shop or .env.local, or give the param a default.
  ```

  When it is the only codebase, the start stops there.

Two differences are deliberate. The official emulator writes the answers it
took, defaults included, to `.env.local`; Firenook writes nothing and takes
the default again at every load, so a changed default applies on the next
reload. And a list param reaches your code as the JSON array the SDK's
`.value()` parses, as a deployed function gets it.

The console's Environment tab, on a function's page, lists every param with
the file its value came from, marks the ones that took their default, and
can write a value to `.env.local` for you. It also lists the secrets a
function declares and can add a missing one to `.secret.local`.

Code that reads `process.env` while it loads, outside a function, sees the
`.env` values too, as on the official emulator: a `region` taken from
`process.env.SHOP_REGION` is the same at load and in the function. Defaults
and secrets are not there yet; the function has them.

`PROJECT_ID`, `GCLOUD_PROJECT`, `STORAGE_BUCKET` and `DATABASE_URL` are built
in: options that use them get the project's values.

## What your functions see

Firenook starts one Node worker per codebase. Its environment is yours, with
your `.env` values over it and the emulator's own variables over both.
`FIREBASE_TOKEN` is removed, and `GOOGLE_APPLICATION_CREDENTIALS` and
`CLOUDSDK_CONFIG` point at demo credentials in the run's folder, so your code
never holds your Google credentials, and a token it asks for comes from the
emulators. A `GOOGLE_APPLICATION_CREDENTIALS` your own `.env` sets is kept,
and requests made with it go to Google.

These are the variables Firenook sets, with the values a project on the
default ports gets:

| Variable | Value |
| --- | --- |
| `FUNCTIONS_EMULATOR` | `true` |
| `FIRESTORE_EMULATOR_HOST` | `127.0.0.1:8080` |
| `FIREBASE_AUTH_EMULATOR_HOST` | `127.0.0.1:9099` |
| `FIREBASE_STORAGE_EMULATOR_HOST` | `127.0.0.1:9199` |
| `STORAGE_EMULATOR_HOST` | `http://127.0.0.1:9199` |
| `PUBSUB_EMULATOR_HOST` | `127.0.0.1:8085` |
| `CLOUD_TASKS_EMULATOR_HOST` | `127.0.0.1:9499` |
| `CLOUD_EVENTARC_EMULATOR_HOST` | `http://127.0.0.1:9299` |
| `FIREBASE_EMULATOR_HUB` | `127.0.0.1:4400` |
| `GCLOUD_PROJECT` | your project id, such as `demo-shop` |
| `FIREBASE_CONFIG` | `{"storageBucket":"demo-shop.appspot.com","databaseURL":"https://demo-shop.firebaseio.com","projectId":"demo-shop"}` |
| `TZ` | `UTC` |
| `K_SERVICE`, `FUNCTION_TARGET` | the name of the function being called |

The Admin SDK reads the `*_EMULATOR_HOST` variables, so
`initializeApp()` with no arguments talks to the emulators. The ports follow
your `firebase.json`.

## Next

Change a value and see it in a call: [debugging and reload on
save](https://firenook.dev/docs/functions/debugging) covers reloads, the debugger and the logs.
