# 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](https://unstorage.unjs.io) to provide a runtime agnostic persistent layer.

## Usage

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

```ts
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 to="https://unstorage.unjs.io">



</read-more>

### Available methods

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

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

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

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

```ts
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.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

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

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

<read-more to="https://unstorage.unjs.io/">

You can find the driver list on [unstorage documentation](https://unstorage.unjs.io/) with their configuration.

</read-more>

### 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).

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  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](#server-assets)).

```ts
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.

```ts
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.

```text
my-project/
  assets/
    data.json
    templates/
      welcome.html
  server/
    routes/
      index.ts
```

```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:

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

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

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

```ts
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:

```ts
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");
```

<note>

In development, server assets are read directly from the filesystem. In production, they are bundled and inlined into the build output.

</note>

## Runtime configuration

In scenarios where the mount point configuration is not known until runtime, Nitro can dynamically add mount points during startup using [plugins](/docs/plugins).

```ts [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)
})
```

<warning>

This is a temporary workaround, with a better solution coming in the future! Keep a lookout on the GitHub issue [here](https://github.com/nitrojs/nitro/issues/1161#issuecomment-1511444675).

</warning>
