まくろぐ
更新: / 作成:

何をするか?

Svelte/SvelteKit を使った Web アプリケーションでは、src/routes 以下のサーバーモジュール (+server.ts) で GETPOST 関数をエクスポートするだけで、API ルートとして動作させることができます(Web API のエンドポイントとして動作します)。

このサーバーモジュール内でグローバル変数を定義すると、その値は サーバーが起動している間だけ保持されます。 ここでは、この振る舞いを確認するためのサンプルコードを示します。

複数のクライアントからのリクエスト間で同じ変数が共有されるため、ユーザー情報などを格納してしまうと、セキュリティ上のリスクが生じる可能性があることに注意してください。

実装例

下記のサーバールート実装 (/api/messages) では、POST リクエストで送られてきたメッセージを最大 5 件までグローバル変数 (messages) に保持しています。 GET リクエストでアクセスすると、保持しているメッセージを JSON 形式で返します。

src/routes/api/messages/+server.ts
import { error, json } from '@sveltejs/kit';

// API route のグローバル変数はサーバーが起動している間のみ保持される
let messages: string[] = [];

/** /api/messages への GET リクエストを処理する */
export async function GET() {
	return json({ messages });
}

/** /api/messages への POST リクエストを処理する */
export async function POST({ request }) {
	const msg = (await request.text()).trim();
	if (!msg) {
		error(400, 'Text is required in the request body');
	}
	// 最新の 5 つのメッセージを保持
	messages = [...messages.slice(-4), msg];
	return json({ messages }, { status: 201 });
}

ページコンポーネントからこの API ルートにアクセスするコードは以下のようになります。 テキストボックスに入力したメッセージを送信すると、API ルート側で保持しているメッセージリストが更新され、その内容が画面上に表示されます。

src/routes/+page.svelte
<script lang="ts">
	import { onMount } from 'svelte';

	/** サーバーから取得したメッセージのリスト */
	let receivedMessages: string[] = $state([]);

	/** ユーザーが入力したメッセージ */
	let userInput = $state('');

	async function fetchMessages() {
		const res = await fetch('/api/messages');
		const jsonData = await res.json();
		receivedMessages = jsonData.messages;
	}

	async function handleSubmit(e: Event) {
		e.preventDefault();
		const newMessage = userInput.trim();
		userInput = '';

		// 入力欄が空の場合は何もしない
		if (newMessage === '') {
			return;
		}

		// API エンドポイントに新しいメッセージを POST で送信
		const res = await fetch('/api/messages', {
			method: 'POST',
			headers: { 'Content-Type': 'application/json' },
			body: newMessage
		});

		// 新しいデータで表示を更新
		const jsonData = await res.json();
		receivedMessages = jsonData.messages;
	}

	// ページが読み込まれたときにメッセージを取得
	onMount(fetchMessages);
</script>

<main>
	<form>
		<input bind:value={userInput} placeholder="メッセージを入力" />
		<button type="submit" onclick={handleSubmit}>送信</button>
	</form>
	<ul>
		{#each receivedMessages.toReversed() as message}
			<li>{message}</li>
		{/each}
	</ul>
</main>

SvelteKit のサーバーを起動して Web ブラウザーでアクセスすると、次のように最大 5 件のメッセージが表示されます。 サーバーが起動している間はこのメッセージリストは保持されており、別のブラウザーからアクセスしても同じメッセージが表示されます。

/p/c28msnk/img-001.gif
図: API ルートでのデータ保持

理解したー ٩(๑❛ᴗ❛๑)۶ わーぃ

よく考えたら messages まわりの処理がスレッドセーフになっていないので、複数のリクエストが同時に来た場合に備えて Mutex などで排他制御を行わなきゃですね。 いずれにしても、このようなコードを実際のプロダクション環境で使うことは稀で、多くの場合は、データベースや KV ストアライブラリなどを利用してデータの永続化を行うことになります。

関連記事

更新: / 作成:

何をするか?

機械学習のコンペティションを開催している Kaggle のサイトでは、Web ブラウザから利用可能な Notebook 機能が提供されています。 これを使うとブラウザ上で直接 Python のコードを書いて実行できるのですが、やっぱり使い慣れた Visual Studio Code 環境上で作業したいと思ったので、VS Code 上に Jupyter Notebook の環境を作っていきます。

Python の実行環境についてはインストール済みであることを前提とします。 Python がまだインストールされていない場合は、pyenv などでインストール するとよいです。

Jupyter 拡張のインストール

まずは、Microsoft 純正の Python 拡張Jupyter 拡張 をインストールします。 下記サイトの Install ボタンを押すか、VS Code の拡張機能画面から検索してインストールできます。

Kaggle 用の仮想環境 (venv)

venv の作成

VS Code 上の Jupyter Notebook で Python コードを実行するには、pip コマンドで Python カーネル (ipykernel) をインストールしておく必要があります。 また、機械学習系の実装では numpypandas などのライブラリも必要になってきます。 ここでは、ホームディレクトリに Python の仮想環境ディレクトリ (~/.venv/kaggle) を作成し、そこに各ライブラリをインストールすることにします。

Kaggle 用の venv 環境を作成
$ python -m venv ~/.venv/kaggle

プロジェクトのルートディレクトリに .venv ディレクトリを作る方法も一般的ですが、上記のようにホームディレクトリに作っておくと、他のプロジェクトからも使いまわせて便利です。

venv へのライブラリインストール

仮想環境を作成したら、仮想環境をアクティベートして、必要になりそうなライブラリをインストールしておきます。 少なくとも ipykernel は Jupyter Notebook 上での Python 実行のために必須です。

Kaggle 環境用のライブラリをインストール
# venv をアクティベート
$ source ~/.venv/kaggle/bin/activate

# Jupyter で必須のライブラリ
$ pip install ipykernel

# あとは必要に応じて
$ pip install numpy pandas matplotlib scikit-learn
☝️ activate 用エイリアス

毎回 source ~/.venv/kaggle/bin/activate と入力するのが面倒な場合は、シェルの設定ファイル(.zshrc など)にエイリアスを設定しておくと便利です。

alias venv-kaggle="source ~/.venv/kaggle/bin/activate"

venv の検索パスを追加

前述のように、プロジェクトの外に venv 環境を作成した場合は、VS Code の Python 拡張が見つけられるように検索パスを追加しておく必要があります。 Ctrl/Cmd + Shift + PPreferences: Open User Settings (JSON) で設定ファイルを開いて、次のような行を追加しておきます(参考: Where the extension looks for environment)。

settings.json
{
  // Python の venv 検索パスを追加(~/.venv/kaggle などを検索するため)
  "python.venvPath": "~/.venv",

  // あるいは下記でも可能(ホームディレクトリ以下のディレクトリ名を列挙)
  // "python.venvFolders": [".venv"],

python.venvPath あるいは python.venvFolders のいずれかで設定すれば大丈夫です。 プロジェクト(ワークスペース)のルートディレクトリ内に .venv ディレクトリを配置する場合は、この設定は不要です。

Python 拡張に venv を認識させる

VS Code 上の Python 拡張は、デフォルトではシステムにインストールされた Python インタープリター(python コマンド)を使おうとするので、上記で作成した仮想環境 (venv) 内の Python インタープリターを使うように設定しておきます。 この作業は、プロジェクト内で一度だけ実行すれば OK です。

まず、適当にプロジェクト用のディレクトリを作成して、VS Code を起動します。

VS Code の起動
$ mkdir ~/kaggle
$ code ~/kaggle

VS Code 上で Python ファイルを開いて、右下に表示された Python バージョンをクリックするか、コマンドパレット (Ctrl/Cmd + Shift + P) から Python: Select Interpreter を選択し、先ほど作成した Python 仮想環境 (~/.venv/kaggle) を選択します。 これで、このプロジェクトでは仮想環境内の Python インタープリターが使われるようになります。

☝️ Python インタープリター設定はどこに保存されるか?

Python: Select Interpreter で選択した Python インタープリターの設定は、VS Code 内部の保存領域に保存されるため、JSON 形式の設定ファイルとしては確認できないようです(参考: Setting descriptions)。 この情報をクリアするには、コマンドパレットから Python: Clear Workspace Interpreter Setting を実行します(システムの Python を使う状態に戻ります)。 次のように、デフォルトの Python インタープリターを設定しておくこともできます。

{
  // ...
  "python.defaultInterpreterPath": "~/.venv/kaggle/bin/python",
  // ...
}

この設定は、プロジェクト内でまだ一度も Python インタープリターを選択していないときに使われます(あるいは、前述の方法で設定をクリアした場合)。 ただ、Python インタープリターの設定はプロジェクト内で一度だけ行えばよいので、この設定はあまり使わないかもしれません。

Jupyter Notebook を起動する

Ctrl/Cmd + Shift + P でコマンドパレットを開いて、Create: New Jupyter Notebook を選択すると、新しい Jupyter Notebook (Untitled-1.ipynb) ファイルが作成されます。

こちらも、どの Python 実行環境を使うかを選択しておく必要があります。 Jupyter Notebook では「カーネル」と呼ばれ、Python 拡張が参照する「Python インタープリター」とは別に設定する必要があります。 Jupyter Notebook 画面上の カーネル選択 ボタンを押すか、コマンドパレットから Notebook: Select Notebook Kernel を選択し、使用する Python 仮想環境 (~/.venv/kaggle) を選択します。

これで、Jupyter Notebook 上で Python コードを実行する準備が整いました。 pandas などのライブラリもインポートできるようになっているはずです。

Kaggle CLI のインストール

kaggle コマンドのインストールと設定

これは VS Code とは直接関係ありませんが、Kaggle 公式の kaggle コマンドをインストールしておくと、コマンドラインからコンペティションのデータセットのダウンロードや、解答の提出ができて便利です。 簡単にセットアップできるので入れておきましょう。

Kaggle CLI のインストール
$ pip install kaggle

kaggle コマンドを実行するには、Kaggle のサイトで API トークンを取得して、~/.kaggle/kaggle.json に保存しておく必要があります。 Kaggle のアカウント設定 を開き、Create New Token ボタンをクリックすると、kaggle.json ファイルをダウンロードできます。 OS によって保存先が異なるので注意してください(kaggle コマンドのエラー表示を見て確認するのでもよいです)。

OS保存先
WindowsC:\Users\<ユーザー名>\.kaggle\kaggle.json
Linux~/.config/kaggle/kaggle.json
macOS / その他~/.kaggle/kaggle.json

kaggle コマンドの使い方

開催中のコンペティションの一覧 (kaggle competitions list)

現在開催中のコンペの情報を調べるには、kaggle competitions list コマンドを使います。 ここで表示された URL 情報は、後述のコマンドでデータセットをダウンロードする際などに必要になります。

$ kaggle c list                            # 基本的な使い方
$ kaggle c list --help                     # ヘルプ
$ kaggle c list --search titanic           # 検索 (Titanic)
$ kaggle c list --category gettingStarted  # カテゴリ (Getting Started)
$ kaggle c list --category playground      # カテゴリ (Playground)
$ kaggle c list --sort-by recentlyCreated  # ソート (Recently Created)
$ kaggle c list --sort-by recentlyCreated
ref                                                                                deadline             category                reward  teamCount  userHasEntered
---------------------------------------------------------------------------------  -------------------  ---------------  -------------  ---------  --------------
https://www.kaggle.com/competitions/playground-series-s5e2                         2025-02-28 23:59:00  Playground                Swag       1363           False
https://www.kaggle.com/competitions/konwinski-prize                                2025-03-12 23:59:00  Featured         1,225,000 Usd        306           False
https://www.kaggle.com/competitions/lux-ai-season-3                                2025-03-10 23:59:00  Featured            50,000 Usd        463           False
https://www.kaggle.com/competitions/equity-post-HCT-survival-predictions           2025-03-05 23:59:41  Research            50,000 Usd       2459           False
https://www.kaggle.com/competitions/llms-you-cant-please-them-all                  2025-03-04 23:59:00  Featured            50,000 Usd       1317           False
https://www.kaggle.com/competitions/fide-google-efficiency-chess-ai-challenge      2025-02-11 23:59:00  Featured            50,000 Usd       1120           False
https://www.kaggle.com/competitions/wsdm-cup-multilingual-chatbot-arena            2025-03-10 23:59:00  Featured            50,000 Usd        950           False
https://www.kaggle.com/competitions/ai-mathematical-olympiad-progress-prize-2      2025-04-01 23:59:00  Featured         2,117,152 Usd       1356           False
https://www.kaggle.com/competitions/llm-classification-finetuning                  2030-07-01 23:59:00  Getting Started      Knowledge        125           False
https://www.kaggle.com/competitions/jane-street-real-time-market-data-forecasting  2025-07-12 23:59:00  Featured           120,000 Usd       3757           False
https://www.kaggle.com/competitions/spaceship-titanic                              2030-01-01 00:00:00  Getting Started      Knowledge       1962           False
https://www.kaggle.com/competitions/store-sales-time-series-forecasting            2030-06-30 23:59:00  Getting Started      Knowledge        676           False
https://www.kaggle.com/competitions/gan-getting-started                            2030-07-01 23:59:00  Getting Started      Knowledge        139           False
https://www.kaggle.com/competitions/contradictory-my-dear-watson                   2030-07-01 23:59:00  Getting Started      Knowledge         32           False
https://www.kaggle.com/competitions/tpu-getting-started                            2030-06-03 23:59:00  Getting Started      Knowledge         49           False
https://www.kaggle.com/competitions/connectx                                       2030-01-01 00:00:00  Getting Started      Knowledge        176           False
https://www.kaggle.com/competitions/nlp-getting-started                            2030-01-01 00:00:00  Getting Started      Knowledge        553           False
https://www.kaggle.com/competitions/home-data-for-ml-course                        2030-01-01 23:59:00  Getting Started      Knowledge       5897           False
https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques    2030-01-01 00:00:00  Getting Started      Knowledge       3506           False
https://www.kaggle.com/competitions/titanic                                        2030-01-01 00:00:00  Getting Started      Knowledge      12781            True

データセットのダウンロード (kaggle competitions download)

上記のコマンドで各コンペティションの URL が分かったら、その URL の末尾部分(最後の / 以降)を kaggle competitions download コマンドに渡すことでデータセットをダウンロードできます。 次の例では、コンペティション名 playground-series-s5e2 のデータセットをダウンロードしています(先に Kaggle のサイトで対象のコンペに参加しておく必要があります)。

コンペのデータセットをダウンロード
$ kaggle c download playground-series-s5e2

zip 形式でダウンロードされるので、unzip コマンドなどで展開すればコーディングの準備完了です。

zip ファイルを data ディレクトリに展開
$ unzip playground-series-s5e2.zip -d data
Archive:  playground-series-s5e2.zip
  inflating: data/sample_submission.csv
  inflating: data/test.csv
  inflating: data/train.csv
  inflating: data/training_extra.csv

サブミット/提出 (kaggle competitions submit)

コンペティションに予測結果 (submission.csv) を提出するには、kaggle competitions submit コマンドを使います。

提出
$ kaggle c submit playground-series-s5e2 -f submission.csv -m "Submission message"

これまでの提出履歴とその Public スコアを確認するには、kaggle competitions submissions コマンドを使います。

これまでの提出履歴を表示
$ kaggle c submissions playground-series-s5e2

できたー ٩(๑❛ᴗ❛๑)۶ わーぃ

更新: / 作成:

Hooks とは

SvelteKit には Hooks の仕組みが提供されており、特にサーバー Hooks を定義することで、任意のリクエストに割り込んで処理を行うことができます。 例えば、各ルートのハンドラーが呼び出される前に、次のような処理を行うことができます。

  • リクエストのログを取る
  • リクエストの内容を変更する(リダイレクトなど)
  • リクエストを拒否する(アクセス制御)
  • 後続の処理のためにデータを付加する(Cookie に基づくユーザー情報など)

この記事では、サーバー Hooks の利用例として、簡単な Basic 認証 を実装する方法を解説します。

/p/mem8u54/img-001.png
図: Chrome の Basic 認証ダイアログ

管理者にしかアクセスできないページを手っ取り早く作りたい場合などに便利ですが、Basic 認証はセキュアな認証方式ではないので、あくまで Hooks の使い方の例と考えてください。 本番環境に認証・認可の仕組みを導入する場合は、OAuth などのよりセキュアな方式を検討してください。

サーバー Hooks の基本

サーバー Hooks を定義するのはとても簡単で、SvelteKit のプロジェクト内に src/hooks.server.ts(あるいは .js)を作成し、handle 関数をエクスポートするだけです。 ファイル名を間違えると、Hooks が正しく動作しないので注意してください(特に hook でなく hooks であることに注意)。

次の サーバー Hooks 実装では、リクエストイベントの内容をコンソールに出力しています。

src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
	console.log(JSON.stringify(event, undefined, 2));
	const response = await resolve(event);
	return response;
};
{
	"cookies": {},
	"locals": {},
	"params": {},
	"request": {},
	"route": {
		"id": "/"
	},
	"url": "http://localhost:5173/",
	"isDataRequest": false,
	"isSubRequest": false
}

handle 関数には、event オブジェクト (RequestEvent) と、resolve 関数が渡されます。 resolve 関数は、各ルートに対応するレンダラーを呼び出して、その結果を Response オブジェクトとして返す関数です。 よって、上記のコードのように単純に resolve(event) とすると、デフォルトのレンダリング処理を行った結果を返すことになります。

resolve 関数を呼び出さずに、独自の Response オブジェクトを作成して返すこともできます。 以下の例では、URL パスの最初のセグメントが hello の場合には Hello! というテキストを返し、admin の場合には 403 Forbidden エラーを返しています。

src/hooks.server.ts
import { error, type Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
	// URL パスの最初のセグメントを取得
	const url = new URL(event.request.url);
	const firstSegment = url.pathname.split('/')[1];

	// "/hello" 以下にアクセスした場合
	if (firstSegment === 'hello') {
		return new Response('Hello!');
	}

	// "/admin" 以下にアクセスした場合
	if (firstSegment === 'admin') {
		error(403, 'Forbidden');
	}

	return await resolve(event);
};
☝️ ワンポイント

よく次のように URL パスのプレフィックスだけを見て処理を分岐させている例を見かけますが、このようにすると、/hello だけではなく、/helloworld などにもヒットしてしまいます。

if (event.url.pathname.startsWith('/hello')) {
	return new Response('Hello!');
}

ちゃんとパスをセグメント(コンポーネント)に分割してから比較するようにしましょう。

サーバー Hooks による Basic 認証

Basic 認証の仕組み

Basic 認証の仕組みはとてもシンプルで、次のような流れでブラウザーとサーバーが連携します。

  1. ブラウザーがサーバーにリクエストを送信する。
  2. サーバーがレスポンスに WWW-Authenticate ヘッダーを付加して、認証が必要であることを通知する。
  3. ブラウザーがユーザー名とパスワードを入力するダイアログを表示する。
  4. ブラウザーが <username>:<password> というテキストを Base64 エンコードして、Authorization ヘッダーに付加して再度リクエストを送信する(例: Authorization: Basic XXXXXX)。
  5. サーバーが Authorization ヘッダーを検証して、認証が成功した場合にはリクエストを処理する。

Basic 認証の実装

以下は、SvelteKit のサーバ Hooks を使った Basic 認証の実装例です。 管理者アクセス用のクレデンシャル情報(ユーザー名とパスワード)は、Private な環境変数として、.env ファイル(開発用)、あるいはサーバーの環境変数(本番用)で設定しておきます。 Private な環境変数の扱い方はこちらの記事を参考にしてください → Svelte 入門: 外部 API 用のキーを Private な環境変数で定義する ($env)

.env
ADMIN_CREDENTIALS="user:pass"
src/hooks.server.ts
import { type Handle } from '@sveltejs/kit';
import { createUnauthorizedResponse, validateBasicAuth } from '$lib/basic-auth';

export const handle: Handle = async ({ event, resolve }) => {
	// Basic 認証用のリクエストヘッダーを確認
	if (!validateBasicAuth(event.request)) {
		// 認証に失敗した場合は 401 Unauthorized レスポンスを返す
		return createUnauthorizedResponse();
	}

	// 認証に成功した場合は処理を続行
	return resolve(event);
};
src/lib/basic-auth.ts
import { error } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';

/** Basic 認証用の正しいリクエストヘッダーが含まれているか検証します。 */
export function validateBasicAuth(request: Request): boolean {
	const authHeader = request.headers.get('Authorization');
	if (!authHeader || !authHeader.startsWith('Basic ')) {
		return false;
	}
	const base64Credentials = authHeader.split(' ')[1];
	const credentials = atob(base64Credentials).split(':');
	const [username, password] = credentials;
	return isAdmin(username, password);
}

/** Basic 認証用のダイアログを表示するためのレスポンスを生成します。 */
export function createUnauthorizedResponse(): Response {
	return new Response('Not authorized', {
		status: 401,
		headers: { 'WWW-Authenticate': 'Basic realm="Member Only"' }
	});
}

/** ユーザー名とパスワードが正しいか検証します。 */
function isAdmin(username: string, password: string): boolean {
	const cred = env.ADMIN_CREDENTIALS;
	if (!cred) {
		error(500, 'Server is not properly set up');
	}
	return `${username}:${password}` === cred;
}

pnpm dev コマンドなどで Svelte の開発サーバーを立ち上げて、http://localhost:5173 にアクセスすると、次のように Basic 認証のダイアログが表示されるはずです。

/p/mem8u54/img-001.png
図: Chrome の Basic 認証ダイアログ

できたー ٩(๑❛ᴗ❛๑)۶ わーぃ

関連記事

メニュー

まくろぐ
サイトマップまくへのメッセージ