How Rowy built a spreadsheet UI for Firestore

Harini Janakiraman, Rowy’s CEO and co-founder, talks about their journey and how they use Firebase to build and ship tools that enable developers to manage data in Firestore without having to build admin interfaces themselves.

Cloud Firestore is a schemaless database that provides developers with a flexible environment to build apps fast. However, the way the data is presented in the Firebase console UI is bare bones and difficult to navigate, especially for non-technical users. This makes it hard to collaborate and manage the data with teams internally.

As a workaround, many developers end up building their own internal management UIs from scratch / using internal tool builders or downloading Firestore data as spreadsheets using custom scripts. These spreadsheets are then shared back and forth over emails, resulting in redundant stale copies of the data. These solutions result in developers’ time being wasted and away from their actual product development.

An Open-Source CMS for Firebase

Rowy is an open-source project to solve this problem by allowing users to manage their Firestore data in a collaborative and familiar spreadsheet-like UI. All the features that are typically needed for an operational database are available, such as filtering & sorting of data, bulk importing & exporting of Firestore data to CSV, TSV, or JSON files.

Watch this short product walkthrough video to learn more.

Spreadsheet-UI to manage Firestore data
Spreadsheet-UI to manage Firestore data

Rowy connects directly to your Firebase project and allows you to manage existing Firestore collections or create entirely new ones from scratch. Here is a high-level architectural overview of Rowy:

Overview of Rowy architecture
Overview of Rowy architecture

Granular role based access control with Firebase Authentication and Firestore Security Rules

Normally, giving non-technical users access to production data can be scary. Rowy empowers teams to be autonomous in their day-to-day operational tasks without the need to rely on developers..

Rowy leverages Firebase Authentication and Firestore Security Rules to allow access in a granular manner needed by teams for their role. This allows for flexible roles-based access control with Firebase custom claims without having to build an authorization layer from scratch.

Project admins can invite users with custom roles from the Rowy UI in just a few steps:

  1. Create custom roles and write Firestore security rules

    Project admins can create specific roles based access controls for various teams in the organization, for example: marketing, operations, or product. Each collection table can be assigned to specific roles that can access them using granular Firestore security rules.

    User management with role based access control
    User management with role based access control

    This allows for flexibility to have multiple table views for Firestore collections and roles relevant to them. For example, admins can hide columns that trigger backend functions from the marketing team while keeping them editable for developers. Plus, Rowy automatically suggests Firestore Security Rules for every new table, so admins just need to copy and paste them to the Firebase Console.

  2. Invite users

Team members can then be easily invited to the projects with their specific roles. When they sign in, they will only see the tables assigned to their roles. This way, admins have full flexibility to control access without having to write a single line of code.

Inviting users with custom roles
Inviting users with custom roles

Live Collaboration on Firestore through Realtime Listeners

Rowy is built with collaboration at its core and achieves a smooth, realtime collaborative experience by using Firestore Snapshot Listeners. This allows multiple users to access the same Firestore collection through the Rowy interface with all updates reflected in realtime in Firestore.

Realtime collaboration on spreadsheet-UI connected to Firestore Snapshot Listeners
Realtime collaboration on spreadsheet-UI connected to Firestore Snapshot Listeners

Let’s take a look at how this is implemented.

// Create a listener for the query
import { onSnapshot } from 'firebase/firestore'
const unsubscribe = onSnapshot(
  query,
  (snapshot) => {
    // Extract doc data from query
    const docs = snapshot.docs.map((doc) => doc.data());
    // Store the data in our state manager Jotai
    setTableRows(docs);
  },
  (error) => {
    // Handle Firestore errors in the UI
    handleError(error);
  }
);

// Use the Firestore data in our table component
export default function Table() {
  const [tableRows] = useAtom(tableRowsAtom);
  ...
  return <DataGrid ... />;
}

In the above code, Firestore’s onSnapshot function is used for listening to realtime updates from your Firestore collection. Jotai is also used to store the query state, allowing users to filter by specific fields, display a single document (using Firestore where query constraints), or order by a field value (using orderBy). Incoming data is then stored using a React state manager, Jotai, and the Table UI component receives the data from the Jotai store.

Explore the listener code on GitHub here and the consuming Table component here.

Customizable Columns Types for the Firebase Admin Panel

Rowy supports flexible field types such as Color, Rating, Rich Text, Markdown, JSON and more. There are also column fields that allow files/images uploads to Cloud Storage and action button fields that trigger Cloud Functions.

These customizable components map every field from Rowy to its underlying supported field types in Firestore. For example, the Single Select field writes a string to Firestore, and Multi Select writes an array of strings.

Custom field types mapped to the supported fields of Firestore
Custom field types mapped to the supported fields of Firestore

By abstracting away the Firestore field types at a UI level, we were able to provide support for over 30 column field types. Explore them on the Rowy live demo table.

<MultiSelect
  // Ensure we have an array of strings
  value={sanitizeValue(value)}
  // And always write an array of strings to Firestore
  onChange={onSubmit}
  // Provide a list of options to be displayed in the UI
  options={config.options}

  // Allow users to write anything not provided in config.options
  freeText={config.freeText}
  // Allow the admin to lock this field for editing
  disabled={column.locked}
  // Display a human-friendly name for this field,
  // instead of the actual field key in Firestore
  label={column.name}
/>

These components can also be customized, and their data validated before they are persisted to Firestore with regular expressions or scripts. Default values can also be assigned to Firestore data fields for each document created or fields can be made as required before the entire row of data is saved.

Required and default value options for Firestore fields
Required and default value options for Firestore fields

This lets users build powerful workflows, where non-technical users can have easy inputs, and developers can have predictable field values on Firestore. Explore the code for all the supported field types. You can also create your own field types by contributing to Rowy’s open-source code base.

Conclusion

Firebase enabled Rowy to build a complete product with a small team of developers. And with Rowy, developers like you can focus on building products for your customers rather than on internal tools. Try Rowy using the live playground or explore by cloning one of the Firestore table templates.

Rowy is open-source and available on GitHub.