{
  "slug": "persist/adapters",
  "title": "Storage Adapters",
  "description": "Configure SQLite, Postgres, S3, and P2P adapters for Persist stores.",
  "url": "https://cuitty.com/docs/persist/adapters",
  "markdown_url": "https://cuitty.com/docs/persist/adapters.md",
  "json_url": "https://cuitty.com/docs/persist/adapters.json",
  "frontmatter": {
    "title": "Storage Adapters",
    "description": "Configure SQLite, Postgres, S3, and P2P adapters for Persist stores.",
    "order": 2,
    "section": "Persist",
    "updatedAt": "2026-05-12"
  },
  "headings": [
    {
      "depth": 1,
      "slug": "storage-adapters",
      "text": "Storage Adapters"
    },
    {
      "depth": 2,
      "slug": "sqlite",
      "text": "SQLite"
    },
    {
      "depth": 2,
      "slug": "postgres",
      "text": "Postgres"
    },
    {
      "depth": 3,
      "slug": "connection-pooling",
      "text": "Connection pooling"
    },
    {
      "depth": 2,
      "slug": "s3",
      "text": "S3"
    },
    {
      "depth": 2,
      "slug": "p2p",
      "text": "P2P"
    },
    {
      "depth": 2,
      "slug": "custom-adapters",
      "text": "Custom adapters"
    }
  ],
  "body_markdown": "# Storage Adapters\n\nPersist uses pluggable adapters for storage backends. Every adapter exposes the same API (`store.records`, `store.kv`, `store.blobs`, `store.events`), so you can swap backends without changing application code.\n\n## SQLite\n\nThe default adapter. Ideal for local-first apps, development, and mobile.\n\n```ts\nimport { createStore } from \"@cuitty/persist\";\n\nconst store = await createStore({\n  name: \"my-app\",\n  adapter: \"sqlite\",\n  path: \"./data/my-app.db\",\n});\n```\n\nSQLite stores are single-file, need no external process, and support WAL mode for concurrent reads out of the box.\n\n## Postgres\n\nProduction-grade durability with full SQL support. Best for server-side workloads.\n\n```ts\nconst store = await createStore({\n  name: \"my-app\",\n  adapter: \"postgres\",\n  connection: {\n    host: \"localhost\",\n    port: 5432,\n    database: \"persist\",\n    user: \"persist\",\n    password: process.env.PG_PASSWORD,\n  },\n});\n```\n\n### Connection pooling\n\nPass `pool: { max: 20 }` to control the connection pool size. The default is 10.\n\n## S3\n\nBlob storage adapter for large files and binary data.\n\n```ts\nconst store = await createStore({\n  name: \"my-app\",\n  adapter: \"s3\",\n  bucket: \"my-persist-bucket\",\n  region: \"us-east-1\",\n  credentials: {\n    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n  },\n});\n```\n\nThe S3 adapter maps `store.blobs` operations to S3 objects and `store.records` to JSON objects stored under a configurable prefix.\n\n## P2P\n\nPeer-to-peer replication with no central server. Peers discover each other and sync directly.\n\n```ts\nconst store = await createStore({\n  name: \"my-app\",\n  adapter: \"p2p\",\n  discovery: {\n    method: \"mdns\",         // or \"dht\" for wide-area\n    topic: \"my-app-sync\",\n  },\n});\n```\n\nThe P2P adapter uses CRDTs internally to merge writes from multiple peers without conflicts.\n\n## Custom adapters\n\nImplement the `PersistAdapter` interface to build your own backend.\n\n```ts\nimport type { PersistAdapter } from \"@cuitty/persist\";\n\nconst myAdapter: PersistAdapter = {\n  async get(key: string) { /* ... */ },\n  async put(key: string, value: unknown) { /* ... */ },\n  async delete(key: string) { /* ... */ },\n  async list(prefix: string) { /* ... */ },\n};\n\nconst store = await createStore({\n  name: \"my-app\",\n  adapter: myAdapter,\n});\n```\n\nAll four methods must return promises. `list` returns an async iterable of `{ key, value }` pairs.",
  "body_html": "<h1 id=\"storage-adapters\">Storage Adapters</h1>\n<p>Persist uses pluggable adapters for storage backends. Every adapter exposes the same API (<code>store.records</code>, <code>store.kv</code>, <code>store.blobs</code>, <code>store.events</code>), so you can swap backends without changing application code.</p>\n<h2 id=\"sqlite\">SQLite</h2>\n<p>The default adapter. Ideal for local-first apps, development, and mobile.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"ts\"><code><span class=\"line\"><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> { createStore } </span><span style=\"color:#F97583\">from</span><span style=\"color:#9ECBFF\"> \"@cuitty/persist\"</span><span style=\"color:#E1E4E8\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> store</span><span style=\"color:#F97583\"> =</span><span style=\"color:#F97583\"> await</span><span style=\"color:#B392F0\"> createStore</span><span style=\"color:#E1E4E8\">({</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  name: </span><span style=\"color:#9ECBFF\">\"my-app\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  adapter: </span><span style=\"color:#9ECBFF\">\"sqlite\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  path: </span><span style=\"color:#9ECBFF\">\"./data/my-app.db\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">});</span></span></code></pre>\n<p>SQLite stores are single-file, need no external process, and support WAL mode for concurrent reads out of the box.</p>\n<h2 id=\"postgres\">Postgres</h2>\n<p>Production-grade durability with full SQL support. Best for server-side workloads.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"ts\"><code><span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> store</span><span style=\"color:#F97583\"> =</span><span style=\"color:#F97583\"> await</span><span style=\"color:#B392F0\"> createStore</span><span style=\"color:#E1E4E8\">({</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  name: </span><span style=\"color:#9ECBFF\">\"my-app\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  adapter: </span><span style=\"color:#9ECBFF\">\"postgres\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  connection: {</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    host: </span><span style=\"color:#9ECBFF\">\"localhost\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    port: </span><span style=\"color:#79B8FF\">5432</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    database: </span><span style=\"color:#9ECBFF\">\"persist\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    user: </span><span style=\"color:#9ECBFF\">\"persist\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    password: process.env.</span><span style=\"color:#79B8FF\">PG_PASSWORD</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  },</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">});</span></span></code></pre>\n<h3 id=\"connection-pooling\">Connection pooling</h3>\n<p>Pass <code>pool: { max: 20 }</code> to control the connection pool size. The default is 10.</p>\n<h2 id=\"s3\">S3</h2>\n<p>Blob storage adapter for large files and binary data.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"ts\"><code><span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> store</span><span style=\"color:#F97583\"> =</span><span style=\"color:#F97583\"> await</span><span style=\"color:#B392F0\"> createStore</span><span style=\"color:#E1E4E8\">({</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  name: </span><span style=\"color:#9ECBFF\">\"my-app\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  adapter: </span><span style=\"color:#9ECBFF\">\"s3\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  bucket: </span><span style=\"color:#9ECBFF\">\"my-persist-bucket\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  region: </span><span style=\"color:#9ECBFF\">\"us-east-1\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  credentials: {</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    accessKeyId: process.env.</span><span style=\"color:#79B8FF\">AWS_ACCESS_KEY_ID</span><span style=\"color:#F97583\">!</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    secretAccessKey: process.env.</span><span style=\"color:#79B8FF\">AWS_SECRET_ACCESS_KEY</span><span style=\"color:#F97583\">!</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  },</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">});</span></span></code></pre>\n<p>The S3 adapter maps <code>store.blobs</code> operations to S3 objects and <code>store.records</code> to JSON objects stored under a configurable prefix.</p>\n<h2 id=\"p2p\">P2P</h2>\n<p>Peer-to-peer replication with no central server. Peers discover each other and sync directly.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"ts\"><code><span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> store</span><span style=\"color:#F97583\"> =</span><span style=\"color:#F97583\"> await</span><span style=\"color:#B392F0\"> createStore</span><span style=\"color:#E1E4E8\">({</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  name: </span><span style=\"color:#9ECBFF\">\"my-app\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  adapter: </span><span style=\"color:#9ECBFF\">\"p2p\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  discovery: {</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    method: </span><span style=\"color:#9ECBFF\">\"mdns\"</span><span style=\"color:#E1E4E8\">,         </span><span style=\"color:#6A737D\">// or \"dht\" for wide-area</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    topic: </span><span style=\"color:#9ECBFF\">\"my-app-sync\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  },</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">});</span></span></code></pre>\n<p>The P2P adapter uses CRDTs internally to merge writes from multiple peers without conflicts.</p>\n<h2 id=\"custom-adapters\">Custom adapters</h2>\n<p>Implement the <code>PersistAdapter</code> interface to build your own backend.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"ts\"><code><span class=\"line\"><span style=\"color:#F97583\">import</span><span style=\"color:#F97583\"> type</span><span style=\"color:#E1E4E8\"> { PersistAdapter } </span><span style=\"color:#F97583\">from</span><span style=\"color:#9ECBFF\"> \"@cuitty/persist\"</span><span style=\"color:#E1E4E8\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> myAdapter</span><span style=\"color:#F97583\">:</span><span style=\"color:#B392F0\"> PersistAdapter</span><span style=\"color:#F97583\"> =</span><span style=\"color:#E1E4E8\"> {</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">  async</span><span style=\"color:#B392F0\"> get</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#FFAB70\">key</span><span style=\"color:#F97583\">:</span><span style=\"color:#79B8FF\"> string</span><span style=\"color:#E1E4E8\">) { </span><span style=\"color:#6A737D\">/* ... */</span><span style=\"color:#E1E4E8\"> },</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">  async</span><span style=\"color:#B392F0\"> put</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#FFAB70\">key</span><span style=\"color:#F97583\">:</span><span style=\"color:#79B8FF\"> string</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#FFAB70\">value</span><span style=\"color:#F97583\">:</span><span style=\"color:#79B8FF\"> unknown</span><span style=\"color:#E1E4E8\">) { </span><span style=\"color:#6A737D\">/* ... */</span><span style=\"color:#E1E4E8\"> },</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">  async</span><span style=\"color:#B392F0\"> delete</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#FFAB70\">key</span><span style=\"color:#F97583\">:</span><span style=\"color:#79B8FF\"> string</span><span style=\"color:#E1E4E8\">) { </span><span style=\"color:#6A737D\">/* ... */</span><span style=\"color:#E1E4E8\"> },</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">  async</span><span style=\"color:#B392F0\"> list</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#FFAB70\">prefix</span><span style=\"color:#F97583\">:</span><span style=\"color:#79B8FF\"> string</span><span style=\"color:#E1E4E8\">) { </span><span style=\"color:#6A737D\">/* ... */</span><span style=\"color:#E1E4E8\"> },</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">const</span><span style=\"color:#79B8FF\"> store</span><span style=\"color:#F97583\"> =</span><span style=\"color:#F97583\"> await</span><span style=\"color:#B392F0\"> createStore</span><span style=\"color:#E1E4E8\">({</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  name: </span><span style=\"color:#9ECBFF\">\"my-app\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  adapter: myAdapter,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">});</span></span></code></pre>\n<p>All four methods must return promises. <code>list</code> returns an async iterable of <code>{ key, value }</code> pairs.</p>",
  "links_out": []
}