SvelteKitでタイプセーフなCookie管理
導入
CookieはWebアプリに必須の存在ですが、オブジェクトを格納したりしようとするとシリアライズ・デシリアライズの手間が発生します。また、変換の際やサーバー・クライアント間で型定義を間違えてつけてエラーを発生させてしまう可能性があります。
今回は、SvelteKitでフルスタックにタープセーフなCookie管理ができるライブラリを紹介します。
svelte-baked-cookie
名前の由来は硬く型付けされたCookieにしたかったのでbaked(焼いた)-cookieにしました。
(svelte-french-toastに影響を受けています)
セットアップ
まずは依存関係のインストール
npm i svelte-baked-cookie
使い方
このライブラリは大きく分けて3つのステップでCookieを管理します。
ステップ1 - Cookie型の定義
アプリケーションで使用するCookieとその型を一覧で定義します。
bakery
関数の第一引数のオブジェクトのキーがCookieのキー、値がそのキーのserde
になります。
serde
についてはこちらの記事を参照して下さい。
以下に例を示します。
import { bakery } from 'svelte-baked-cookie'
import { json, number, string } from 'svelte-baked-cookie/serde'
export const { bake, rebake } = bakery(
{
key1: string,
key2: number,
key3: json(
(x): x is string[] =>
Array.isArray(x) && x.every((y) => typeof y === 'string'),
[]
)
}
)
この例では、3つのCookieを使用します。
1つ目はキーが"key1"で文字列型で格納します。
2つ目はキーが"key2"で数値型で格納します。
3つ目はキーが"key3"で文字列配列型で格納します。
すると、戻り値としてbake
関数とrebake
関数が得られます。
アプリケーションではcookies
やdocument.cookies
を触らずに、これらを使ってCookieの読み書きを行います。
ステップ2 - サーバーサイドでの使用
SvelteKitのサーバーサイドではcookies
が使用できます。
これをbake
関数に渡すと型付け(型変換)されたCookieが得られます。
import { bake } from './bakery.js'
export const load = ({ cookies }) => {
const { bakedCookies } = bake(cookies)
// string
const str = bakedCookies.key1 // 'foo'
// number
const num = bakedCookies.key2 // 123
// string[]
bakedCookies.key3 = ['value', 'set', 'by', 'server']
return {
// ...
}
}
文字列への変換・パースやget
set
メソッドのことを考える必要はありません。
オブジェクトのキーを指定して読み書きするだけでCookieを操作できます。
ステップ3 - クライアントサイドでの使用
SvelteKitのクライアントサイドではcookies
は直接使用できません。
代わりにrebake
関数を使用してサーバーサイドと同じ型付けされたCookieを読み書きできます。
<script>
import { rebake } from './bakery.js'
const cookies = rebake()
// key1: string
// key2: number
// key3: string[]
// 'foo'を表示
console.log(cookies.key1)
// string
cookies.key2 = 123
// string[]
cookies.key3 = ['value', 'set', 'by', 'client']
</script>
ステップ4(任意) - SSR環境における改善
SSRを使用する場合、ステップ3だけでは不十分な場合があります。
なぜならrebake
関数は内部でdocument.cookie
を読み書きしているのですが、サーバー上でsvelteコンポーネントがレンダリングされる際、document.cookie
は存在しません。
よって全ての値がNullishになった状態で、サーバー上でレンダリングされ、ハイドレーションが完了すると初めて実際のCookie値が読み込まれます。
これにより、表示のチラつきが発生する場合があります。
これを回避するためには、追加のステップが必要です。
import { bake } from './bakery.js'
export const load = ({ cookies }) => {
const { bakedCookies, pie } = bake(cookies)
// ...
return {
pie
}
}
サーバー上でbake
関数を実行する際、戻り値でpie
というオブジェクトが返されます。
このpie
オブジェクトの中には型付けされる前のCookieが入っています。
これをreturn
してクライアント側に渡します。
そしてクライアント側ではrebake
関数にpie
を渡します。
<script>
import { rebake } from './bakery.js'
let { data } = $props()
let pie = $derived(data.pie)
// key1: string
// key2: number
// key3: string[]
let cookies = $derived(rebake(pie))
</script>
このような手順を踏むことでSSRのサーバーサイドレンダリング中でもCookieの内容にアクセスできるようになり、表示のチラつきが緩和されます。
まとめ
いかがだったでしょうか。
この記事がSvelteKitにおけるタイプセーフなCookie管理の助けになれば幸いです。
またsvelte-backed-cookieについてバグや不明点がありましたらぜひIssueを開いてください。
Discussion