Skip to content

Commit 08fff55

Browse files
authored
feat!: add adapter system for database store (#12)
1 parent 9ed2738 commit 08fff55

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+972
-636
lines changed

docs/content/docs/cache_drivers.md

+32-59
Original file line numberDiff line numberDiff line change
@@ -150,94 +150,67 @@ So using this function can be costly, both in terms of execution time and API re
150150

151151
## Databases
152152

153-
We offer several drivers to use a database as a cache. Under the hood, we use [Knex](https://knexjs.org/). So all Knex options are available, feel free to check out the documentation.
153+
We offer several drivers to use a database as a cache. The database store should use an adapter for your database. Out of the box, we support [Knex](https://knexjs.org/) and [Kysely](https://kysely.dev/) to interact with the database. Knex and Kysely support many databases: SQLite, MySQL, PostgreSQL, MSSQL, Oracle, and more.
154+
155+
:::note
156+
157+
Note that you can easily create your own adapter by implementing the `DatabaseAdapter` interface if you are using another library not supported by Bentocache. See the [documentation](/docs/advanced/custom-adapters) for more details.
158+
159+
:::
154160

155161
All SQL drivers accept the following options:
156162

157163
| Option | Description | Default |
158164
| --- | --- | --- |
159165
| `tableName` | The name of the table that will be used to store the cache. | `bentocache` |
160166
| `autoCreateTable` | If the cache table should be automatically created if it does not exist. | `true` |
161-
| `connection` | The connection options to use to connect to the database or an instance of `knex`. | N/A |
167+
| `connection` | An instance of `knex` or `Kysely` based on the driver. | N/A |
168+
| `pruneInterval` | The interval in milliseconds to prune expired entries. | `60000` |
162169

163-
### PostgreSQL
170+
### Knex
164171

165-
You will need to install `pg` to use this driver.
172+
You must provide a Knex instance to use the Knex driver. Feel free to check the [Knex documentation](https://knexjs.org/) for more details about the configuration. Knex support many databases : SQLite, MySQL, PostgreSQL, MSSQL, Oracle, and more.
166173

167174
```ts
175+
import knex from 'knex'
168176
import { BentoCache, bentostore } from 'bentocache'
169-
import { postgresDriver } from 'bentocache/drivers/sql'
170-
171-
const bento = new BentoCache({
172-
default: 'pg',
173-
stores: {
174-
pg: bentostore().useL2Layer(postgresDriver({
175-
connection: {
176-
user: 'root',
177-
password: 'root',
178-
database: 'postgres',
179-
port: 5432
180-
}
181-
}))
177+
import { knexDriver } from 'bentocache/drivers/knex'
178+
179+
const db = knex({
180+
client: 'pg',
181+
connection: {
182+
port: 5432
183+
user: 'root',
184+
password: 'root',
185+
database: 'postgres',
182186
}
183187
})
184-
```
185-
186-
### MySQL
187-
188-
You will need to install `mysql2` to use this driver.
189-
190-
```ts
191-
import { BentoCache, bentostore } from 'bentocache'
192-
import { mysqlDriver } from 'bentocache/drivers/sql'
193188

194189
const bento = new BentoCache({
195-
default: 'mysql',
190+
default: 'pg',
196191
stores: {
197-
mysql: bentostore().useL2Layer(mysqlDriver({
198-
connection: {
199-
user: 'root',
200-
password: 'root',
201-
database: 'mysql',
202-
port: 3306
203-
},
204-
}))
192+
pg: bentostore().useL2Layer(knexDriver({ connection: db }))
205193
}
206194
})
207195
```
208196

209-
### SQLite ( better-sqlite3 )
210-
211-
You will need to install `better-sqlite3` to use this driver.
197+
### Kysely
212198

213-
```ts
214-
import { BentoCache, bentostore } from 'bentocache'
215-
import { betterSqliteDriver } from 'bentocache/drivers/sql'
199+
You must provide a Kysely instance to use the Kysely driver. Feel free to check the [Kysely documentation](https://kysely.dev/) for more details about the configuration. Kysely support the following databases : SQLite, MySQL, PostgreSQL and MSSQL.
216200

217-
const bento = new BentoCache({
218-
default: 'sqlite',
219-
stores: {
220-
sqlite: bentostore().useL2Layer(betterSqliteDriver({
221-
connection: { filename: 'cache.sqlite3' },
222-
}))
223-
}
224-
})
225-
```
226-
227-
### SQLite ( sqlite3 )
228-
229-
You will need to install `sqlite3` to use this driver.
201+
You will need to install `mysql2` to use this driver.
230202

231203
```ts
204+
import { Kysely } from 'kysely'
232205
import { BentoCache, bentostore } from 'bentocache'
233-
import { sqliteDriver } from 'bentocache/drivers/sql'
206+
import { mysqlDriver } from 'bentocache/drivers/kysely'
207+
208+
const db = new Kysely<Database>({ dialect })
234209

235210
const bento = new BentoCache({
236-
default: 'sqlite',
211+
default: 'pg',
237212
stores: {
238-
sqlite: bentostore().useL2Layer(sqliteDriver({
239-
connection: { filename: 'cache.sqlite3' },
240-
}))
213+
pg: bentostore().useL2Layer(kyselyStore({ connection: db }))
241214
}
242215
})
243216
```

docs/content/docs/extend/custom_cache_driver.md

+93-4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,94 @@ const bento = new BentoCache({
9494
})
9595
```
9696

97+
## Create an adapter for the DatabaseDriver
98+
99+
If your want to use a database to store your cache entries, you don't need to create a full driver. You can leverage the adapter system available with the database driver.
100+
101+
We only ship adapter for Kysely and Knex to interact with the database for now. If ever you want to use another library, you can create your own adapter by implementing the `DatabaseAdapter` interface accessible from `bentocache/types`. The interface is defined as follows:
102+
103+
```ts
104+
/**
105+
* Interface for a DatabaseAdapter that can be used with the DatabaseDriver
106+
*/
107+
export interface DatabaseAdapter {
108+
/**
109+
* Set the table name for the adapter
110+
*/
111+
setTableName(tableName: string): void
112+
113+
/**
114+
* Get an entry from the database
115+
*/
116+
get(key: string): Promise<{ value: any; expiresAt: number | null } | undefined>
117+
118+
/**
119+
* Delete an entry from the database
120+
*
121+
* You should return true if the entry was deleted, false otherwise
122+
*/
123+
delete(key: string): Promise<boolean>
124+
125+
/**
126+
* Delete multiple entries from the database
127+
*
128+
* Should return the number of entries deleted
129+
*/
130+
deleteMany(keys: string[]): Promise<number>
131+
132+
/**
133+
* Disconnect from the database
134+
*/
135+
disconnect(): Promise<void>
136+
137+
/**
138+
* Create the cache table if it doesn't exist
139+
*
140+
* This method is responsible for checking it the table
141+
* exists before creating it
142+
*/
143+
createTableIfNotExists(): Promise<void>
144+
145+
/**
146+
* Remove expired entries from the cache table
147+
*/
148+
pruneExpiredEntries(): Promise<void>
149+
150+
/**
151+
* Clear all entries from the cache table
152+
*/
153+
clear(prefix: string): Promise<void>
154+
155+
/**
156+
* Set a value in the cache
157+
* You should also make sure to not create duplicate entries for the same key.
158+
* Make sure to use `ON CONFLICT` or similar
159+
*/
160+
set(row: { key: string; value: any; expiresAt: Date | null }): Promise<void>
161+
}
162+
```
163+
164+
You can take a look at the code of the [Kysely adapter](https://github.com/Julien-R44/bentocache/blob/main/packages/bentocache/src/drivers/kysely.ts#L22) or the [Knex adapter](https://github.com/Julien-R44/bentocache/blob/main/packages/bentocache/src/drivers/kysely.ts#L22) for inspiration.
165+
166+
Once you defined your adapter, you can create your own store that use the DatabaseDriver and your adapter:
167+
168+
```ts
169+
export class PrismaAdapter implements DatabaseAdapter {
170+
// ...
171+
}
172+
173+
import { DatabaseDriver } from '@bentocache/drivers/database'
174+
175+
export function prismaDriver(options: PrismaOptions): CreateDriverResult<DatabaseDriver> {
176+
return {
177+
config,
178+
factory: () => {
179+
const adapter = new PrismaAdapter(config)
180+
return new DatabaseStore(adapter, config)
181+
},
182+
}
183+
}
184+
```
97185
## Tests
98186

99187
If you want to test your driver and its compliance, Bentocache is shipped with a test suite for [Japa](https://japa.dev/docs) that you can use. Note that you will also need to have `@japa/assert` installed. Then, you can use it like this:
@@ -106,11 +194,12 @@ import { MyDriver } from '../src/my_driver.js'
106194

107195
test.group('My Driver', (group) => {
108196
registerCacheDriverTestSuite({
197+
test,
109198
group,
110-
driver: MyDriver,
111-
config: {
112-
// Your driver options
113-
}
199+
createDriver: (options) => new MyDriver({
200+
myOption: 'myValue',
201+
...options
202+
}),
114203
})
115204
})
116205
```

packages/bentocache/benchmarks/mtier_get_key.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import { multiCaching, caching } from 'cache-manager'
1111
import { redisStore } from 'cache-manager-ioredis-yet'
1212

1313
import { BentoCache } from '../index.js'
14-
import { redisDriver } from '../drivers/redis.js'
1514
import { bentostore } from '../src/bento_store.js'
16-
import { memoryDriver } from '../drivers/memory.js'
15+
import { redisDriver } from '../src/drivers/redis.js'
16+
import { memoryDriver } from '../src/drivers/memory.js'
1717
import { REDIS_CREDENTIALS } from '../test_helpers/index.js'
1818

1919
const bench = new Bench()

packages/bentocache/benchmarks/mtier_set_key.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import { multiCaching, caching } from 'cache-manager'
1111
import { redisStore } from 'cache-manager-ioredis-yet'
1212

1313
import { BentoCache } from '../index.js'
14-
import { redisDriver } from '../drivers/redis.js'
1514
import { bentostore } from '../src/bento_store.js'
16-
import { memoryDriver } from '../drivers/memory.js'
15+
import { redisDriver } from '../src/drivers/redis.js'
16+
import { memoryDriver } from '../src/drivers/memory.js'
1717
import { REDIS_CREDENTIALS } from '../test_helpers/index.js'
1818

1919
const bench = new Bench()

packages/bentocache/benchmarks/onetier_get_key.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { caching } from 'cache-manager'
1010
import { redisStore } from 'cache-manager-ioredis-yet'
1111

1212
import { BentoCache } from '../index.js'
13-
import { redisDriver } from '../drivers/redis.js'
1413
import { bentostore } from '../src/bento_store.js'
14+
import { redisDriver } from '../src/drivers/redis.js'
1515
import { REDIS_CREDENTIALS } from '../test_helpers/index.js'
1616

1717
const bench = new Bench()

packages/bentocache/benchmarks/onetier_set_key.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { caching } from 'cache-manager'
99

1010
import { BentoCache } from '../index.js'
1111
import { bentostore } from '../src/bento_store.js'
12-
import { memoryDriver } from '../drivers/memory.js'
12+
import { memoryDriver } from '../src/drivers/memory.js'
1313

1414
const bench = new Bench()
1515

packages/bentocache/drivers/dynamodb.ts

-12
This file was deleted.

packages/bentocache/drivers/file.ts

-13
This file was deleted.

packages/bentocache/drivers/memory.ts

-13
This file was deleted.

packages/bentocache/drivers/redis.ts

-26
This file was deleted.

0 commit comments

Comments
 (0)