start-server-core について
TanStack Start アプリを開発する人向けのサーバーサイドランタイムです。サーバー関数、サーバールート、アプリの SSR を処理し、リクエストコンテキストで使うリクエスト、レスポンス、Cookie のユーティリティを提供します。
主な機能
- 01関数、ルート、SSR のリクエストハンドラー
- 02リクエストとレスポンスのユーティリティ
- 03Cookie 管理ヘルパー
- 04AsyncLocalStorage によるリクエストコンテキスト
- 05外部セッションライブラリとの連携基盤
活用シーン
- 01アプリへの受信リクエストの処理
- 02リクエストヘッダーと Cookie の読み取り
- 03レスポンスヘッダーと Cookie の設定
- 04外部セッションライブラリとの接続
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tanstack/ router --skill start-server-coreインストール後、Claude Code で/start-server-coreを入力して読み込まれたか確認します。
詳細
Start Server Core (@tanstack/start-server-core)
TanStack Start のサーバーサイドランタイムです。リクエストハンドラー、リクエスト/レスポンスユーティリティ、Cookie 管理、外部セッションライブラリ向けの統合プリミティブを提供します。リクエスト中は、AsyncLocalStorage を介してコールスタック内のどこからでもすべてのユーティリティを利用できます。
CRITICAL:これらのユーティリティはサーバー専用です。メインのエントリーポイントではなく、
@tanstack/<framework>-start/serverからインポートしてください。サーバーリクエストコンテキスト外で呼び出すとエラーが発生します。CRITICAL:型は完全に推論されます。推論された値をキャストしたり、型注釈を付けたりしないでください。
CRITICAL:Cookie、ヘッダー、リクエスト URL、ランタイム環境の値は、アクティブなリクエスト内で読み取ってください。モジュールスコープで取得しないでください。エッジランタイムではリクエストごとに値が注入される場合があり、並行するリクエスト間でリクエスト由来の状態を共有してはなりません。
createStartHandler
すべての受信リクエストを、サーバー関数、サーバールート、アプリの SSR の3つのフェーズを通じて処理するメインのリクエストハンドラーを作成します。
// src/server.ts
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createStartHandler } from '@tanstack/react-start/server'
import { defaultStreamHandler } from '@tanstack/react-start/server'
export default createStartHandler({
handler: defaultStreamHandler,
})
アセット URL の変換を使用する場合(CDN):
export default createStartHandler({
handler: defaultStreamHandler,
transformAssets: 'https://cdn.example.com',
})
リクエストユーティリティ
すべて @tanstack/<framework>-start/server からインポートします。リクエスト処理中はどこからでも利用でき、パラメーターを渡す必要はありません。
リクエストデータの読み取り
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
getRequest,
getRequestHeaders,
getRequestHeader,
getRequestIP,
getRequestHost,
getRequestUrl,
getRequestProtocol,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'GET' }).handler(async () => {
const request = getRequest()
const headers = getRequestHeaders()
const auth = getRequestHeader('authorization')
const ip = getRequestIP({ xForwardedFor: true })
const host = getRequestHost()
const url = getRequestUrl()
const protocol = getRequestProtocol()
return { ip, host }
})
レスポンスデータの設定
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
setResponseHeader,
setResponseHeaders,
setResponseStatus,
getResponseHeaders,
getResponseHeader,
getResponseStatus,
appendResponseHeader,
removeResponseHeader,
clearResponseHeaders,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'POST' }).handler(async () => {
setResponseStatus(201)
setResponseHeader('x-custom', 'value')
setResponseHeaders({ 'cache-control': 'no-store' })
return { created: true }
})
getResponseHeader と getResponseHeaders は読み取り用のヘルパーです。getResponseHeaders() が返す Headers はスナップショットとして扱ってください。変更しても送信されるレスポンスには反映されません。書き込みには、設定/削除用のヘルパーを使用してください。
appendResponseHeader(name, value) は既存の値を置き換えずに追加します。set-cookie の場合は、完全にシリアライズされた Cookie 文字列を受け取り、Cookie の識別情報(名前 + ドメイン + パス)に基づいてマージします(cookie-es が解析できない値は、文字列が完全に一致する場合に識別されます)。これは、外部のセッション/認証ライブラリとの橋渡しに使うプリミティブです。
appendResponseHeader('set-cookie', await externalSessionLib.commit())
Cookie 管理
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
getCookies,
getCookie,
setCookie,
deleteCookie,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'POST' }).handler(async () => {
const allCookies = getCookies()
const token = getCookie('session-token')
setCookie('preference', 'dark', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 30, // 30 days
path: '/',
})
deleteCookie('old-cookie')
})
外部セッションライブラリ
Start には組み込みのセッション API はありません。外部ライブラリを使用し、Cookie ヘルパー、シリアライズ済みヘッダー、または Fetch ハンドラーを使って、アクティブなリクエストに接続してください。
Cookie アダプターの場合、iron-session 9 では Start の Cookie 関数をそのまま利用できます。
import { getIronSession } from 'iron-session'
import { getCookie, setCookie } from '@tanstack/react-start/server'
// This is an application helper, not a Start export.
export function getAppSession() {
const password = process.env.SESSION_SECRET
if (!password || password.length < 32) {
throw new Error('SESSION_SECRET must be at least 32 characters')
}
return getIronSession<{ userId: string }>(
{ read: getCookie, write: setCookie },
{
cookieName: 'app-session',
password,
ttl: 7 * 24 * 60 * 60,
cookieOptions: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
},
},
)
}
// After validating credentials in a server function:
const session = await getAppSession()
session.userId = user.id
await session.save()
// In a logout handler:
const logoutSession = await getAppSession()
logoutSession.destroy()
読み取り時には、ユーザーが存在しない場合のチェックを行い、session.userId を使用してください。操作ごとにセッションオブジェクトを1つ使うか、ミドルウェアからリクエストコンテキストを通じて渡してください。アダプターは受信した Cookie を読み取ります。再度呼び出しても、保留中のレスポンス Cookie は読み取りません。書き込みは、返却またはリダイレクトする前に await してください。セッションオブジェクトやリクエスト由来の設定をモジュールスコープに置かないでください。
iron-session はシールを解除するときに ttl を確認し、保存のたびにその有効期間を更新します。有効期限、ローテーション、ストレージの動作は、明示的に選択してテストしてください。
Fetch の Request を受け取り、Response を返すライブラリの場合は、現在のリクエストを渡し、サーバールートからライブラリのレスポンスを返してください。Cookie のみを受け渡す場合は、response.headers.getSetCookie() の各値を個別に追加してください。Expires 属性内にカンマが含まれる場合があります。無関係な Cookie に追加するために setResponseHeader('set-cookie', ...) を使用しないでください。
本番環境でのセッションに関するルール
- 認証情報とセッションオブジェクトはサーバーコード内に保持し、リクエストごとに新しいオブジェクトを使用してください。
- アカウントの現在の権限は、信頼できるストアから読み込んでください。クライアントルートガードでは、サーバー関数の認可はできません。
- 即時の失効、デバイス追跡、大量のデータには、サーバーサイドのセッションレコードを使用してください。Cookie を削除しても、ステートレスな認証情報のコピーは無効になりません。
- HTTPS のデプロイでは、
HttpOnly、適切なSameSite、Path=/、Secureを使用してください。Cookie フラグは CSRF 保護を補完するものです。 - ログイン、認証済み SSR とナビゲーション、リダイレクト前の永続化、有効期限切れ、ログアウト、並行リクエストをテストしてください。Start はリクエスト/レスポンスの処理を担い、セッションの動作はアプリケーションとセッションライブラリが担います。
リクエスト処理の仕組み
createStartHandler はリクエストを次の 3 つのフェーズで処理します。
-
サーバー関数のディスパッチ — URL がサーバー関数のプレフィックス(
/_serverFn)に一致した場合、ペイロードをデシリアライズし、グローバルリクエストミドルウェアを実行してからサーバー関数を実行し、シリアライズした結果を返します。 -
サーバールートハンドラー — サーバー関数以外のリクエストの場合、URL を
server.handlersを持つルートと照合します。ルートミドルウェアを実行し、一致した HTTP メソッドのハンドラーを実行します。ハンドラーはResponseを返すか、next()を呼び出して SSR に処理を引き継ぐことができます。 -
アプリルーターの SSR — すべてのルートローダーを読み込み、クライアントでのハイドレーションに備えて状態をデハイドレーションし、ハンドラーのコールバック(例:
defaultStreamHandler)を呼び出して HTML をレンダリングします。
よくある間違い
1. 重大:クライアントコードでサーバーユーティリティをインポートする
サーバーユーティリティは AsyncLocalStorage を使用しており、サーバーでのリクエスト処理中にのみ動作します。クライアントコードでインポートすると、ビルドエラーや実行時クラッシュが発生します。
// WRONG — importing in a component file that runs on client
import { getCookie } from '@tanstack/react-start/server'
function MyComponent() {
const token = getCookie('auth') // crashes on client
}
// CORRECT — use inside server functions only
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import { getCookie } from '@tanstack/react-start/server'
const getAuth = createServerFn({ method: 'GET' }).handler(async () => {
return getCookie('auth')
})
2. 高:外部セッションの永続化を忘れる
セッションオブジェクトを変更しても、必ずしも Cookie が書き込まれるわけではありません。ライブラリが提供する明示的な保存またはコミット API を使用し、レスポンスを返すかリダイレクトする前にその完了を待ってください。シリアライズされた Set-Cookie の値はそれぞれ個別に追加し、他のミドルウェアの Cookie を保持してください。
3. 中:本番環境で HTTPS を使わずにセッションを使用する
本番環境では、セッション Cookie に secure: true を設定してください。デフォルトの Cookie オプションでは、これが強制されない場合があります。
4. 重大:モジュールスコープでリクエストまたは環境の状態を取得する
モジュールの読み込み時に process.env からセッション設定を作成したり、getRequest()、ヘッダー、Cookie、セッションデータをモジュール変数にキャッシュしたりしないでください。設定の作成とリクエスト状態の読み取りは、ハンドラーまたはミドルウェアのコールバック内で行ってください。これはリクエストごとに分離されたエッジ環境に必要であり、リクエスト間のデータ漏洩を防ぎます。
関連項目
@tanstack/start-client-core#start-core/server-functions— これらのユーティリティを使用するサーバー関数の作成@tanstack/start-client-core#start-core/middleware— リクエストミドルウェア@tanstack/start-client-core#start-core/server-routes— サーバールートハンドラー