Skip to content

Key–value store

This API is available since Fedify 0.5.0.

The KvStore interface is a crucial component in Fedify, providing a flexible key–value storage solution for caching and maintaining internal data. This guide will help you choose the right KvStore implementation for your project and even create your own custom implementation if needed.

Choosing a KvStore implementation

Fedify offers several KvStore implementations to suit different needs.

Choose the implementation that best fits your project's requirements, considering factors like scalability, runtime environment, and distributed nature of your system.

MemoryKvStore

MemoryKvStore is a simple in-memory key–value store that doesn't persist data. It's best suited for development and testing environments where data don't have to be shared across multiple nodes. No setup is required, making it easy to get started.

Best for
Development and testing.
Pros
Simple, no setup required.
Cons
Data is not persistent, not suitable for production.
typescript
import { 
createFederation
,
MemoryKvStore
} from "@fedify/fedify";
const
federation
=
createFederation
<void>({
kv
: new
MemoryKvStore
(),
// ... other options });

DenoKvStore (Deno only)

DenoKvStore is a key–value store implementation for Deno runtime that uses Deno's built-in Deno.openKv() API. It provides persistent storage and good performance for Deno environments. It's suitable for production use in Deno applications.

Best for
Production use in Deno environments.
Pros
Persistent storage, good performance, easy to set up.
Cons
Only available in Deno runtime.
typescript
import { createFederation } from "@fedify/fedify";
import { DenoKvStore } from "@fedify/fedify/x/deno";

const kv = await Deno.openKv();
const federation = createFederation<void>({
  kv: new DenoKvStore(kv),
  // ... other options
});

RedisKvStore

NOTE

The RedisKvStore class is available in the @fedify/redis package.

RedisKvStore is a key–value store implementation that uses Redis as the backend storage. It provides scalability and high performance, making it suitable for production use in distributed systems. It requires a Redis server setup and maintenance.

Best for
Production use, distributed systems.
Pros
Scalable, supports clustering.
Cons
Requires Redis setup and maintenance.
typescript
import { 
createFederation
} from "@fedify/fedify";
import {
RedisKvStore
} from "@fedify/redis";
import
Redis
from "ioredis";
const
redis
= new
Redis
(); // Configure as needed
const
federation
=
createFederation
<void>({
kv
: new
RedisKvStore
(
redis
),
// ... other options });

PostgresKvStore

NOTE

The PostgresKvStore class is available in the @fedify/postgres package.

PostgresKvStore is a key–value store implementation that uses PostgreSQL as the backend storage. It provides scalability and high performance, making it suitable for production use in distributed systems. It requires a PostgreSQL server setup and maintenance.

Best for
Production use, a system that already uses PostgreSQL.
Pros
Scalable, no additional setup required if already using PostgreSQL.
Cons
Requires PostgreSQL setup and maintenance.
typescript
import { 
createFederation
} from "@fedify/fedify";
import {
PostgresKvStore
} from "@fedify/postgres";
import
postgres
from "postgres";
const
federation
=
createFederation
<void>({
kv
: new
PostgresKvStore
(
postgres
("postgresql://user:pass@localhost/db"),
), // ... other options });

Implementing a custom KvStore

TIP

We are always looking to improve Fedify and add more KvStore implementations. If you've created a custom implementation that you think would be useful to others, consider contributing it to the community by packaging it as a standalone module and sharing it on JSR and npm.

If the provided implementations don't meet your needs, you can create a custom KvStore. Here's a step-by-step guide:

Implement the KvStore interface

Create a class that implements the KvStore interface. The interface defines three methods: get(), set(), and delete():

typescript
import { KvStore, 
KvKey
, KvStoreSetOptions } from "@fedify/fedify";
class
MyCustomKvStore
implements KvStore {
async
get
<
T
= unknown>(
key
:
KvKey
):
Promise
<
T
| undefined> {
// Implement get logic } async
set
(
key
:
KvKey
,
value
: unknown,
options
?: KvStoreSetOptions
):
Promise
<void> {
// Implement set logic } async
delete
(
key
:
KvKey
):
Promise
<void> {
// Implement delete logic } }

Handle KvKey

The KvKey is an array of strings. You'll need to convert it into a format suitable for your storage backend. For example:

typescript
private 
serializeKey
(
key
:
KvKey
): string {
return
key
.
join
(':');
}

NOTE

The above example uses a simple colon-separated string as the serialized key, but in practice, it probably needs to be more sophisticated to handle complex keys and avoid conflicts.

Implement get() method

Retrieve the value associated with the key. Remember to handle cases where the key doesn't exist:

typescript
async 
get
<
T
= unknown>(
key
:
KvKey
):
Promise
<
T
| undefined> {
const
serializedKey
= this.
serializeKey
(
key
);
// Retrieve value from your storage backend const
value
= await this.
storage
.
retrieve
(
serializedKey
);
return
value
as
T
| undefined;
}

Implement set() method

Store the value with the given key. Handle the optional TTL if your backend supports it:

typescript
async 
set
(
key
:
KvKey
,
value
: unknown,
options
?: KvStoreSetOptions,
):
Promise
<void> {
const
serializedKey
= this.
serializeKey
(
key
);
if (
options
?.
ttl
== null) {
await this.
storage
.
set
(
serializedKey
,
value
);
} else { // Set with TTL if supported await this.
storage
.
setWithTtl
(
serializedKey
,
value
,
options
.
ttl
.
total
('millisecond'),
); } }

Implement delete() method

Remove the value associated with the key:

typescript
async 
delete
(
key
:
KvKey
):
Promise
<void> {
const
serializedKey
= this.
serializeKey
(
key
);
await this.
storage
.
remove
(
serializedKey
);
}

Use your custom KvStore

That's it! You can now use your custom KvStore implementation with Fedify:

typescript
import { 
createFederation
} from "@fedify/fedify";
const
customKvStore
= new
MyCustomKvStore
();
const
federation
=
createFederation
<void>({
kv
:
customKvStore
,
// ... other options });