Firebase Realtime Database to MySQL: How to Move Your Data

Replicate Firebase Realtime Database into MySQL with Airbyte. Scoping the node path, the local_infile prerequisite, and handling nested JSON data.

Summarize with AI:

Moving Firebase Realtime Database to MySQL turns a JSON tree into relational rows. Firebase stores everything as one large nested document synced live to your clients, which is excellent for building apps and poor for answering questions with SQL. MySQL gives you joins, aggregates, and a schema your reporting tools already understand.

Airbyte connects the two without code, and the whole setup comes down to a handful of values on each side. Here is what you need before you start.

SideWhat you needNotes
FirebaseDatabase nameThe identifier from your database URL
FirebaseService Account Key JSONNeeds the Firebase Realtime Database Viewer role. JSON keys only
FirebaseNode Path (optional)Which subtree to sync. Defaults to the root node. The most important choice here
FirebaseBuffer Size (optional)How many records are fetched at once
MySQLlocal_infile set to trueRequired. Airbyte loads via LOAD DATA LOCAL INFILE and fails without it
MySQLA target databaseActs as the default schema when the source provides no namespace

Why move Firebase data into MySQL?

Realtime Database is built around live synchronisation to connected clients. That design makes it very good at what apps need and awkward for everything else. There is no SQL, querying is limited to what your indexes and data structure allow, and analysts cannot point a BI tool at a JSON tree.

Replicating into MySQL gives you a relational copy that joins against your other operational data, supports the reporting stack you already run, and takes analytical read load off the database serving your users. Airbyte runs that copy on whatever schedule you set, so nobody maintains an export script.

Why is Node Path the decision that matters?

The Firebase Realtime Database source syncs a node path from your database, and the default is the root node. On a real Firebase project, the root is the entire database: users, sessions, app state, cached content, and everything else your app has ever written, in one nested structure.

Leaving it at the default is the single most common way this pipeline goes wrong. You end up pulling far more than you wanted, the structure that arrives in MySQL reflects your whole app rather than the entity you care about, and every sync repeats the exercise.

Set a specific path instead, something like /data/users or /orders, so the connector reads the subtree that corresponds to one logical entity. If you need several entities, create several connections, each scoped to its own path. That maps naturally onto separate MySQL tables and keeps each sync small enough to reason about.

Buffer Size controls how many records are fetched at a time. The default is fine for most cases, and it is the setting to adjust if you see memory pressure on large nodes.

Setting up the Firebase source

Create a Google Cloud service account with the Firebase Realtime Database Viewer role, which grants read access and nothing more. Airbyte's documentation recommends a service account used exclusively for Airbyte, which makes permissions and auditing considerably easier later.

Generate a key in JSON format, since that is the only format Airbyte supports. Google shows you the contents exactly once, so download it at creation. Paste the contents into the source configuration alongside your database name and node path, then delete the file from your machine.

Setting up the MySQL destination

One prerequisite catches almost everyone. Before configuring the MySQL destination, you must set the local_infile system variable to true by running SET GLOBAL local_infile = true as a user with SYSTEM_VARIABLES_ADMIN permission. Airbyte loads data using LOAD DATA LOCAL INFILE, and without this the destination will not work.

Note that this is a global variable, so on managed MySQL you may need your provider's parameter group rather than a session query, and on a shared instance it is a change worth agreeing with whoever owns the server.

Then choose or create the database that will act as the default schema when the source provides no namespace, and supply host, port, and credentials for a user that can write to it. An SSH tunnel through a bastion is available if the instance has no directly reachable address.

One naming detail worth checking: MySQL's identifier case sensitivity depends on the lower_case_table_names system variable, and it behaves differently across operating systems. If your Firebase node names use mixed casing, confirm what actually gets created before writing queries against the table names you expect.

What about nested data?

This is the fundamental tension in the pipeline. Firebase stores arbitrarily nested JSON, and MySQL wants columns. Scoping the node path is what makes the difference: point it at a node whose children are records of the same shape, and what arrives in MySQL looks like a table. Point it at the root, and it does not.

Where nesting is unavoidable, plan to do the unpacking in MySQL with its JSON functions rather than expecting the connector to flatten a structure that has no fixed shape. Structuring the Firebase side with replication in mind, keeping entity collections shallow and consistent, pays off more than any connector setting.

Frequently asked questions

Why does my MySQL destination fail immediately?

Almost always local_infile. Airbyte loads data with LOAD DATA LOCAL INFILE, so the variable must be set to true before the destination will work. On managed MySQL, set it through your provider's parameter configuration.

Should I leave Node Path at the default?

No. The default is the root node, meaning your entire database. Set a path scoped to one logical entity, and create separate connections for separate entities so each lands in its own MySQL table.

What permissions does the service account need?

The Firebase Realtime Database Viewer role, which grants read access only. Use a service account dedicated to Airbyte so permissions and auditing stay simple.

Can I sync several parts of my database?

Yes, by creating one connection per node path. That keeps each sync scoped and gives you one MySQL table per entity, which is easier to model than a single sync of the whole tree.

How is deeply nested data handled?

Scope the node path so the children you sync share a consistent shape. Where nesting remains, unpack it in MySQL using its JSON functions rather than expecting a connector to flatten arbitrary structures.

Get your Firebase data into MySQL

Set local_infile, scope the node path to one entity, and let Airbyte keep MySQL current on the schedule you choose. Moving a document store into a relational database instead? See our guides to MongoDB to PostgreSQL and MySQL to PostgreSQL.

Start syncing now →

Integrate with 600+ apps using Airbyte

Move data from 600+ sources into warehouses, lakes, and beyond. Set up pipelines in minutes with pre-built connectors and the Connector Builder.