Bun SQLite Key Value
A super fast key-value store with SQLite that uses bun:sqlite
and v8 as a fast JSON replacement.
Bun's lightning-fast
SQLite implementation makes Bun-SQLite-Key-Value
perfect for a fast storage and cache solution with TTL support.
You need Bun to be able to use this package.
The ideas for the implementation come from
bun-sqlite-cache and
bun-kv. Thank you very much!
Installation
bun add bun-sqlite-key-value
Usage
Using this key value store is dead simple:
create a new BunSqliteKeyValue instance and you're set.
And if you want to save the data permanently, enter the path to the database.
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("myKey", [1, 2, 3, 4])
store.get("myKey")
store.data.myKey = "Hello world!"
store.data.myKey
store.d.myKey = 123456789
store.d.myKey
Open Database
const store = new BunSqliteKeyValue(filename?, options?)
Opens and creates the SQLite database either in memory or on the file system.
filename (optional)
The full path of the SQLite database to open.
Pass an empty string (""
) or ":memory:"
or undefined
for an in-memory database.
options (optional)
readonly?: boolean
:
Open the database as read-only (default: false).
create?: boolean
:
Allow creating a new database (default: true).
If the database folder does not exist, it will be created.
readwrite?: boolean
:
Open the database as read-write (default: true).
ttlMs?: boolean
:
Standard time period in milliseconds before
an entry written to the DB becomes invalid.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store1 = new BunSqliteKeyValue()
const store2 = new BunSqliteKeyValue(undefined, {ttlMs: 30000})
const store3 = new BunSqliteKeyValue("./store3.sqlite")
Write Value
set(key: string, value: any, ttlMs?: number)
data.<key> = <value>
data[<key>] = <value>
d.<key> = <value>
d[<key>] = <value>
Writes a value into the database.
key
The key must be a string.
value
The value can be any object that can be serialized with
v8.
This means that not only simple data types (string, number) are possible,
but also more complex types such as sets or maps.
You can find a list of the
supported data types here.
ttlMs (optional)
"Time to live" in milliseconds. After this time,
the item becomes invalid and is deleted from the database
the next time it is accessed or when the application is started.
Set the value to 0 if you want to explicitly deactivate the process.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("myKey1", "my-value")
store.data.myKey2 = "my-value"
store.data["myKey3"] = "my-value"
store.d.myKey4 = "my-value"
store.d["myKey5"] = "my-value"
store.set("myKey6", "item-with-ttl", 30000)
Read Value
get(key: string): any
data.<key>: any
data[<key>]: any
d.<key>: any
d[<key>]: any
Reads a value from the database.
key
The key must be a string.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("myKey", "my-value")
store.get("myKey")
store.data.myKey
store.data["myKey"]
store.d.myKey
store.d["myKey"]
Read Item
Reads the key and the value from the database.
getItem(key: string): {key: string, value: any}
key
The key must be a string.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("my-key", "my-value")
const item = store.getItem("my-key")
console.log(item)
Write Multiple Items
setItems(items: {key: string, value: T, ttlMs?: number}[]) {
Adds a large number of items to the database and takes only
a small fraction of the time that set()
would take individually.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.setItems([
{key: "a:1", value: "test-value-1"},
{key: "a:2", value: "test-value-2"},
])
Read Values
getValues(startsWithOrKeys?: string | string[]): any[]
<store>.values
Reads the data from the database and returns an array with the values.
startsWithOrKeys
undefined
: Returns all values in an array.
string
: Returns all values in an array whose keys begin with the passed string.
If you plan the names of the keys well, more complex data can be stored.
It is advisable to divide keys into ranges using separators.
For example "language:de"
, "language:en"
, "language:it"
.
A search for "language:"
would return all languages.
string[]
: Array with keys. The returned array is exactly
the same size as the passed array.
Entries that are not found are returned as undefined
.
Only exact matches with the keys are returned.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("language:de", "German")
store.set("language:en", "English")
store.set("language:it", "Italian")
store.getValues()
store.getValues("language:")
store.values
Read Items
getItems(startsWithOrKeys?: string | string[]): {key: string, value: any}[]
<store>.items
Reads the data from the database and returns entries in an array as key-value pairs.
startsWithOrKeys
undefined
: Returns all items (key, value) in an array.
string
: Returns all items (key, value) in an array whose keys begin with
the passed string.
If you plan the names of the keys well, more complex data can be stored.
It is advisable to divide keys into ranges using separators.
For example "language:de"
, "language:en"
, "language:it"
.
A search for "language:"
would return all languages.
string[]
: Array with keys. The returned array is exactly
the same size as the passed array.
Entries that are not found are returned as undefined
.
Only exact matches with the keys are returned.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("language:de", "German")
store.set("language:en", "English")
store.set("language:it", "Italian")
store.getItems("language:")
store.items
Multiple Databases
It is no problem at all to use several databases and access them at the same time.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
import { join } from "node:path"
const dbDir = join(__dirname, "databases")
const settingsPath = join(dbDir, "settings.sqlite")
const languagesPath = join(dbDir, "languages.sqlite")
const settingsStore = new BunSqliteKeyValue(settingsPath)
const languagesStore = new BunSqliteKeyValue(languagesPath)
settingsStore.set("language", "de")
settingsStore.set("page-size", "A4")
settingsStore.set("screen-position", {top: 100, left: 100})
languagesStore.set("de", "German")
languagesStore.set("en", "English")
languagesStore.set("it", "Italian")
const settingItems = settingsStore.getItems()
console.log(settingItems)
const languageValues = languagesStore.getValues()
console.log(languageValues)
const languageKey = settingsStore.get("language")
const currentLanguage = languagesStore.get(languageKey)
console.log(`Current language: "${currentLanguage}"`)
settingsStore.close()
languagesStore.close()
Read and Write Binary Files (Images)
SQLite has no problem with images and other binaries.
The maximum size of a binary file is 2 GB.
Example (async)
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
const sourceFile = Bun.file("<Source File Path>")
store.set("my-image", await sourceFile.arrayBuffer())
const targetArrayBuffer = store.get("my-image")
await Bun.write(Bun.file("<Target File Path>"), targetArrayBuffer)
Example (sync)
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
import { readFileSync, writeFileSync } from "node:fs"
const store = new BunSqliteKeyValue()
const sourceContent = readFileSync("<Source File Path>")
store.set("my-image", sourceContent)
const targetBuffer = store.get("my-image")
writeFileSync("<Target File Path>", targetBuffer)
Cache Values with TTL
You can specify a caching period when you open the database.
This period in milliseconds is then added with each write.
If you read the value within this period, the value is returned.
If the value is read after this period, undefined
is returned.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue(undefined, {ttlMs: 1000})
const KEY = "cache-key"
store.set(KEY, 12345)
await Bun.sleep(500)
console.log(store.get(KEY))
await Bun.sleep(1000)
console.log(store.get(KEY))
Has (key)
has(key: string): boolean
<key> in <store>.data
<key> in <store>.d
Checks if key exists. Returns false
if the item is expired.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.has("my-key")
console.log("my-key" in store.data)
Read Keys
getKeys(startsWithOrKeys?: string | string[]): string[]
<store>.keys
Reads the keys from the database and returns an array.
startsWithOrKeys
undefined
: Returns all keys in an array.
string
: Returns an array with the keys that begin with the passed string.
If you plan the names of the keys well, more complex data can be stored.
It is advisable to divide keys into ranges using separators.
For example "language:de"
, "language:en"
, "language:it"
.
A search for "language:"
would return all languages.
string[]
: Array with keys.
Only exact matches with the keys are returned.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("language:de", "German")
store.set("language:en", "English")
store.set("language:es", "Esperanto")
store.getKeys()
store.keys
store.getKeys("language:e")
store.getKeys(["language:de", "language:fr"])
Delete Items
delete()
delete(key: string)
delete(keys: string[])
clear()
delete <store>.data.<key>
delete <store>.d.<key>
Deletes all items if no parameter was passed.
key: string
: Deletes the entry whose key was passed as a string.
keys: string[]
: Deletes the entries whose keys were passed in an array.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.delete()
store.clear()
store.delete("myKey")
delete store.data.myKey
delete store.d.myKey
store.delete(["key1", "key2"])
Delete Old Expiring Items
deleteOldExpiringItems(maxExpiringItemsInDb: number)
If there are more expiring items in the database than maxExpiringItemsInDb
,
the oldest items are deleted until there are only maxExpiringItemsInDb
items with
an expiration date in the database.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("static:1", "my-value")
store.set("static:2", "my-value")
store.set("dynamic:1", "my-value", 40)
store.set("dynamic:2", "my-value", 45)
store.set("dynamic:3", "my-value", 50)
store.deleteOldExpiringItems(2)
console.log(store.getKeys("dynamic:"))
Count All Items
getCount(): number
length
Returns the number of all items, including those that have already expired.
The fact that possibly expired entries are also counted is for reasons of speed.
Use getCountValid()
if you want to get the number of items that have not yet expired.
If you do not use ttlMs
(time to live), getCount()
is faster than getCountValid()
.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("my-key1", "my-value1")
store.set("my-key2", "my-value2")
store.getCount()
store.length
Count Valid Items
getCountValid(deleteExpired?: boolean): number
Returns the number of valid (non-expired) items.
Can also delete the expired items.
deleteExpired
If the parameter is not specified or false
is passed,
then only the entries that have no expiration date or
whose expiration date is in the future are counted.
If true
is passed, the expired entries are deleted first
before the entries are counted.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("my-key1", "my-value1")
store.set("my-key2", "my-value2", 100)
store.getCountValid()
await Bun.sleep(500)
store.getCountValid()
Increment
incr(key: string, incrBy: number = 1, ttlMs?: number): number
Increments the saved number by incrBy
(default = 1),
saves the new number and returns it.
If the key does not yet exist in the database,
the value is set to 0 before being incremented by incrBy
.
If a string is stored in the database that can be converted into a number,
this is converted first.
If the stored value cannot be converted into a number, NaN
is returned.
key
The key must be a string.
incrBy
The stored number is increased by this value.
ttlMs (optional)
"Time to live" in milliseconds. After this time,
the item becomes invalid and is deleted from the database
the next time it is accessed or when the application is started.
Set the value to 0 if you want to explicitly deactivate the process.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.incr("my-key")
store.incr("my-key")
Decrement
decr(key: string, decrBy: number = 1, ttlMs?: number): number
Decrements the saved number by decrBy
(default = 1),
saves the new number and returns it.
If the key does not yet exist in the database,
the value is set to 0 before being decremented by decrBy
.
If a string is stored in the database that can be converted into a number,
this is converted first.
If the stored value cannot be converted into a number, NaN
is returned.
key
The key must be a string.
incrBy
The stored number is decreased by this value.
ttlMs (optional)
"Time to live" in milliseconds. After this time,
the item becomes invalid and is deleted from the database
the next time it is accessed or when the application is started.
Set the value to 0 if you want to explicitly deactivate the process.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.set("my-key", 10)
store.decr("my-key")
store.decr("my-key")
Append
append(key: string, value: string, ttlMs?: number): number
If key already exists, this command appends the value at the end of the string.
If key does not exist it is created and set as an empty string,
so append()
will be similar to set()
in this special case.
Inspired by: https://docs.keydb.dev/docs/commands/#append
Returns the length of the string after the append operation.
key
The key must be a string.
value
The string that is appended to the existing string.
ttlMs (optional)
"Time to live" in milliseconds. After this time,
the item becomes invalid and is deleted from the database
the next time it is accessed or when the application is started.
Set the value to 0 if you want to explicitly deactivate the process.
Example
import { BunSqliteKeyValue } from "bun-sqlite-key-value"
const store = new BunSqliteKeyValue()
store.append("my-key", "Hello!")
store.append("my-key", "World!")
store.get("my-key")
All Functions
Database
new BunSqliteKeyValue()
--> Open databaseclose()
--> Close database
Set value
set(key: string, value: any)
setValue(key: string, value: any)
--> alias for set()<store>.data.<key> = <value>
<store>.d.<key> = <value>
Set items
setItems({key: string, value: any}[])
Get value
get(key: string): any
getValue(key: string)
--> alias for get()<store>.data.<key>
<store>.d.<key>
getSet(key: string, value: any): any
Get item
getItem(key: string)
--> Object
Get items as Array
getItems()
--> Array with all itemsgetItems(startsWith: string)
--> ArraygetItems(keys: string[])
--> ArraygetItemsArray()
--> alias for getItems()getItemsArray(startsWith: string)
--> alias for getItems()getItemsArray(keys: string[])
--> alias for getItems()items
--> alias for getItems()
Get items as Object
getItemsObject()
--> Object with all itemsgetItemsObject(startsWith: string)
--> ObjectgetItemsObject(keys: string[])
--> Object
Get items as Map()
getItemsMap()
--> Map with all itemsgetItemsMap(startsWith: string)
--> MapgetItemsMap(keys: string[])
--> Map
Get values as Array
getValues()
--> Array with all valuesgetValues(startsWith: string)
--> ArraygetValues(keys: string[])
--> ArraygetValuesArray()
--> alias for getValues()getValuesArray(startsWith: string)
--> alias for getValues()getValuesArray(keys: string[])
--> alias for getValues()values
--> alias for getValues()
Get values as Set()
getValuesSet()
--> Set with all valuesgetValuesSet(startsWith: string)
--> SetgetValuesSet(keys: string[])
--> Set
Delete
delete()
--> Delete all itemsdelete(key: string)
--> Delete itemdelete(keys: string[])
--> Delete itemsclear()
--> alias for delete()deleteOldExpiringItems(maxExpiringItemsInDb: number)
--> Delete itemsdelete <store>.data.<key>
delete <store>.d.<key>
Count
getCount()
--> Numberlength
--> alias for getCount()getCountValid(deleteExpired?: boolean)
--> Number
Get keys
has(key: string)
--> BooleangetKeys()
--> Array with all KeysgetKeys(startsWith: string)
--> ArraygetKeys(keys: string[])
--> Arraykeys
--> alias for getKeys()<key> in <store>.data
<key> in <store>.d
Math operations
incr()
--> Numberdecr()
--> Number
String operations
SQLite as base for a key value storage
SQLite provides a solid and well-tested foundation.
SQLite reliably takes care of saving and reading data -
both for short strings and for larger BLOBs.
It provides a robust foundation on which to build.
Even if SQLite is not fully utilized and no relations between tables are required,
this is not a disadvantage.
Please give this GitHub project
a ⭐ if this project is useful to you. Thank you very much!
And if you speak German, here is my business homepage:
GP-Softwaretechnik
Maybe you will find something interesting for you there. 😃