svelte-kit-connect-cloudflare-kv
Version:
Cloudflare Workers KV session storage for svelte-kit-sessions.
193 lines (137 loc) • 7.53 kB
Markdown
[](https://www.npmjs.com/package/svelte-kit-connect-cloudflare-kv)
[](https://github.com/yutak23/svelte-kit-connect-cloudflare-kv/actions/workflows/test.yaml)

**svelte-kit-connect-cloudflare-kv** provides [Cloudflare Workers KV](https://developers.cloudflare.com/kv/) session storage for [svelte-kit-sessions](https://www.npmjs.com/package/svelte-kit-sessions).
**svelte-kit-connect-cloudflare-kv** requires [`svelte-kit-sessions`](https://www.npmjs.com/package/svelte-kit-sessions) to installed.
```console
$ npm install svelte-kit-connect-cloudflare-kv svelte-kit-sessions
$ yarn add svelte-kit-connect-cloudflare-kv svelte-kit-sessions
$ pnpm add svelte-kit-connect-cloudflare-kv svelte-kit-sessions
```
`svelte-kit-connect-cloudflare-kv` can be used as a custom store for `svelte-kit-sessions` as follows.
**Note** For more information about `svelte-kit-sessions`, see https://www.npmjs.com/package/svelte-kit-sessions.
**Warning** You need to check that `event.platform` does not come out `undefined`. When [prerendering](https://kit.svelte.dev/docs/page-options#prerender) is done at build time, `event.platform` is `undefined` because it is before [bindings](https://kit.svelte.dev/docs/adapter-cloudflare#bindings) in Cloudflare, resulting in the following error.
```console
> Using @sveltejs/adapter-cloudflare
TypeError: Cannot read properties of undefined (reading 'env')
```
```ts
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import type { SessionStoreData } from 'svelte-kit-sessions';
import { sveltekitSessionHandle } from 'svelte-kit-sessions';
import KvStore from 'svelte-kit-connect-cloudflare-kv';
declare module 'svelte-kit-sessions' {
interface SessionData {
userId: string;
name: string;
}
}
export const handle: Handle = async ({ event, resolve }) => {
let sessionHandle: Handle | null = null;
if (event.platform && event.platform.env) {
// https://kit.svelte.dev/docs/adapter-cloudflare#bindings
const store = new KvStore<SessionStoreData>({ client: event.platform.env.YOUR_KV_NAMESPACE });
sessionHandle = sveltekitSessionHandle({
secret: 'secret',
store
});
}
return sessionHandle ? sessionHandle({ event, resolve }) : resolve(event);
};
```
<details>
<summary>If you want to use it with your own handle, you can use sequence</summary>
```ts
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { sveltekitSessionHandle } from 'svelte-kit-sessions';
import KvStore from 'svelte-kit-connect-cloudflare-kv';
let sessionHandle: Handle | null = null;
const handleForSession: Handle = async ({ event, resolve }) => {
let sessionHandle: Handle | null = null;
if (event.platform && event.platform.env) {
// https://kit.svelte.dev/docs/adapter-cloudflare#bindings
const store = new KvStore({ client: event.platform.env.YOUR_KV_NAMESPACE });
sessionHandle = sveltekitSessionHandle({
secret: 'secret',
store
});
}
return sessionHandle ? sessionHandle({ event, resolve }) : resolve(event);
};
const yourOwnHandle: Handle = async ({ event, resolve }) => {
// your code here
const result = await resolve(event);
return result;
};
export const handle: Handle = sequence(handleForSession, yourOwnHandle);
```
</details>
```ts
import KvStore from 'svelte-kit-connect-cloudflare-kv';
new KvStore(options);
```
Create a Cloudflare Workers KV store for `svelte-kit-sessions`.
A summary of the `options` is as follows.
| Name | Type | required/optional | Description |
| ---------- | ----------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| client | KVNamespace | _required_ | An KVNamespace |
| prefix | string | _optional_ | Key prefix in Redis (default: `sess:`). |
| serializer | Serializer | _optional_ | Provide a custom encoder/decoder to use when storing and retrieving session data from Redis (default: `JSON.parse` and `JSON.stringify`). |
| ttl | number | _optional_ | ttl to be used if ttl is _Infinity_ when used from `svelte-kit-sessions` |
#### client
An KVNamespace.
#### prefix
Key prefix in Redis (default: `sess:`).
#### serializer
Provide a custom encoder/decoder to use when storing and retrieving session data from Redis (default: `JSON.parse` and `JSON.stringify`).
**Note** When setting up a custom serializer, the following interface must be satisfied.
```ts
interface Serializer {
parse(s: string): SessionStoreData | Promise<SessionStoreData>;
stringify(data: SessionStoreData): string;
}
```
When `svelte-kit-sessions` calls a method of the store (the `set` function), ttl(milliseconds) is passed to it. However, if the cookie options `expires` and `maxAge` are not set, the ttl passed will be _Infinity_.
If the ttl passed is _Infinity_, the ttl to be set can be set with this option. The unit is milliseconds.
**Warning** Cloudflare Workers KV's expirationTtl is 60 seconds minimum. The store is implemented in such a way that an error will occur if the value is less than that.
```ts
// `svelte-kit-connect-cloudflare-kv` implementation excerpts
const ONE_DAY_IN_SECONDS = 86400;
export default class KvStore implements Store {
constructor(options: KvStoreOptions) {
// The number of seconds for which the key should be visible before it expires. At least 60.
// (https://developers.cloudflare.com/api/operations/workers-kv-namespace-write-multiple-key-value-pairs#request-body)
if (options.ttl && options.ttl < 60 * 1000)
throw new Error(
'ttl must be at least 60 * 1000. please refer to https://developers.cloudflare.com/workers/runtime-apis/kv#expiration-ttlhttps://developers.cloudflare.com/api/operations/workers-kv-namespace-write-multiple-key-value-pairs#request-body.'
);
this.ttl = options.ttl || ONE_DAY_IN_SECONDS * 1000;
}
ttl: number;
async set(id: string, storeData: SessionStoreData, ttl: number): Promise<void> {
if (ttl < 60 * 1000)
throw new Error(
'ttl must be at least 60 * 1000. please refer to https://developers.cloudflare.com/workers/runtime-apis/kv#expiration-ttlhttps://developers.cloudflare.com/api/operations/workers-kv-namespace-write-multiple-key-value-pairs#request-body.'
);
// omission ...
// Infinite time does not support, so it is implemented separately.
if (ttl !== Infinity) {
// https://developers.cloudflare.com/api/operations/workers-kv-namespace-write-multiple-key-value-pairs#request-body
await this.client.put(key, serialized, { expirationTtl: ttl / 1000 });
return;
}
await this.client.put(key, serialized, { expirationTtl: this.ttl / 1000 });
}
}
```
[](./LICENSE)