Skip to content

miftahDB/miftahdb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tgram

MiftahDB

Fast and lightweight key-value database library.

NPM Version NPM Type Definitions NPM Downloads NPM License

Release Test-Bun Test-Node Lint

Documentation • NPM • Benchmarks

Contents

Features

  • Fast and efficient key-value storage
  • Support for expiration of keys
  • Disk and in-memory database support
  • Synchronous API for better performance and concurrency
  • Built on top of better-sqlite3 for optimal Node.js performance
  • Utilizes bun:sqlite for seamless Bun integration
  • Pattern-based key retrieval
  • Result type handling ( no try-catch blocks )
  • Supports both Bun and Node.js environments

Installation

# With NPM
npm install miftahdb

# With Bun
bun install miftahdb

Usage

// Node runtime
import { MiftahDB } from "miftahdb";

// For Bun runtime
import { MiftahDB } from "miftahdb/bun";

Example Usage

// Create a new disk-based database instance
const db = new MiftahDB("database.db");

// Use the database
db.set("user:1234", { name: "Ahmad Aburob" });
const user = db.get("user:1234");
console.log(user);

Synchronous API

MiftahDB uses a synchronous API, which may seem counterintuitive but actually provides better performance and concurrency than an asynchronous API for most use cases.

Error Handling

MiftahDB uses result types to handle errors. The result type includes a boolean indicating whether the operation was successful and the data returned by the operation, or an error if the operation failed.

const result = db.get("user:1234");
if (result.success) {
  console.log(`User: ${result.data}`);
} else {
  console.log(result.error.message);
}

API Reference

Constructor

Creates a new MiftahDB instance.

  • Parameters:
    • path: The path to the database file. Defaults to ":memory:" if not provided.
// New MiftahDB instance with disk-based database
const db = new MiftahDB("test.db");

// New MiftahDB instance with in-memory database
const db = new MiftahDB(":memory:");

Get

Retrieves a value from the database by its key.

  • Parameters:
    • key: The key to look up.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful and the value, or an error if the operation failed.
const result = db.get<User>("user:1234");
if (result.success) {
  console.log(`User: ${result.data.name}`);
} else {
  console.log(result.error.message);
}

Set

Sets a value in the database with an optional expiration.

  • Parameters:
    • key: The key under which to store the value.
    • value: The value to store.
    • expiresAt: Optional expiration date as a Date object or number of milliseconds.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
// Full example with result type handling
const result = db.set<User>("user:1234", { name: "Ahmad" });
if (result.success) {
  console.log("Key set successfully");
} else {
  console.log(result.error.message);
}

// Set a value with expiration in milliseconds
db.set("key", "value", 90000);

// Set a value with Date object expiration
db.set("key", "value", new Date("2030-12-31"));

Exists

Checks if a key exists in the database.

  • Parameters:
    • key: The key to check.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
if (db.exists("user:12345").success) {
  console.log("User exists");
} else {
  console.log("User does not exist");
}

Note: The exists method is faster than get because it uses a simpler SQL query. Use exists when you only need to check if a key is present, as it can boost performance in speed-sensitive situations.


Delete

Deletes a key-value pair from the database.

  • Parameters:
    • key: The key to delete.
  • Returns:
    • The result of the operation, includes a number indicating the number of rows affected by the operation or an error if the operation failed.
const result = db.delete("user:1234");
if (result.success) {
  console.log(`Deleted ${result.data} rows`);
} else {
  console.log(result.error.message);
}

Rename

Renames a key in the database.

  • Parameters:
    • oldKey: The current key name.
    • newKey: The new key name.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
if (db.rename("user:old_id", "user:new_id").success) {
  console.log("Key renamed successfully");
} else {
  console.log(result.error.message);
}

Get Expire

Gets the expiration date of a key.

  • Parameters:
    • key: The key to check.
  • Returns:
    • The result of the operation, includes the expiration date of the key or an error if the operation failed.
const result = db.getExpire("session:5678");
if (result.success) {
  console.log(`Expiration date: ${result.data}`);
} else {
  console.log(result.error.message);
}

Set Expire

Sets or update the expiration date of a key.

  • Parameters:
    • key: The key to set the expiration date for.
    • expiresAt: The expiration date to set as Date object or number of milliseconds.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
// Date object expiration
if (db.setExpire("user:1234", new Date("2028-12-31")).success) {
  console.log("Expiration date set successfully");
} else {
  console.log(result.error.message);
}

// Number of milliseconds expiration
if (db.setExpire("user:1234", 90000).success) {
  console.log("Expiration date set successfully");
} else {
  console.log(result.error.message);
}

Keys

Retrieves keys matching a pattern.

  • Parameters:
    • pattern: Optional SQL LIKE pattern to match keys. Defaults to "%" which matches all keys.
  • Returns:
    • The result of the operation, includes an array of matching keys or an error if the operation failed.
// Get all keys with result type handling
const result = db.keys();
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.message);
}

// Get all keys
const allKeys = db.keys();

// Get keys starting with "user:"
const userKeys = db.keys("user:%");

// Get keys with exactly 5 characters
const fiveCharKeys = db.keys("_____");

// Get keys starting with "log", followed by exactly two characters, and ending with any number of characters
const logKeys = db.keys("log__:%");

Pagination

Retrieves a paginated list of keys matching a pattern.

  • Parameters:
    • limit: The maximum number of keys to return per page.
    • page: The page number to retrieve (1-based index).
    • pattern: Optional SQL LIKE pattern to match keys. Defaults to "%" which matches all keys.
  • Returns:
    • The result of the operation, includes an array of keys that match the pattern or an error if the operation failed.
// Get the first 5 keys from the database with result type handling
const result = db.pagination(5, 1);
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.message);
}

// Get the first 5 keys from the database
const firstPage = db.pagination(5, 1);

// Get the first 10 keys with pattern
const firstUsersPage = db.pagination(10, 1, "user:%");

// Get the next 10 keys with pattern
const secondUsersPage = db.pagination(10, 2, "user:%");

Count

Counts the number of keys in the database.

  • Parameters:
    • pattern: Optional SQL LIKE pattern to match keys. Defaults to "%" which matches all keys.
  • Returns:
    • The result of the operation, includes the number of keys in the database or an error if the operation failed.
// Get the total number of keys with result type handling
const result = db.count();
if (result.success) {
  console.log(`Total keys: ${result.data}`);
} else {
  console.log(result.error.message);
}

// Get the number of keys matching "user:%"
const userCount = db.count("user:%");

Note: The count method is faster than using keys().length because it directly executes a SQL query optimized for counting rows. Use count when you need to determine the number of keys, as it provides better performance compared to fetching all keys and measuring their length.


Count Expired

Counts the number of expired keys in the database.

  • Parameters:
    • pattern: Optional SQL LIKE pattern to match keys. Defaults to "%" which matches all keys.
  • Returns:
    • The result of the operation, includes the number of expired keys in the database or an error if the operation failed.
// Get the total number of expired keys with result type handling
const result = db.countExpired();
if (result.success) {
  console.log(`Total expired keys: ${result.data}`);
} else {
  console.log(result.error.message);
}

// Get the number of expired keys matching "user:%"
const userCountExpired = db.countExpired("user:%");

Multi Get

Retrieves multiple values from the database by their keys.

  • Parameters:
    • keys: An array of keys to look up.
  • Returns:
    • The result of the operation, includes an array of values or an error if the operation failed.
const result = db.multiGet<User>(["user:1234", "user:5678"]);
if (result.success) {
  console.log(result.data[0].age);
} else {
  console.log(result.error.message);
}

Multi Set

Sets multiple key-value pairs in the database with optional expirations.

  • Parameters:
    • entries: An array of objects containing key, value, and optional expiresAt date as a Date object or number of milliseconds.
  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
const result = db.multiSet<User>([
  {
    key: "user:1234",
    value: { name: "Ahmad", age: 15 },
    expiresAt: new Date("2025-12-31"), // 1 year in Date object
  },
  {
    key: "user:5678",
    value: { name: "Fatima", age: 30 },
    expiresAt: 86400000, // 1 day in milliseconds
  },
  { key: "user:7890", value: { name: "Mohamed", age: 25 } }, // No expiration
]);

if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.message);
}

Multi Delete

Deletes multiple key-value pairs from the database.

  • Parameters:
    • keys: An array of keys to delete.
  • Returns:
    • The result of the operation, includes the number of rows affected by the operation or an error if the operation failed.
const result = db.multiDelete(["user:1234", "user:5678"]);
if (result.success) {
  console.log(`Deleted ${result.data} rows`);
} else {
  console.log(result.error.message);
}

Cleanup

Removes expired key-value pairs from the database.

  • Returns:
    • The result of the operation, includes the number of rows affected by the operation or an error if the operation failed.
const result = db.cleanup();
if (result.success) {
  console.log(`Cleaned up ${result.data} rows`);
} else {
  console.log(result.error.message);
}

Note: Regularly run the cleanup() method to optimize the database and reduce its size.


Vacuum

Optimizes the database file, reducing its size.

  • Returns:
    • The result of the operation, includes a boolean indicating whether the operation was successful or an error if the operation failed.
if (db.vacuum().success) {
  console.log("Database vacuumed successfully");
} else {
  console.log(result.error.message);
}

Note: Regularly run the cleanup() method to optimize the database and reduce its size.


Flush

Ensures all the changes are written to disk.

  • Returns:
    • The result of the operation, includes the number of rows affected by the operation or an error if the operation failed.
const result = db.flush();
if (result.success) {
  console.log(`Flushed ${result.data} rows`);
} else {
  console.log(result.error.message);
}

Namespace

Creates a namespaced database instance.

  • Parameters:
    • name: The name of the namespace.
  • Returns:
    • A new database instance with the namespace applied.
// Create a new database instance
const db = new MiftahDB(":memory:");

// Make a namespaced database instance
const users = db.namespace("users");
const posts = db.namespace("posts");
const comments = db.namespace("comments");

// Set/Get a value with a namespace
users.set("852335", { name: "Ahmad" });
console.log(users.get("852335"));

// Will count the keys only on the "users" namespace only
users.count();

// Will remove expired keys only on the "users" namespace only
users.cleanup();

// Will remove all keys only on the "users" namespace only
users.flush();

Execute

Executes a raw SQL statement and returns the result.

  • Parameters:
    • sql: The SQL statement to execute.
    • params: Optional parameters to bind to the SQL statement.
  • Returns:
    • The result of the operation, includes the result of the SQL query or an error if the operation failed.
// Execute a SELECT statement and get results
const result = db.execute("SELECT * FROM miftahdb WHERE key LIKE ? LIMIT 5;", [
  "%",
]);

if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.message);
}

Backup

Backups the database to a file asynchronously.

  • Parameters:
    • path: The path to where the backup should be saved.
const db = new MiftahDB(":memory:");

db.set("key", "value");

const result = await db.backup("backup-1.db");
if (result.success) {
  console.log("Backup completed successfully");
} else {
  console.log(result.error.message);
}

Restore

Restores the database from a backup file asynchronously.

  • Parameters:
    • path: The path to the backup file.
const db = new MiftahDB(":memory:");

const result = await db.restore("backup-1.db");
if (result.success) {
  console.log("Restore completed successfully");
} else {
  console.log(result.error.message);
}

console.log(db.get("key"));

Close

Closes the database connection.

db.close();

Supported Value Types

MiftahDB supports various value types:

No Type
1 String
2 Number
3 Boolean
4 Array
5 Record (Object)
6 Date
7 Buffer (Binary Data)
8 Uint8Array (Binary Data)
9 Null

Example for each type:

db.set("String", "Hello!");
db.set("Number", 42);
db.set("Boolean", true);
db.set("Array", [1, 2, 3, 4, 5]);
db.set("Record", { name: "Ahmad", age: 15 });
db.set("Date", new Date());
db.set("Buffer", Buffer.from([1, 2, 3, 4, 5]));
db.set("Uint8Array", new Uint8Array([1, 2, 3, 4, 5]));
db.set("Null", null);

Pattern Matching

MiftahDB provides powerful pattern matching capabilities for working with keys. You can use patterns in multiple methods, such as:

  • db.keys(pattern)
  • db.pagination(limit, page, pattern)
  • db.count(pattern)
  • db.countExpired(pattern)

The pattern syntax follows SQL-like wildcard matching:

  • %: Matches any sequence of characters (including none).
  • _: Matches exactly one character.
// Match keys starting with "user:"
db.keys("user:%");

// Match keys ending with "osama"
db.keys("%osama");

// Match keys starting with "osama" and ending with any number of characters
db.keys("osama%");

// Match keys that are exactly 3 characters long
db.keys("___");

// Combine patterns: Match keys starting with "log", followed by exactly two characters, and ending with any number of characters
db.keys("log__:%");

TypeScript Typing & Generics

MiftahDB is fully typed with TypeScript, allowing you to leverage TypeScript's static type checking and type inference. You can use generic types to specify the type of values stored and retrieved from the database.

When retrieving values from MiftahDB, you can define the type of the stored value for better type safety:

type User = {
  name: string;
  age: number;
};

// Set a value with TypeScript typing
db.set<User>("user:1234", {
  name: "Ahmad",
  age: 15,
});

// Retrieve the value with TypeScript typing
const getResult = db.get<User>("user:1234");
if (getResult.success) {
  console.log(`User: ${getResult.data.name}`);
} else {
  console.log(getResult.error.message);
}

// Multi-Set a value with TypeScript typing
db.multiSet<User>([
  {
    key: "user:2345",
    value: { name: "Ahmad", age: 15 },
    expiresAt: new Date("2025-12-31"),
  },
  { key: "user:7890", value: { name: "Mohamed", age: 25 } },
]);

// Multi-Get a value with TypeScript typing
const multiGetResult = db.multiGet<User>(["user:2345", "user:7890"]);
if (multiGetResult.success) {
  console.log(multiGetResult.data[0].age);
} else {
  console.log(multiGetResult.error.message);
}

Performance Considerations

MiftahDB is designed for high performance:

  1. Synchronous API reduces overhead and improves concurrency
  2. Optimized SQLite settings for improved performance
  3. In-memory database option for maximum speed

For best performance, consider the following tips:

  • Use in-memory databases for temporary or cache-like data
  • Regularly run the cleanup() and vacuum() methods to optimize the database