# One CLI to Rule Them All, One Command to Configure

## The setup tax

Every Firebase + React Native project starts the same way. You open the Firebase Console. You create an Android app with your package name. You create an iOS app with your bundle ID. You download `google-services.json`. You download `GoogleService-Info.plist`. You open `app.json` and add the `googleServicesFile` paths. You go back to your terminal, extract the API keys, write a `.env.dev` file. You add the output directory and `.env.*` to `.gitignore`.

Then your team lead says "we need a staging environment."

You do it again.

Website: [stack.cardor.dev/rfc](https://stack.cardor.dev/rfc)

Then QA finds a bug that only reproduces in prod, and someone — not you, obviously — had `google-services.json` from dev pointing to the prod Firebase project because filenames were identical and got overwritten during the last setup. Nobody knew. The workflow "worked."

This is the setup tax. It's not technically hard. It's just repetitive, error-prone, and completely uninteresting to solve manually.

## What the CLI does

`@cardor/rn-firebase-cli` is a Node.js CLI that wraps the entire Firebase setup flow for React Native projects. One command. Interactive wizard. Everything wired automatically.

```bash
npm install -g @cardor/rn-firebase-cli
cd my-react-native-app
rn-firebase init
```

The wizard:

1.  Checks that `firebase-tools` is installed and you're authenticated
    
2.  Detects your project type (Expo or Bare RN) by scanning `app.json`, `app.config.*`, `android/`, `ios/`
    
3.  Reads your bundle IDs from `app.json` — or prompts for them if they're not found
    
4.  Lists your Firebase projects and lets you pick one
    
5.  Verifies matching Android/iOS apps exist **by package name and bundle ID**
    
6.  If no matching app exists: prompts you to create it right there via `firebase apps:create` — no Console tab-switching required
    
7.  Downloads `google-services.json` and `GoogleService-Info.plist`
    
8.  Extracts the OAuth web client ID automatically
    
9.  Writes `config/firebase.config.ts` (or `.mjs`/`.cjs` depending on your project)
    
10.  Writes `.env.{envName}` with all Firebase vars, prefixed correctly for your project type (`EXPO_PUBLIC_` for Expo, no prefix for Bare RN with `react-native-config`)
     
11.  Updates `app.json` with `googleServicesFile` paths
     
12.  Updates `.gitignore`
     

## The multi-environment problem — solved properly

The naive approach to multiple Firebase environments is to keep overwriting the same `google-services.json` file. That's how you end up with prod pointing at dev. The CLI solves this at the filesystem level with prefixed filenames:

```plaintext
keys/
  dev-com.myapp-google-services.json
  staging-com.myapp-google-services.json
  prod-com.myapp-google-services.json
  dev-com.myapp-GoogleService-Info.plist
  prod-com.myapp-GoogleService-Info.plist
```

The naming pattern is `{env}-{packageName|bundleId}-{original filename}`. It's deterministic, readable, and collision-proof. `app.json` gets updated with the correct prefixed path on each run.

### The `app.json` problem

`app.json` is static JSON. It can only hold one value for `googleServicesFile`. That's fine for one environment. For multiple, you need `app.config.ts`.

When the CLI detects a second environment being added (it checks whether `app.json` already has a `googleServicesFile`), it offers:

```plaintext
Multiple envs detected. Generate app.config.ts for dynamic Firebase paths (APP_ENV)?
```

If you accept, it generates a typed `app.config.ts`:

```typescript
// Auto-generated by @cardor/rn-firebase-cli
import type { ExpoConfig } from 'expo/config'
import appJsonData from './app.json'

const env = (process.env.APP_ENV ?? 'dev') as string

const firebaseFiles: Record<string, { android?: string; ios?: string }> = {
  dev: {
    android: './keys/dev-com.myapp-google-services.json',
    ios: './keys/dev-com.myapp-GoogleService-Info.plist',
  },
  prod: {
    android: './keys/prod-com.myapp-google-services.json',
    ios: './keys/prod-com.myapp-GoogleService-Info.plist',
  },
}

const config: ExpoConfig = {
  ...appJsonData.expo,
  android: {
    ...(appJsonData.expo?.android as object | undefined),
    googleServicesFile: firebaseFiles[env]?.android,
  },
  ios: {
    ...(appJsonData.expo?.ios as object | undefined),
    googleServicesFile: firebaseFiles[env]?.ios,
  },
}

export default config
```

It also cleans `googleServicesFile` out of `app.json` — that field is now owned by `app.config.ts` and the dead entry in `app.json` would only cause confusion.

If you already have your own `app.config.ts`, the CLI skips generation and prints a contextual two-step snippet showing exactly what to add and where.

## Expo SDK 56 + `src/` detection

Expo SDK 55+ generates a `src/` directory by default. The CLI detects its presence and places `firebase.config.ts` inside `src/config/` rather than root `config/`. No config needed — it's automatic.

## Creating missing Firebase apps inline

Previously, if you ran the CLI against a Firebase project that didn't have an Android app with your package name, it would error and tell you to go create it in the Console. Now it asks:

```plaintext
No Android app found with package name "com.myapp" in project "my-project-dev".
Create Android app now? (Y/n)
```

If you confirm, it calls `firebase apps:create ANDROID` and continues the flow. No Console tab required.

## Using the configured files

After `init`, your app gets:

*   `keys/{env}-{id}-google-services.json` — native Firebase config (gitignored)
    
*   `keys/{env}-{id}-GoogleService-Info.plist` — native Firebase config (gitignored)
    
*   `config/firebase.config.ts` — runtime config with web client ID env ref
    
*   `rn-firebase.config.ts` — CLI config for future `update` runs
    
*   `.env.{envName}` — all Firebase vars (gitignored)
    

In your Expo app:

```typescript
const apiKey = process.env.EXPO_PUBLIC_FIREBASE_API_KEY
const webClientId = process.env.EXPO_PUBLIC_FIREBASE_WEB_CLIENT_ID
```

For builds that switch environments:

```bash
APP_ENV=prod eas build --platform all
APP_ENV=dev npx expo start
```

## Commands

```bash
rn-firebase init              # interactive setup
rn-firebase init \
  --project my-project \
  --platform both             # non-interactive (CI/CD)
rn-firebase update            # re-download config files
rn-firebase update --env prod # re-download specific env
rn-firebase status            # check what's configured
```

## Current limitations

Expo is fully supported. Bare React Native is detected and the wizard runs, but native file integration (editing `android/build.gradle`, `ios/Podfile`) is coming in v2. For now, Bare RN users get the `.env` file and the wizard flow.

* * *

```bash
npm install -g @cardor/rn-firebase-cli
```

[npmjs.com/package/@cardor/rn-firebase-cli](https://www.npmjs.com/package/@cardor/rn-firebase-cli)

[github.com/enmanuelmag/rn-firebase-cli](https://github.com/enmanuelmag/rn-firebase-cli)
