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

# Persistence

> Understanding what persists and how

# Persistence

When you modify `@field` properties, Idyllic automatically persists changes to durable storage. You don't write database code, configure serialization, or explicitly save data. This guide explains what persists, when, and how to work with the system effectively.

## What Persists Automatically

Everything marked with `@field` persists. After each action completes, Idyllic serializes field values to SQLite storage within the Durable Object. When the system wakes—whether seconds or days later—state restores exactly as you left it.

```typescript theme={null}
export default class TaskManager extends AgenticSystem {
  @field tasks: Task[] = [];
  @field settings = { theme: 'dark' };
  @field lastSync: Date | null = null;

  @action()
  addTask(title: string) {
    this.tasks.push({ id: crypto.randomUUID(), title, done: false });
    // Persists automatically when action completes
  }
}
```

Every task added through `addTask` stores durably. Close your browser, restart the server, return a week later—`this.tasks` contains everything you added. No explicit save call needed.

Stream fields persist their `current` value. When streaming completes, accumulated content saves. Clients connecting afterward see the final result.

## What Does Not Persist

Properties without `@field` exist only in memory. They reset when the Durable Object hibernates or restarts.

```typescript theme={null}
export default class MySystem extends AgenticSystem {
  @field count = 0;      // Persists

  cache = new Map();     // Lost on restart
  tempData = [];         // Lost on restart
}
```

Use non-field properties for temporary caches, computed values, or data you don't want to persist.

## When Persistence Happens

State writes to storage after each `@action` method completes successfully and when streams complete or fail. The write happens asynchronously—no blocking I/O in your action methods.

If an action throws before completing, state changes roll back. This transactional behavior keeps state consistent even when errors occur.

## Serialization Requirements

State stores as JSON. All values must be JSON-serializable:

| Type            | Supported | Notes                         |
| --------------- | --------- | ----------------------------- |
| Primitives      | Yes       | string, number, boolean, null |
| Plain objects   | Yes       |                               |
| Arrays          | Yes       |                               |
| Maps, Sets      | Yes       | Serialized/restored correctly |
| Dates           | Yes       | Serialized as ISO string      |
| Functions       | No        | Cannot serialize              |
| Class instances | No        | Lose prototype chain          |

## Handling Schema Changes

As your application evolves, state shape may change. Handle migrations in `onCreate()`:

```typescript theme={null}
@field version = 2;
@field tasks: Task[] = [];
@field settings = { theme: 'dark', notifications: true };

onCreate() {
  if ((this as any).version === 1) {
    this.settings.notifications = true;
    this.version = 2;
  }
}
```

Include a version number, check it on load, apply transformations.

## The SQL Escape Hatch

For complex queries on large datasets, Idyllic provides direct SQL access:

```typescript theme={null}
@action()
async getRecentEvents(limit: number) {
  return await this.sql`
    SELECT * FROM events
    ORDER BY timestamp DESC
    LIMIT ${limit}
  `;
}
```

Use this for append-only logs or datasets too large to load into memory.

***

## FAQ

### How much data can I store?

Durable Objects support gigabytes. For most applications—conversation histories, task lists—tens of thousands of items work fine.

### What's the read/write latency?

Reads from state are instant (property access in memory). Writes persist asynchronously after actions complete—no blocking I/O.

### Can I query across instances?

No. Each instance has isolated storage. For cross-instance queries, use an external database.

### How do I back up data?

Create an export action:

```typescript theme={null}
@action()
exportData() {
  return { tasks: this.tasks, settings: this.settings };
}
```
