Nitro logoNitro

KV Storage

Nitro provides a built-in storage layer that can abstract filesystem or database or any other data source.

Nitro has built-in integration with unstorage to provide a runtime agnostic persistent layer.

Usage

To use the storage layer, you can use the useStorage() utility to access the storage instance.

import { useStorage } from "nitro/storage";

// Default storage (in-memory)
await useStorage().setItem("test:foo", { hello: "world" });
const value = await useStorage().getItem("test:foo");

// You can specify a base prefix with useStorage(base)
const testStorage = useStorage("test");
await testStorage.setItem("foo", { hello: "world" });
await testStorage.getItem("foo"); // { hello: "world" }

// You can use generics to type the return value
await useStorage<{ hello: string }>("test").getItem("foo");
await useStorage("test").getItem<{ hello: string }>("foo");
Read more in unstorage.unjs.io.

Available methods

The storage instance returned by useStorage() provides the following methods:

MethodDescription
getItem(key)Get the value of a key. Returns null if the key does not exist.
getItems(items)Get multiple items at once. Accepts an array of keys or { key, options } objects.
getItemRaw(key)Get the raw value of a key without parsing. Useful for binary data.
setItem(key, value)Set the value of a key.
setItems(items)Set multiple items at once. Accepts an array of { key, value } objects.
setItemRaw(key, value)Set the raw value of a key without serialization.
hasItem(key)Check if a key exists. Returns a boolean.
removeItem(key)Remove a key from storage.
getKeys(base?)Get all keys, optionally filtered by a base prefix.
clear(base?)Clear all keys, optionally filtered by a base prefix.
getMeta(key)Get metadata for a key (e.g., mtime, atime, ttl).
setMeta(key, meta)Set metadata for a key.
removeMeta(key)Remove metadata for a key.
mount(base, driver)Dynamically mount a storage driver at a base path.
unmount(base)Unmount a storage driver from a base path.
watch(callback)Watch for changes. Callback receives (event, key) where event is "update" or "remove".
unwatch()Stop watching for changes.

Shorthand aliases are also available: get, set, has, del, remove, keys.

import { useStorage } from "nitro/storage";

// Get all keys under a prefix
const keys = await useStorage("test").getKeys();

// Check if a key exists
const exists = await useStorage().hasItem("test:foo");

// Remove a key
await useStorage().removeItem("test:foo");

// Get raw binary data
const raw = await useStorage().getItemRaw("assets/server:image.png");

// Get metadata (type, etag, mtime, etc.)
const meta = await useStorage("assets/server").getMeta("file.txt");

Configuration

You can mount one or multiple custom storage drivers using the storage option.

The key is the mount point name, and the value is the driver name and configuration.

nitro.config.ts
import { defineNitroConfig } from "nitro/config";

export default defineNitroConfig({
  storage: {
    redis: {
      driver: "redis",
      /* redis connector options */
    }
  }
})

Then, you can use the redis storage using the useStorage("redis") function.

You can find the driver list on unstorage documentation with their configuration.

Development storage

You can use the devStorage option to override storage configuration during development and prerendering.

This is useful when your production driver is not available in development (e.g., a managed Redis instance).

nitro.config.ts
import { defineNitroConfig } from "nitro/config";

export default defineNitroConfig({
  storage: {
    db: {
      driver: "redis",
      host: "prod.example.com",
    }
  },
  devStorage: {
    db: {
      driver: "fs",
      base: "./.data/db"
    }
  }
})

When running in development mode, devStorage mounts are merged on top of storage mounts, allowing you to use a local filesystem driver or an in-memory driver while developing.

Built-in mount points

Nitro automatically mounts the following storage paths:

/assets

Server assets are mounted at the /assets base path. This mount point provides read-only access to bundled server assets (see Server assets).

import { useStorage } from "nitro/storage";

// Access server assets via the /assets mount
const content = await useStorage("assets/server").getItem("my-file.txt");

Default (in-memory)

The root storage (without a base path) uses an in-memory driver by default. Data stored here is not persisted across restarts.

import { useStorage } from "nitro/storage";

// In-memory by default, not persisted
await useStorage().setItem("counter", 1);

To persist data, mount a driver with a persistent backend (e.g., fs, redis, etc.) using the storage configuration option.

Server assets

Nitro allows you to bundle files from an assets/ directory at the root of your project. These files are accessible at runtime via the assets/server storage mount.

my-project/
  assets/
    data.json
    templates/
      welcome.html
  server/
    routes/
      index.ts
server/routes/index.ts
import { useStorage } from "nitro/storage";

export default defineHandler(async () => {
  const serverAssets = useStorage("assets/server");

  const keys = await serverAssets.getKeys();
  const data = await serverAssets.getItem("data.json");
  const template = await serverAssets.getItem("templates/welcome.html");

  return { keys, data, template };
});

Custom asset directories

You can register additional asset directories using the serverAssets config option:

nitro.config.ts
import { defineNitroConfig } from "nitro/config";

export default defineNitroConfig({
  serverAssets: [
    {
      baseName: "templates",
      dir: "./templates",
    }
  ]
})

Custom asset directories are accessible under assets/<baseName>:

import { useStorage } from "nitro/storage";

const templates = useStorage("assets/templates");
const keys = await templates.getKeys();
const html = await templates.getItem("email.html");

Asset metadata

Server assets include metadata such as content type, ETag, and modification time:

import { useStorage } from "nitro/storage";

const serverAssets = useStorage("assets/server");

const meta = await serverAssets.getMeta("image.png");
// { type: "image/png", etag: "\"...\"", mtime: "2024-01-01T00:00:00.000Z" }

// Useful for setting response headers
const raw = await serverAssets.getItemRaw("image.png");
In development, server assets are read directly from the filesystem. In production, they are bundled and inlined into the build output.

Runtime configuration

In scenarios where the mount point configuration is not known until runtime, Nitro can dynamically add mount points during startup using plugins.

plugins/storage.ts
import { useStorage } from "nitro/storage";
import { definePlugin } from "nitro";
import redisDriver from "unstorage/drivers/redis";

export default definePlugin(() => {
  const storage = useStorage()

  // Dynamically pass in credentials from runtime configuration, or other sources
  const driver = redisDriver({
    base: "redis",
    host: process.env.REDIS_HOST,
    port: process.env.REDIS_PORT,
    /* other redis connector options */
  })

  // Mount driver
  storage.mount("redis", driver)
})
This is a temporary workaround, with a better solution coming in the future! Keep a lookout on the GitHub issue here.