Skip to content

KV Storage

KV (key-value) storage is the default storage type for Vulcan apps. It's fast, simple, and handles the vast majority of use cases with no hassle.


What it is

Think of KV like a giant dictionary. Your app stores named items (keys) with associated data (values). It's great for:

  • Lists of records (tasks, contacts, tickets, inventory items)
  • App settings and configuration
  • Per-user data
  • Anything where you're reading and writing individual items

When to use KV

Use KV when:

  • You're building an app where users add, view, edit, and delete records
  • You don't need complex multi-field queries or joins
  • You want fast setup with no schema design

Use SQL instead when:

  • You need to filter, sort, or aggregate across many fields at once
  • You're building reports or analytics features
  • The data is highly relational (lots of linked tables)

Learn about SQL storage


How to add it to your app

Just tell the AI:

"Store the form submissions in KV" "I need this data to persist between page loads" "Add KV storage to save and retrieve records"

The AI will provision a KV namespace and wire it up for you. You'll see it referenced as KV in your app's configuration.


Seed data and migrations

When the AI sets up KV storage for a new app, it creates a small migrations system inside src/migrations/. Migrations seed your app with example data so it doesn't look empty on first load — and they run exactly once, so they never overwrite data you've already added.

File structure

src/migrations/
  runner.ts          ← Migration engine (don't touch this)
  index.ts           ← Migration registry — import and register all migrations here
  001-seed-data.ts   ← First migration: seeds initial example data
  002-add-items.ts   ← Second migration (added later if needed)
  ...

How it works

  1. On the first request to your app, runMigrations() is called automatically from data.ts
  2. It reads a migrations:applied key in your KV namespace to see which migrations have already run
  3. Any migration not in that list is run in order
  4. After each migration succeeds, its ID is added to migrations:applied so it never runs again

This means migrations are lazy — they run on demand, not at deploy time.

Adding or changing seed data

Never edit an existing migration file. Once a migration has run, its ID is recorded in KV and the file is never executed again. Changing the file has no effect.

To add or update data, ask the AI:

"Add 5 more sample contacts to the seed data" "Seed the app with some example inventory items"

The AI will create a new numbered migration file (e.g. 002-add-contacts.ts) and register it at the end of src/migrations/index.ts. On the next request, only that new migration runs — existing data is untouched.

Manual reset (dev / testing)

If you want to wipe and re-seed your data during development, delete the migrations:applied key from your KV namespace. All migrations will run again on the next request.

You can ask the AI to do this:

"Reset the seed data so I can start fresh"


Limits

  • Individual values have a maximum size of 25 MB
  • KV is optimized for reads; heavy write workloads may be better suited to SQL
  • Not ideal for data that needs complex relational queries

When KV starts to break down

KV works well for most internal tools, but there are patterns that signal it's time to switch to SQL:

  • Large growing lists — if your app stores records as a single JSON array in one key (common in AI-generated apps), that array grows with every record. Once it reaches thousands of entries, reads and writes become slow and you risk hitting the 25 MB value limit. Switch to SQL, or ask the AI to paginate by splitting records across multiple keys.
  • Filtering and sorting — KV has no query layer. If your app needs "show all orders from last week" or "sort contacts by last name", you're loading everything into memory and filtering in code. SQL handles this far more efficiently.
  • Concurrent writes — KV uses last-writer-wins. If two users submit a form at the same moment, one write can silently overwrite the other. For high-traffic write paths, use SQL instead.
  • Running reports or aggregates — counts, sums, averages across many records should live in SQL.

If you're not sure which to use, just ask the AI — it will pick the right storage type based on your description.

Learn about SQL storage

Built by the Veho Developer Platform team