Writing data to Fabric

Append new rows, replace small tables whole, load files that are already in the lakehouse and write files to it.

This page will show you how to write data from Python into Microsoft Fabric:

  • Appending the day's new rows to a table that grows
  • Replacing a small table whole with the current list
  • Turning a file that is already in the lakehouse into a table
  • Writing a file to the lakehouse

Under the hood, the Zato services will automatically call OneLake and trigger load-to-table Fabric APIs

Connection and IDs

The app registration, the Fabric connection in the Zato Dashboard, and the workspace and lakehouse IDs are the same as in the tutorial, so set them up as described there first.

Writing to tables needs the app registration to have the Contributor role in the workspace - in Fabric, open the workspace, click "Manage access" and check the role next to the app registration's name.

Two kinds of tables

As a recap, a lakehouse is where Fabric keeps tables and files together, and the tables in it can be generally of two kinds:

  • Some will record what happened, e.g. orders, calls or transactions, and they only ever grow, so new rows are appended to them
  • Others will describe things, e.g. sites, products or customers, and they are small, so the whole table can be replaced with the current list whenever it changes

Appending the day's rows

Say you have a source system that records admissions and, once a night, a scheduled service should copy the day's new ones to the admissions table in Fabric. The service below does that:

  • It keeps in the cache the time it stopped at the last time
  • It takes everything recorded since then
  • It appends the rows with conn.write_table
  • It moves the marker in the cache forward for the next night
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class AppendAdmissions(Service):

    name = 'fabric.append-admissions'

    def handle(self):

        # The two IDs from the tutorial ..
        workspace_id = '262ddd3d-3d0d-4495-b80f-6dddda1e23a1'
        lakehouse_id = '73811955-064d-481a-a7e9-f9c563124f9e'

        # .. where the previous run stopped ..
        since = self.cache.get('admissions.loaded_until')

        # .. the rows recorded since then, from the source system ..
        rows = [
          {
            'admission_id': 'ADM-1041',
            'location': 'Riverside',
            'admitted_at': '2026-09-01 07:15:00',
            'discharged_at': '',
            'status': 'admitted',
          },
          {
            'admission_id': 'ADM-1042',
            'location': 'Riverside',
            'admitted_at': '2026-09-01 09:40:00',
            'discharged_at': '',
            'status': 'admitted',
          },
        ]

        # .. get a Fabric connection ..
        conn = self.microsoft.fabric['Zato Fabric']

        # .. add the rows to what the table already holds ..
        # .. note the "Append" mode ..
        conn.write_table(workspace_id, lakehouse_id,
            'admissions', rows, mode='Append',
        )

        # .. store the last timestamp for the next run ..
        last_row = rows[-1]
        self.cache.set('admissions.loaded_until', last_row['admitted_at'])

        # .. and tell our caller what was done.
        self.response.payload = {'appended': len(rows), 'since': since}

After invoking it, it will append new rows, and you'll see a response as below

{"appended": 2, "since": "2026-08-31 09:00:00"}

Method conn.write_table takes any number of rows:

  • It writes them as CSV files to the lakehouse's Files section, 100,000 rows per file
  • It tells the lakehouse to load all of the files into the table in one operation
  • It returns when that operation is done so conn.query can read the rows

Replacing a small table

In this example, the list of locations has its source in another system and the locations table in Fabric is a copy of it. The service below will:

  • Read the list, which comes with the source system's own field names
  • Map each field sto the Fabric table's columns
  • Replace the whole table in Fabric - note that no mode is needed for this, because replacing is the default
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class ReplaceLocations(Service):

    name = 'fabric.replace-locations'

    def handle(self):

        # The two IDs from the tutorial ..
        workspace_id = '262ddd3d-3d0d-4495-b80f-6dddda1e23a1'
        lakehouse_id = '73811955-064d-481a-a7e9-f9c563124f9e'

        # .. in a real service, this list would be obtained ..
        # .. via a REST API call or a similar call to the source system ..
        items = [
          {
            'id': 'LOC-1',
            'label': 'Riverside',
            'town': 'Portland',
            'region': 'OR',
            'bed_count': 120,
          },
          {
            'id': 'LOC-2',
            'label': 'Oak Hill',
            'town': 'Salem',
            'region': 'OR',
            'bed_count': 80,
          },
          {
            'id': 'LOC-3',
            'label': 'Maple Grove',
            'town': 'Eugene',
            'region': 'OR',
            'bed_count': 60,
          },
        ]

        # .. rename the fields to the table's columns ..
        rows = []
        for item in items:
            row = {
                'location_id': item['id'],
                'name': item['label'],
                'city': item['town'],
                'state': item['region'],
                'beds': item['bed_count'],
            }
            rows.append(row)

        # .. get a Fabric connection ..
        conn = self.microsoft.fabric['Zato Fabric']

        # .. replace the table with the current list ..
        conn.write_table(workspace_id, lakehouse_id, 'locations', rows)

        # .. and tell our caller how many rows the table has now.
        self.response.payload = {'locations': len(rows)}

After invoking the service you'll see:

{"locations": 3}

Turning a file into a table

In this case, picture a partner uploads a CSV file to the lakehouse's Files/incoming folder each month and you need a service to turn that file into rows of the invoices table.

You'd create a scheduled job to trigger a service like below, which will then use two calls:

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class LoadInvoicesFile(Service):

    name = 'fabric.load-invoices-file'

    def handle(self):

        # The two IDs from the tutorial ..
        workspace_id = '262ddd3d-3d0d-4495-b80f-6dddda1e23a1'
        lakehouse_id = '73811955-064d-481a-a7e9-f9c563124f9e'

        # .. get a Fabric connection ..
        conn = self.microsoft.fabric['Zato Fabric']

        # .. start loading the file into the table ..
        location = conn.load_table(
            workspace_id,
            lakehouse_id,
            'invoices',
            'Files/incoming/invoices-2026-08.csv',
            mode='Append',
        )

        # .. wait until Fabric reports that it is done ..
        operation = conn.wait_for_operation(location)

        # .. and return the outcome to our caller.
        self.response.payload = {'status': operation['status']}

After invoking the service you'll see:

{"status": "Succeeded"}

Writing a file to Fabric

In this example, the billing system has the month's invoiced totals per location and a CSV file with them needs to land in the lakehouse's Files/exports folder, where other people pick it up.

The service below will:

  • Read the totals from the billing system
  • Write them to a file under Files/exports with conn.onelake_write, which turns a list of dicts into CSV and creates the file, or replaces it if it exists
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class WriteInvoiceExport(Service):

    name = 'fabric.write-invoice-export'

    def handle(self):

        # The two IDs from the tutorial ..
        workspace_id = '262ddd3d-3d0d-4495-b80f-6dddda1e23a1'
        lakehouse_id = '73811955-064d-481a-a7e9-f9c563124f9e'

        # .. which month this export is for ..
        month = '2026-08'

        # .. in a real service, these totals would be obtained ..
        # .. from a database or a REST API call to the billing system ..
        rows = [
          {'location': 'Riverside',   'invoiced': 14050.00},
          {'location': 'Oak Hill',    'invoiced': 13450.00},
          {'location': 'Maple Grove', 'invoiced': 13750.00},
        ]

        # .. get a Fabric connection ..
        conn = self.microsoft.fabric['Zato Fabric']

        # .. write the rows as a CSV file to the exports folder ..
        file_path = f'{lakehouse_id}/Files/exports/invoices-{month}.csv'
        conn.onelake_write(workspace_id, file_path, rows)

        # .. and tell our caller where the file is.
        self.response.payload = {'file': file_path, 'rows': len(rows)}

After invoking the service you'll see:

{
  "file": "73811955-064d-481a-.../Files/exports/invoices-2026-08.csv",
  "rows": 3
}

See also

PageWhat it covers
Your first Fabric integrationThe app registration, the connection and where the IDs come from
Fabric API - Tablesconn.write_table, conn.load_table, conn.list_tables and conn.wait_for_operation, with every parameter
Fabric API - Filesconn.onelake_write, conn.onelake_read, conn.onelake_list and conn.onelake_delete, with every parameter

Learn more