> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.latch.bio/llms.txt
> Use this file to discover all available pages before exploring further.

# Registry Usage Tutorial

This tutorial walks through a practical example of integrating **Latch Registry** into a workflow.

* Code: [https://github.com/latchbio/fancy\_samplesheet\_workflow/tree/main](https://github.com/latchbio/fancy_samplesheet_workflow/tree/main)
* Workflow UI: [https://console.latch.bio/workflows/111268/parameters](https://console.latch.bio/workflows/111268/parameters)

## What You Will Achieve

By the end of this tutorial, your workflow will let scientists seamlessly connect Registry tables to workflow execution. Here's the user experience you are enabling:

1. In the UI, scientists click **“Import from Registry”**, choose a table, and select specific rows.
2. A modal appears to match Registry column names with the workflow input parameters you define in code.
3. Once confirmed, the selected rows are imported directly into the workflow.
4. Users can launch the workflow with the imported data.
5. The workflow then upserts results back into the same Registry records.
6. Updated records are visible in the original Registry table, linking inputs with outputs.

## Importing rows from Registry

### Usage Example

```python expandable theme={null}
from dataclasses import dataclass

from latch.resources.tasks import small_task
from latch.resources.workflow import workflow
from latch.types.file import LatchFile
from latch.types.metadata import LatchAuthor, LatchMetadata, LatchParameter
from latch.types.samplesheet_item import SamplesheetItem

# Define a dataclass to represent a row in the samplesheet
@dataclass
class Row:
    sample_name: str
    fastq_1: LatchFile
    fastq_2: LatchFile


metadata = LatchMetadata(
    display_name="Test Record from Samplesheet Input",
    author=LatchAuthor(
        name="CHANGE ME",
    ),
    parameters={
        "rows": LatchParameter(
            display_name="Test",
            samplesheet=True, # Enable samplesheet input display in the UI
            batch_table_column=True,  # Show this parameter in batched mode.
        ),
    },
)

@small_task
def task(rows: list[SamplesheetItem[Row]]) -> None: # Wrap the `Row` dataclass in `SamplesheetItem` 
    for row in rows:
        if row.record is None: # Check if the row was imported from Registry
            print(f"{row.data.sample_name}: Data is not from registry")
            continue

        # If the row was imported from Registry, we can access the Record object

        print(f"{row.data.sample_name}:")
        print(f" -> From Record {row.record.id} in Table {row.record.get_table_id()}")
        print(f" -> Created at {row.record.get_creation_time().isoformat()}")
        print(f" -> Last updated at {row.record.get_last_updated().isoformat()}")


@workflow(metadata)
def fancy_samplesheet_wf(rows: list[SamplesheetItem[Row]]) -> None:
    return task(rows=rows)
```

### Core Ideas

To import a table from Registry into your workflow, follow these steps:

1. **Define a dataclass:**
   Create a Python dataclass that represents a single row in the samplesheet.

2. **Mark it as a samplesheet:**
   In the LatchMetadata, set samplesheet=True to indicate that this dataclass should be treated as a samplesheet.

3. **Match Registry columns:**
   The fields in your dataclass should match the column names in the Registry table (or be a subset). If your dataclass fields use the exact same names as the Registry columns, the workflow UI will automatically align them in the “column matching” modal.

4. **Wrap rows in SamplesheetItem:**
   Each row should be wrapped in a SamplesheetItem, which allows the workflow to detect whether the row came from Registry.

5. **A `SamplesheetItem` has two fields:**

   * `data` – the actual row data. For example, `row.data.sample_name` will give you the value from the sample\_name column.
   * `record` – the Registry Record object for that row.
     * If the row was imported from Registry, `record` will be populated with the corresponding Record.
     * If the row was created manually (not imported), `record` will be `None`.

## Upserting rows to Registry

A `Table` can be modified by using the `Table.update()` function. `Table.update()` returns a context manager (and hence must be called using `with` syntax) with an `upsert_record` method. Visit the [Table Object](/registry/sdk/table-objects) page for the API reference.

### Usage Example

```python expandable theme={null}
from latch.registry.table import Table
from latch.types.directory import LatchDir

@small_task
def task(rows: list[SamplesheetItem[Row]]) -> None: # Wrap the `Row` dataclass in `SamplesheetItem` 
    for row in rows:
        if row.record is None: # Check if the row was imported from Registry
            print(f"{row.data.sample_name}: Data is not from registry")
            continue

        # If the row was imported from Registry, we can access the Record object

        print(f"{row.data.sample_name}:")
        print(f" -> From Record {row.record.id} in Table {row.record.get_table_id()}")
        print(f" -> Created at {row.record.get_creation_time().isoformat()}")
        print(f" -> Last updated at {row.record.get_last_updated().isoformat()}")

        # Imagine a directory object that we want to upsert
        cellranger_output = LatchDir(...)

        # Upsert the record
        table = Table(id=row.record.get_table_id())
        with table.update() as updater:
            updater.upsert_record(
                "<Record identifier under the Name column in Registry table UI>",
                cellranger_output=cellranger_output
            )
```

### Core Ideas

* Retrieve the Table ID by calling `get_table_id()` on the `Record` object.
* Initialize a `Table` object by calling `Table(id=table_id)`.
* Call `Table.update()` to get a context manager with an `upsert_record` method.
