DEV Community

Cover image for I Built a Terminal UI for Firebase Firestore (and It Changed How I Work)
Marjo
Marjo

Posted on

I Built a Terminal UI for Firebase Firestore (and It Changed How I Work)

The Frustration That Started It All

Picture this: You're deep in a debugging session. Your terminal is split between your code editor, logs streaming in real-time, and a test runner. You're in the zone. Then you need to check a document in Firestore.

Alt-tab. Open browser. Navigate to Firebase Console. Wait for it to load. Click on your project. Wait again. Click Firestore. Expand the collection. Scroll. Find the document. Click it. Finally see the data.

By the time you get back to your terminal, you've lost your train of thought. The context switch killed your flow.

If you've worked with Firebase Firestore, you know exactly what I'm talking about. The Firebase Console is fine for occasional use, but when you're actively developing and need to check data constantly, it becomes a bottleneck.

I kept thinking: Why can't I just browse Firestore from my terminal?

So I built it.

Introducing LazyFire

LazyFire is a terminal user interface (TUI) for browsing Firebase Firestore. It's heavily inspired by lazygit - the fantastic terminal UI for git that changed how many of us interact with version control.

LazyFire Preview

The core philosophy is simple: stay in your terminal, stay in your flow.

LazyFire connects to your Firestore database using your existing Firebase CLI credentials. No separate authentication, no API keys to manage - if you can run firebase commands, you can run LazyFire.

The Interface

When you launch LazyFire, you're greeted with a clean, multi-panel interface:

┌─ Projects ────┬─ Collections ─┬─ Tree ─────────────┬─ Details ──────────────┐
│               │               │                    │                        │
│ 🔥 my-app     │ 📁 users      │ ▼ 📁 users         │ {                      │
│   my-app-stg  │ 📁 products   │     📄 user_001    │   "name": "John Doe",  │
│   my-app-dev  │ 📁 orders     │     📄 user_002    │   "email": "john@...", │
│               │ 📁 analytics  │   ▶ 📁 products    │   "created": "2024..." │
│               │               │                    │ }                      │
├───────────────┴───────────────┴────────────────────┴────────────────────────┤
│ Commands: GET users/user_001 ✓ 234ms                                        │
└─────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Five panels, each with a purpose:

  1. Projects - All your Firebase projects, pulled from your CLI config
  2. Collections - Root-level collections in the selected project
  3. Tree - Documents and subcollections in an expandable tree view
  4. Details - The selected document's data with syntax highlighting
  5. Commands - A log of API calls with timing information

You can navigate between panels with h and l (or arrow keys), and move within lists using j and k. If you've used vim or lazygit, you'll feel right at home.

Feature Deep Dive

Let me walk you through the features that make LazyFire genuinely useful for day-to-day development.

Vim-Style Navigation

Everything is keyboard-driven. Here's the complete keybinding reference:

Key Action
h Move to left panel
l Move to right panel
j Move down in list
k Move up in list
Tab Jump to details panel
Enter Select item / Expand collection
Space Toggle expand/collapse
v Enter visual select mode
F Open query builder
/ Filter current panel
c Copy JSON to clipboard
s Save JSON to file
e Open in external editor
r Refresh current view
? Show help
@ Show command history
Esc Go back / Cancel
q Quit

The beauty of these keybindings is muscle memory. After a few sessions, you'll be flying through your database without thinking about it.

Expandable Tree View with Subcollections

Firestore's nested structure is fully supported. When you select a collection, LazyFire loads its documents. Each document can be expanded to reveal its subcollections, which can themselves be expanded to show their documents, and so on.

▼ 📁 users
    📄 user_001
    ▼ 📁 orders
        📄 order_abc
        📄 order_def
        ▶ 📁 items
    ▶ 📁 preferences
    📄 user_002
▶ 📁 products
Enter fullscreen mode Exit fullscreen mode

The arrow indicators ( for collapsed, for expanded) make it easy to see the current state at a glance. Collection folders are cyan, document icons are green - subtle color coding that helps you parse the tree quickly.

Interactive Query Builder

This is one of my favorite features. Press F (Shift+F) on any collection or subcollection to open the query builder:

Query view

How it works:

  • Navigate with j/k between rows (WHERE, ORDER BY, LIMIT, buttons)
  • Navigate with h/l between fields within a row
  • Press Enter to edit a field
  • Press a to add a new filter condition
  • Press d to delete a filter
  • Tab through operators and value types using popup selectors

Supported operators:

  • Comparison: ==, !=, <, <=, >, >=
  • Array operations: in, not-in, array-contains, array-contains-any

Value types:

  • auto - LazyFire guesses the type (string, number, boolean)
  • string - Force string interpretation
  • integer - Force integer
  • double - Force floating point
  • boolean - Force boolean
  • null - Null value
  • array - Comma-separated values for in/not-in operators

When you execute the query, results replace the tree view (or appear under the subcollection if you queried a nested collection). It's a massive time saver compared to writing queries in code or using the Firebase Console.

jq Query Support

The details panel shows your document as syntax-highlighted JSON. But what if you only care about a specific field? Or you want to transform the data before copying it?

Press / in the details panel and enter a jq expression:

Filter What it does
.name Extract just the name field
.address.city Nested field access
.users[0] First element of users array
.users[].name All names from users array
`.data \ keys`
`.items \ length`
select(.status == "active") Filter by condition

The filtered result is displayed in the details panel. Here's the powerful part: when a jq filter is active, copy and save operations use the filtered result, not the full document.

This means you can:

  1. Open a complex document
  2. Filter to just the fields you need with .data.users[].email
  3. Press c to copy only those email addresses to your clipboard

No more copying a massive JSON blob and manually extracting what you need.

Visual Select Mode

Sometimes you need to fetch multiple documents at once. Visual select mode makes this painless.

  1. Press v in the tree panel to enter select mode
  2. Move up/down with j/k to extend your selection (works in both directions, like vim)
  3. Selected documents are highlighted with a dim yellow + marker
  4. Press Space to fetch all selected documents in parallel
  5. Press Enter to view the combined results
  6. Press Esc to exit select mode

The parallel fetching is key here. Instead of fetching documents one by one (which would be slow due to network latency), LazyFire fires off all requests simultaneously. Even fetching 20 documents feels instant.

Smart Caching

Network calls are expensive. LazyFire implements a two-level caching system:

  1. Document cache - Once you fetch a document's data, it's cached. You'll see a small yellow dot · next to cached documents in the tree.

  2. Collection cache - When you expand a collection, the list of documents is cached. Collapsing and re-expanding uses the cache instead of hitting the API again.

This makes navigation feel snappy. The first time you expand a collection, there's a brief loading spinner. After that, it's instant.

The cache is session-based (cleared when you quit) and can be refreshed anytime with r.

Syntax Highlighting

Document JSON is displayed with full syntax highlighting using the chroma library:

  • Keys are colored distinctly from values
  • Strings, numbers, booleans, and nulls each have their own color
  • Proper indentation for nested objects
  • Large documents are scrollable

It's a small thing, but it makes a huge difference when you're scanning through document data.

External Editor Integration

Press e in the details panel to open the current document in your external editor. LazyFire checks these environment variables in order:

  1. $EDITOR
  2. $VISUAL
  3. Falls back to nvim if installed, otherwise vim

The document opens as a temporary JSON file. This is read-only for now (edits aren't saved back to Firestore), but it's useful when you need vim's full power to search or analyze a complex document.

Customizable Themes

LazyFire comes with a clean default theme, but you can customize every color. Create a config file at ~/.lazyfire/config.yaml:

ui:
  # Nerd Fonts version: "3", "2", or "" to disable icons
  nerdFontsVersion: "3"

  theme:
    activeBorderColor:
      - "#ed8796"
      - bold
    inactiveBorderColor:
      - "#5f626b"
    optionsTextColor:
      - "#8aadf4"
    selectedLineBgColor:
      - "#494d64"
Enter fullscreen mode Exit fullscreen mode

Color options:

  • Named colors: black, red, green, yellow, blue, magenta, cyan, white, default
  • Hex colors: #ed8796, #ff79c6, etc.
  • 256-color palette: Numbers 0 through 255
  • Attributes: bold, underline, reverse

Example themes:

Catppuccin Macchiato:

ui:
  theme:
    activeBorderColor: ["#ed8796", "bold"]
    inactiveBorderColor: ["#5f626b"]
    optionsTextColor: ["#8aadf4"]
    selectedLineBgColor: ["#494d64"]
Enter fullscreen mode Exit fullscreen mode

Dracula:

ui:
  theme:
    activeBorderColor: ["#ff79c6", "bold"]
    inactiveBorderColor: ["#6272a4"]
    optionsTextColor: ["#8be9fd"]
    selectedLineBgColor: ["#44475a"]
Enter fullscreen mode Exit fullscreen mode

Tokyo Night:

ui:
  theme:
    activeBorderColor: ["#7aa2f7", "bold"]
    inactiveBorderColor: ["#565f89"]
    optionsTextColor: ["#7dcfff"]
    selectedLineBgColor: ["#292e42"]
Enter fullscreen mode Exit fullscreen mode

Mouse Support

While keyboard navigation is the primary interface, LazyFire also supports mouse input:

  • Click on any panel to focus it
  • Click on items to select them
  • Click outside a popup to close it

This makes it accessible to team members who might not be familiar with vim-style navigation.

Nerd Font Icons

If you use a Nerd Font (and you should - they're great), LazyFire displays beautiful icons throughout the interface:

  • 🔥 Firebase flame for the welcome screen and projects
  • 📁 Folder icons for collections
  • 📄 Document icons for documents
  • Various status indicators

If icons don't display correctly, you can switch to Nerd Fonts v2 compatibility mode or disable icons entirely:

ui:
  nerdFontsVersion: "2"  # For older Nerd Fonts
  # or
  nerdFontsVersion: ""   # Disable icons
Enter fullscreen mode Exit fullscreen mode

Installation

Getting started takes less than a minute.

Homebrew (macOS/Linux)

brew tap marjoballabani/tap
brew install lazyfire
Enter fullscreen mode Exit fullscreen mode

Go Install

If you have Go installed:

go install github.com/marjoballabani/lazyfire@latest
Enter fullscreen mode Exit fullscreen mode

From Source

git clone https://github.com/marjoballabani/lazyfire.git
cd lazyfire
go build -o lazyfire .
./lazyfire
Enter fullscreen mode Exit fullscreen mode

Download Binary

Pre-built binaries for macOS and Linux are available on the releases page.

Prerequisites

  1. Firebase CLI - Install with npm install -g firebase-tools
  2. Firebase Authentication - Run firebase login if you haven't already
  3. Terminal with true color support - Most modern terminals (iTerm2, Alacritty, Kitty, Windows Terminal) support this
  4. Nerd Font (optional) - For icons. Download here

Quick Start

Once installed, getting started is simple:

# Make sure you're logged in
firebase login

# Launch LazyFire
lazyfire
Enter fullscreen mode Exit fullscreen mode

You'll see a welcome screen with your Firebase projects. Use j/k to navigate, Enter to select a project, and start exploring.

Pro tip: If you work with multiple Firebase projects, LazyFire remembers them all. Switch between production, staging, and development environments without re-authenticating.

The Tech Behind It

For those curious about the implementation:

  • Language: Go - chosen for its excellent cross-platform support, single binary distribution, and strong concurrency primitives (goroutines make parallel document fetching trivial)

  • Terminal UI: gocui - Jesse Duffield's fork of the gocui library, which powers lazygit. It's battle-tested and handles all the complexity of terminal rendering.

  • jq Implementation: gojq - A pure Go implementation of jq. No external dependencies, works on all platforms.

  • Syntax Highlighting: chroma - Fast, accurate syntax highlighting with theme support.

  • Firebase API: Direct REST API calls using the access token from Firebase CLI. No Firebase Admin SDK means fewer dependencies and a smaller binary.

The entire application is around 5,000 lines of Go code. It's not a massive project, but every feature is intentional and polished.

Why I Built This

I'll be honest: I built this for myself.

I spend most of my working day in the terminal. Neovim for editing code, lazygit for version control, various CLIs for deployment and infrastructure. My terminal is my IDE, and I've optimized it over years to minimize friction.

But Firestore was always the exception. Every time I needed to check a document or verify that a write succeeded, I had to leave my terminal and open a browser. It broke my concentration every single time.

One day, after the hundredth alt-tab to the Firebase Console, I thought: "lazygit exists for git... why doesn't something like this exist for Firestore?"

I couldn't find anything that fit the bill, so I built it.

The first version was rough - just enough to browse collections and view documents. But over time, I kept adding features that would save me time:

  • "I keep running the same queries" → Query builder
  • "I just want to see one field" → jq support
  • "Fetching documents one by one is slow" → Visual select mode + parallel fetching
  • "I hate waiting for data to load again" → Caching

Each feature came from real frustration during real development work. That's why LazyFire feels cohesive - it's not a collection of features someone thought might be useful, it's a collection of solutions to problems I actually had.

What's Next

LazyFire is actively maintained and I have a roadmap of features I'm considering:

Near-term:

  • Document editing (create, update, delete) - currently read-only
  • Batch operations on selected documents
  • Export to CSV format

Medium-term:

  • Firestore emulator support for local development
  • Realtime listeners for live document updates
  • Saved queries that persist across sessions

Long-term:

  • Firebase Auth user browsing
  • Cloud Functions log viewing
  • Multiple database support (for projects with multiple Firestore instances)

If any of these would be particularly useful for your workflow, let me know in the comments or open an issue on GitHub. User feedback directly influences what I prioritize.

Try It Out

The project is open source under the MIT license:

github.com/marjoballabani/lazyfire

If you work with Firestore and spend any amount of time in the terminal, give it a try. The installation takes 30 seconds, and you might find it changes how you interact with your database.

Stars on GitHub are always appreciated - they help others discover the project. And if you run into any issues or have feature requests, please open an issue. I read every single one.


About Me

I'm Marjo Ballabani, a software developer who believes that good developer tools should get out of your way. I build things that make my workflow faster, and sometimes those things are useful to others too.

You can find me on GitHub where I occasionally open source projects like this one.


What terminal tools have changed your workflow? I'd love to hear about your favorite TUIs and CLI tools in the comments below!

Top comments (0)