Skip to content

Repository

A repository is a data access object that provides an abstraction over the underlying data source. BotKit provides a few built-in repositories that can be used to interact with the database, but you can also create your own repositories to interact with other data sources.

Bot-scoped storage

Since BotKit 0.5.0, a single repository stores the data of every bot hosted on an instance: every Repository method takes the identifier of the owning bot actor as its first parameter, and data belonging to different identifiers are isolated from each other. The Repository.forIdentifier() method returns an ActorScopedRepository, a view of the repository bound to one bot actor, which exposes the same operations without the identifier parameter.

Custom Repository implementations written for BotKit 0.4 or earlier need to be updated to the new method signatures. Besides the added parameter, two methods joined the interface: ~Repository.findFollowedBots(), a reverse lookup answering which bots follow a given actor (used for routing incoming messages to the right bots), and the optional ~Repository.migrate(), which adopts data stored by BotKit 0.4 or earlier for a bot actor identifier. The built-in repositories migrate legacy data automatically when the bot is created through createBot().

FEP-044f quote support also adds quote authorization storage methods and quote authorization reference methods. The reference methods map a received QuoteAuthorization stamp URI back to the local quote message that depends on it, so BotKit can update that message when the remote author later changes the quote's authorization state.

KvRepository

It is the default repository provided by BotKit. If you omit the repository option of the createBot() function, BotKit will use the KvRepository by default.

The KvRepository is a repository that stores data in a key–value store through the KvStore interface, which is provided by the Fedify. Since the KvStore interface itself also abstracts over the underlying data source, you can easily switch between different key–value stores without changing the repository implementation.

There are several KvStore implementations available in the Fedify:

RedisKvStore

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.

NOTE

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

PostgresKvStore

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.

NOTE

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

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.

NOTE

The DenoKvStore class is available in x/deno module of the @fedify/fedify package.

MemoryKvStore

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.

TIP

Although MemoryKvStore is provided by Fedify, BotKit also re-exports it for convenience.

SqliteRepository

This API is available since BotKit 0.3.0.

The SqliteRepository is a repository that stores data in a SQLite database using the node:sqlite module. It provides a production-ready storage solution with excellent performance and reliability, while maintaining compatibility with both Deno and Node.js environments.

Unlike KvRepository which requires a separate key–value store setup, SqliteRepository can operate with either an in-memory database for development/testing or a file-based database for production use. It offers ACID compliance through transactions, write-ahead logging (WAL) mode for optimal performance, and proper indexing for efficient data retrieval.

In order to use SqliteRepository, you need to install the @fedify/botkit-sqlite package:

deno add jsr:@fedify/botkit-sqlite
npm add @fedify/botkit-sqlite
pnpm add @fedify/botkit-sqlite
yarn add @fedify/botkit-sqlite

The SqliteRepository constructor accepts an options object with the following properties:

path (optional)
Path to the SQLite database file. Defaults to ":memory:" for an in-memory database. Use a file path for persistent storage.
wal (optional)
Whether to enable write-ahead logging (WAL) mode for better performance. Defaults to true for file-based databases. WAL mode is not applicable for in-memory databases.

RedisRepository

This API is available since BotKit 0.5.0.

The RedisRepository is a repository that stores data in Redis using the node-redis client. It is suited for deployments where multiple bot processes need to share the same repository state, or where you already operate Redis for other BotKit infrastructure.

Unlike KvRepository, RedisRepository stores BotKit data directly in Redis data structures such as strings, sets, and sorted sets. It supports either an internally owned Redis client created from a connection URL or an injected client whose lifecycle stays under your control.

In order to use RedisRepository, you need to install the @fedify/botkit-redis package:

deno add jsr:@fedify/botkit-redis
npm add @fedify/botkit-redis
pnpm add @fedify/botkit-redis
yarn add @fedify/botkit-redis

The RedisRepository constructor accepts the following properties:

url
A Redis connection string used to create an internal client. Exactly one of url and client must be provided.
client
An existing node-redis compatible client. When this is provided, the repository does not own the client and calling close() will not shut it down.
prefix (optional)
The Redis key prefix used for BotKit data. Defaults to "botkit".
clientOptions (optional)
Additional node-redis client options. This option is only valid when url is used.
lockTimeoutMs (optional)
How long a Redis lock can live without renewal, in milliseconds. Defaults to 30000.
lockPollIntervalMs (optional)
How long to wait before retrying a held Redis lock, in milliseconds. Defaults to 20.
lockRenewIntervalMs (optional)
How often to renew a held Redis lock, in milliseconds. Defaults to 10000.

PostgresRepository

This API is available since BotKit 0.4.0.

The PostgresRepository is a repository that stores data in PostgreSQL using the Postgres.js driver. It is suited for deployments where multiple bot processes need to share the same persistent repository state, or where you already operate PostgreSQL for other infrastructure.

Unlike KvRepository, PostgresRepository stores BotKit data in ordinary PostgreSQL tables rather than a key-value abstraction. It creates tables inside a dedicated PostgreSQL schema, uses transactions for multi-step updates, and supports either an internally owned connection pool or an injected Postgres.js client.

In order to use PostgresRepository, you need to install the @fedify/botkit-postgres package:

deno add jsr:@fedify/botkit-postgres
npm add @fedify/botkit-postgres
pnpm add @fedify/botkit-postgres
yarn add @fedify/botkit-postgres

The PostgresRepository constructor accepts an options object with the following properties:

sql
An existing Postgres.js client. When this is provided, the repository does not own the client and calling close() will not shut it down.
url
A PostgreSQL connection string used to create an internal connection pool. Exactly one of sql and url must be provided.
schema (optional)
The PostgreSQL schema used for BotKit tables. Defaults to "botkit".
maxConnections (optional)
The maximum number of connections for the internally created pool. This option is only valid when url is used.
prepare (optional)
Whether to use prepared statements for repository queries. Defaults to true.

These options are mutually exclusive: use either sql or url. The maxConnections option is only meaningful together with url.

The repository initializes its tables and indexes automatically. If you want to provision them before creating the repository, use the exported initializePostgresRepositorySchema() helper:

import postgres from "postgres";
import { initializePostgresRepositorySchema } from "@fedify/botkit-postgres";

const sql = postgres("postgresql://localhost/botkit");
await initializePostgresRepositorySchema(sql, "botkit");

If you disable prepared statements for PgBouncer-style deployments, pass false as the third argument so schema initialization uses the same setting.

MemoryRepository

The MemoryRepository is a repository that stores data in memory, which means that data is not persisted across restarts. 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.

TIP

How does it differ from using KvRepository with MemoryKvStore?—In practice, there's no difference between using MemoryRepository and KvRepository with MemoryKvStore. The only differences are that MemoryRepository is a more convenient way to use MemoryKvStore and that it's slightly more efficient because it doesn't have to go through the KvStore interface.

MemoryCachedRepository

This API is available since BotKit 0.3.0.

The MemoryCachedRepository is a repository decorator that adds an in-memory cache layer on top of another repository. This is useful for improving performance by reducing the number of accesses to the underlying persistent storage, but it increases memory usage. The cache is not persistent and will be lost when the process exits.

It takes an existing Repository instance (like KvRepository or even another MemoryCachedRepository) and wraps it. Write operations are performed on both the underlying repository and the cache. Read operations first check the cache; if the data is found, it's returned directly. Otherwise, the data is fetched from the underlying repository, stored in the cache, and then returned.

NOTE

List operations like getMessages and getFollowers, and count operations like countMessages and countFollowers are not cached due to the complexity of handling various filtering and pagination options. These operations always delegate directly to the underlying repository.

Implementing a custom repository

You can create your own repository by implementing the Repository interface. The Repository interface is a generic interface that defines the basic CRUD (create, read, update, delete) operations for data access.

The Repository interface consists of the following five domains of operations in general:

Key pairs

Key pairs are the singleton data that are used for the bot actor. At the surface level, the key pairs are represented as CryptoKeyPair objects, but you would typically store them as JWK.

TIP

Fedify provides exportJwk() and importJwk() functions to convert a CryptoKey object to a JWK object and vice versa.

Messages

Messages are the data of the messages that are published by the bot. Remote messages received from the remote server are not included in this domain. Each message has its own UUID and represented by either a Create object or an Announce object (which both are provided by Fedify).

You probably want to serialize the messages into JSON before storing them, so you can use toJsonLd() method that belong to the Create and Announce objects, and use fromJsonLd() method to deserialize them.

Followers

Followers are the data of the actors that follow the bot. Each follower is represented by Actor object (provided by Fedify) and is associated with a follow ID, a URI of the Follow activity.

Similar to messages, you can serialize the Actor object into JSON using the toJsonLd() method, and deserialize it using the fromJsonLd() method.

CAUTION

The Repository.hasFollower() method takes the URI of an Actor, not the follow ID.

Sent follows

Sent follows are the data of the follow requests that the bot has sent. Each sent follow is represented by a Follow object (provided by Fedify) and is associated with its own UUID.

Similar to messages and followers, you can serialize the Follow object into JSON using the toJsonLd() method, and deserialize it using the fromJsonLd() method.

Followees

Followees are the data of the actors that the bot follows. Each followee is represented by a Follow object (provided by Fedify) instead of Actor, and associated with an actor ID (not a follow ID).

Similar to messages, followers, and sent follows, you can serialize the Follow object into JSON using the toJsonLd() method, and deserialize it using the fromJsonLd() method.