- Features
- Installation
- Usage
- Synchronous API
- Error Handling
- API Reference
- Supported Value Types
- TypeScript Typing & Generics
- Pattern Matching
- Performance Considerations
- 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
# With NPM
npm install miftahdb
# With Bun
bun install miftahdb
// Node runtime
import { MiftahDB } from "miftahdb";
// For Bun runtime
import { MiftahDB } from "miftahdb/bun";
// 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);
MiftahDB uses a synchronous API, which may seem counterintuitive but actually provides better performance and concurrency than an asynchronous API for most use cases.
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);
}
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:");
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);
}
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"));
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 thanget
because it uses a simpler SQL query. Useexists
when you only need to check if a key is present, as it can boost performance in speed-sensitive situations.
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);
}
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);
}
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);
}
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);
}
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__:%");
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:%");
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 usingkeys().length
because it directly executes a SQL query optimized for counting rows. Usecount
when you need to determine the number of keys, as it provides better performance compared to fetching all keys and measuring their length.
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:%");
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);
}
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);
}
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);
}
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.
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.
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);
}
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();
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);
}
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);
}
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"));
Closes the database connection.
db.close();
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);
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__:%");
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);
}
MiftahDB is designed for high performance:
- Synchronous API reduces overhead and improves concurrency
- Optimized SQLite settings for improved performance
- 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()
andvacuum()
methods to optimize the database