# Python Microsoft Fabric - Loading data into tables

Append new rows every night, replace small tables whole and load files that are already in the lakehouse.

This page loads data into lakehouse tables in three ways - appending the day's new rows to a table that grows, replacing a small table whole with the current list, and turning a file that is already in the lakehouse into a table.

> **What you need from your Fabric admin**
>
> The workspace ID and the lakehouse ID, both from the address bar when the lakehouse is open. The app registration your connection signs in as must be a Contributor on the workspace to write to tables.

## Two kinds of tables {#two-kinds-of-tables}

A lakehouse is where Fabric keeps tables and files together, and the tables in it fall into two kinds. Some record what happened, e.g. orders, calls or transactions, and they only ever grow, so new rows are appended to them. Others describe things, e.g. sites, products or customers, and they are small, so the whole table is replaced with the current list whenever it changes.

## Appending the day's rows {#appending-the-days-rows}

The service below runs once a night. It keeps the time it stopped at in the cache, takes everything recorded since then, appends the rows and moves the marker forward.

Before it runs, the `admissions` table holds three rows:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class AppendAdmissions(Service):

    def handle(self):

        workspace_id = '11111111-2222-3333-4444-555555555555'
        lakehouse_id = '66666666-7777-8888-9999-000000000000'

        conn = self.microsoft.fabric['My Fabric']

        # 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-03-11T07:15:00Z',
                'discharged_at': '',
                'status': 'Admitted',
            },
            {
                'admission_id': 'ADM-1042',
                'location': 'Riverside',
                'admitted_at': '2026-03-11T09:40:00Z',
                'discharged_at': '',
                'status': 'Admitted',
            },
        ]

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

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

        self.response.payload = {'appended': len(rows), 'since': since}
```

After the run, the two new rows are at the bottom of the table:

`write_table` takes any number of rows. It writes them as CSV files to the lakehouse's `Files` section, 100,000 rows per file, then asks the lakehouse to load all of the files into the table in one operation and returns when that operation is done.

## Replacing a small table {#replacing-a-small-table}

The source system returns the list with its own field names. The service maps them to the table's columns and replaces the table whole. No `mode` is needed, because replacing is the default.

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class ReplaceLocations(Service):

    def handle(self):

        workspace_id = '11111111-2222-3333-4444-555555555555'
        lakehouse_id = '66666666-7777-8888-9999-000000000000'

        conn = self.microsoft.fabric['My Fabric']

        # The source system's reply, with its own field names.
        items = [
            {'id': 'LOC-01', 'label': 'Riverside',   'town': 'Portland', 'region': 'OR', 'bed_count': 120},
            {'id': 'LOC-02', 'label': 'Oak Hill',    'town': 'Salem',    'region': 'OR', 'bed_count': 80},
            {'id': 'LOC-03', '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)

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

        self.response.payload = {'locations': len(rows)}
```

## Loading a file that is already there {#loading-a-file-that-is-already-there}

When a CSV file is already in the lakehouse, e.g. in `Files/incoming`, `load_table` loads it into a table. It starts the load and returns the address where its progress can be checked, and `wait_for_operation` waits there until the load is done.

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class LoadAdmissionsFile(Service):

    def handle(self):

        workspace_id = '11111111-2222-3333-4444-555555555555'
        lakehouse_id = '66666666-7777-8888-9999-000000000000'

        conn = self.microsoft.fabric['My Fabric']

        # Start loading the file into the table ..
        location = conn.load_table(
            workspace_id,
            lakehouse_id,
            'admissions',
            'Files/incoming/admissions-2026-03-11.csv',
            mode='Append',
        )

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

        self.response.payload = {'status': operation['status']}
```

The path is relative to the lakehouse. The file is read as CSV with a header row by default, `file_format='Parquet'` reads Parquet instead, and a whole folder of files loads in one call with `path_type='Folder'` and `recursive=True`.

Each load is listed in the lakehouse's recent runs, with its status and duration:

## See also {#see-also}

- [Keeping lookup tables up to date](https://zato.io/docs/dev/examples/cloud/fabric/lookup-tables.html) - One nightly job that replaces several small tables
- [Sending and receiving files](https://zato.io/docs/dev/examples/cloud/fabric/files.html) - Loading incoming files into tables and writing exports
- [Tables](https://zato.io/docs/dev/examples/cloud/fabric/api/tables.html) - write\_table, load\_table, list\_tables and wait\_for\_operation in detail

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - Everything about writing services, in one place
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how to shape both
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
