Skip to content

Commit

Permalink
Add support of Cloudflare D1
Browse files Browse the repository at this point in the history
  • Loading branch information
shinosaki committed Feb 9, 2024
1 parent dbaf805 commit 84dfa78
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 14 deletions.
21 changes: 20 additions & 1 deletion README.ja.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
## サポートしているキーバリューストア
- Cloudflare KV
- Redis ([node-redis](https://github.com/redis/node-redis)を使用)
- **New!** Deno KV
- Deno KV
- **New!** Cloudflare D1 (sqlite)

## サポートしているランタイム
| 対応 | ランタイム | 動作確認 |
Expand Down Expand Up @@ -33,6 +34,18 @@ npm install hono-kv-session
```bash
$ deno run --allow-net --watch --unstable app.ts
```
- Cloudflare D1
1. D1データベースを作成します
`$ wrangler d1 create session-db`
2. `wrangler.toml``database_id`**1.**で出力されたIDと置き換えます
```toml
[[ d1_databases ]]
binding = "SESSION_DB"
database_name = "session-db"
database_id = "<ここにIDを入力します>"
preview_database_id = "local"
```
3. `$ npm run d1:init`を実行します

## 使い方
Githubの[`./dev`](./dev)ディレクトリに`hono-kv-session`を使ったサンプルコードがあります。
Expand Down Expand Up @@ -61,6 +74,12 @@ Githubの[`./dev`](./dev)ディレクトリに`hono-kv-session`を使ったサ
app.use('*', kvClient());
```
- **Cloudflare D1**
```js
import { kvClient } from 'hono-kv-session/d1';
app.use('*', kvClient());
```
### SessionManagerを利用する
- `SessionManager()`ミドルウェアの設定
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Stateful session middleware for [Hono](https://hono.dev/). Works on Cloudflare W
## Supported Key-Value stores
- Cloudflare KV
- Redis (with [node-redis](https://github.com/redis/node-redis))
- **New!** Deno KV
- Deno KV
- **New!** Cloudflare D1 (sqlite)

## Supported Runtimes
| Supported | Runtime | Tested |
Expand Down Expand Up @@ -45,6 +46,18 @@ npm install hono-kv-session
```bash
$ deno run --allow-net --watch --unstable app.ts
```
- Cloudflare D1
1. Create D1 Database.
`$ wrangler d1 create session-db`
2. Update D1's `database_id` to `wrangler.toml`.
```toml
[[ d1_databases ]]
binding = "SESSION_DB"
database_name = "session-db"
database_id = "<Unique ID for Your Database Here>"
preview_database_id = "local"
```
3. Run `$ npm run d1:init`
## Usage
You can see the sample code in the [`./dev`](./dev) directory in Github.
Expand Down Expand Up @@ -73,6 +86,12 @@ You can see the sample code in the [`./dev`](./dev) directory in Github.
app.use('*', kvClient());
```
- **Cloudflare D1**
```js
import { kvClient } from 'hono-kv-session/d1';
app.use('*', kvClient());
```
### Use SessionManager
- Set `SessionManager()` middleware.
```js
Expand Down
33 changes: 21 additions & 12 deletions dev/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,28 @@ app.get('/', async (c) => {
let keys, kvLists;

if (runtime === 'workerd') {
keys = (await c.kv.list()).keys;

kvLists = await Promise.all(
keys.map(async ({ name, expiration }) => {
const value = await c.kv.get(name)

const timestamp = expiration * 1000
const date = new Date(timestamp)
const now = Date.now()

return [ name, value, date.toISOString(), (date - now) / 1000 ]
if ('list' in c.kv) {
keys = (await c.kv.list()).keys;

kvLists = await Promise.all(
keys.map(async ({ name, expiration }) => {
const value = await c.kv.get(name)

const timestamp = expiration * 1000
const date = new Date(timestamp)
const now = Date.now()

return [ name, value, date.toISOString(), (date - now) / 1000 ]
})
)
} else {
const { results } = await c.kv.prepare('SELECT * FROM session').all()
keys = results.map(r => r.key)
kvLists = results.map(r => {
const date = new Date(r.ttl * 1000)
return [r.key, r.value, date.toISOString(), (date.getTime() - Date.now()) / 1000]
})
)
}
} else {
keys = await c.kv.keys('*');

Expand Down
11 changes: 11 additions & 0 deletions dev/d1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Hono } from 'hono'
import { kvClient } from '../src/kv/d1'

const app = new Hono()

app.use('*', kvClient())

import APP from './app'
app.route('/', APP)

export default app
6 changes: 6 additions & 0 deletions migrations/0000_create_session_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Migration number: 0000 2024-02-09T20:50:00.000Z
CREATE TABLE session(
"key" TEXT NOT NULL PRIMARY KEY,
"value" TEXT NOT NULL,
"ttl" INTEGER NOT NULL
);
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"./denokv": {
"import": "./dist/kv/denokv.js",
"require": "./dist/cjs/kv/denokv.js"
},
"./d1": {
"import": "./dist/kv/d1.js",
"require": "./dist/cjs/kv/d1.js"
}
},
"scripts": {
Expand All @@ -48,6 +52,8 @@
"dev:bun": "bun run --hot dev/index.js",
"dev:node": "npm run build:node && node run.cjs",
"dev:deno": "deno run --allow-net --watch --unstable ./dev/deno.tsx",
"dev:d1": "wrangler dev dev/d1.js",
"d1:init": "wrangler d1 migrations apply session-db",
"deploy": "wrangler deploy --minify dev/worker.js"
},
"repository": {
Expand Down
7 changes: 7 additions & 0 deletions src/kv/d1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const kvClient = () => {
return async (c, next) => {
c.kv = c.env.SESSION_DB
c.kvType = 'd1'
await next()
}
}
23 changes: 23 additions & 0 deletions src/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const SessionManager = (options = {}) => {
return async (c, next) => {
c.session = { name }
const url = new URL(c.req.url)
const timestamp = Date.now() / 1000
const { kv } = c

if (!kv) {
Expand All @@ -33,6 +34,15 @@ export const SessionManager = (options = {}) => {
c.session.value = entry.value;
break

case 'd1':
const result = await kv.prepare('SELECT value, ttl FROM session WHERE key = ?1')
.bind(c.session.key)
.first()
c.session.value = (result.ttl > timestamp)
? result.value
: null
break

default:
throw new Error('Invalid kvType')
}
Expand Down Expand Up @@ -74,6 +84,7 @@ export const createSession = async (c, value, options = {}) => {

const { kv } = c
const url = new URL(c.req.url)
const timestamp = Date.now() / 1000

if (c.session.ttl < 60) {
c.session.ttl = 60
Expand All @@ -92,6 +103,12 @@ export const createSession = async (c, value, options = {}) => {
await kv.set(['session', url.hostname, session], value, { expireIn: c.session.ttl * 1000 })
break

case 'd1':
await kv.prepare('REPLACE INTO session (key, value, ttl) VALUES (?1, ?2, ?3)')
.bind(session, value, (timestamp + c.session.ttl))
.run()
break

default:
throw new Error('Invalid kvType')
}
Expand Down Expand Up @@ -133,6 +150,12 @@ export const deleteSession = async (c) => {
await kv.delete(['session', url.hostname, key])
break

case 'd1':
await kv.prepare('DELETE FROM session WHERE key = ?1')
.bind(key)
.run()
break

default:
throw new Error('Invalid kvType')
}
Expand Down

0 comments on commit 84dfa78

Please sign in to comment.