💡

TanStack Startがいい感じだったので調べました

2025/02/18に公開

TanStack Startについて

https://tanstack.com/start/latest
TanStack Routerで作られてるフルスタックフレームワークとのことで、ちょっと調べてみました。
結論から言うと個人的にはServer Functionsが他にはない設計で便利そうだと思いました。
今回は社内の勉強会用にまとめましたので、参考までに読んでみてください。

TanStack Startの特徴

  • Server Functions: クライアントとサーバーの処理を同一プロジェクト内で明示的に分離し、型安全に実装できる。
  • 型安全なルーティング: ルーティングはTanStack Routerで行われるので、明示的にルートツリーを定義することで型安全に実装できる。
  • 完全なSSRとストリーミング: 完全なHTMLドキュメントを生成し、ストリーミングにより部分的にコンテンツを更新できる。

公式には他にもいろいろありましたが、気になったところをピックアップしました。
今回はServer Functionsを中心に紹介します。

Server Functions

これはどんな機能かというと、クライアントからも呼ぶことができるサーバー上の関数です。フック関数からも呼び出すことができたりします。
Remote Procedure Callですかね?(公式に書いてあるから、たぶんそう)
これはサーバー上の関数なのでサーバー上のDBからデータを取得したり、ユーザーには見えないように環境変数にセットしたAPIキーを使って外部サーバーに問い合わせることもできます。
関数なので入力パラメータや戻り値に対して完全な型チェックが効くため、型安全に実装ができます。

TanStack StartのServer Functionsと、Next.jsのServer Componentはどちらもサーバー側でコードを実行する仕組みなのですが、いくつかの点で設計思想と使い方に違いがあるかと思います。

Server Componentとの違い

コンポーネント自体をサーバーでレンダリングする仕組みです。これにより、データフェッチや重い計算処理をサーバー側で行い、クライアントには既にレンダリングされたHTMLを送ることができます。
TanStack StartのServer Functionsは、あくまで「関数」として独立したAPIを提供するもので、コンポーネント全体を置き換えるのではなく、特定の処理(たとえばデータ取得や計算)を担当します。

Next.jsでは、Server Componentはコンポーネントツリーの中で自動的にサーバー側で実行されるのでクライアント側から明示的に呼び出すことはできないです。コンポーネント内でデータフェッチを完結させるので、クライアントとサーバーのコードが混在しやすくなっていました。
Server Functionsであれば、コードの分離がしやすいというメリットがあります。

実装

下記が公式にも記載されているサンプルコードです。

const filePath = 'count.txt'
async function readCount() {
  return parseInt(
    await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
  )
}

const getCount = createServerFn({
  method: 'GET',
}).handler(() => {
  return readCount()
})

readCount関数の役割

async function readCount() {
  return parseInt(
    await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
  )
}

ファイルパス 'count.txt' を指定し、Node.jsのfs.promises.readFileで非同期にファイルを読み込んでいます。ファイルが存在しないなどの理由で読み込みに失敗した場合は、.catch(() => '0')によりデフォルト値 '0' を返しています。
エラー時のフォールバックを設けることで、常に数値を返すことを保証し、型安全な実装となっています。

Server Functionの定義 (getCount)

const getCount = createServerFn({
  method: 'GET',
}).handler(() => {
  return readCount()
})

createServerFnを利用して、HTTPメソッドをGETに指定したServer Functionを作成しています。
.handler()でハンドラーを定義し、その中でreadCount()を呼び出すことで、クライアントから実行されるとサーバー側でファイルの内容を読み取ります。実際にビルドされた場合には、読み取った数値を返すAPIとして機能します。(すごい)
クライアント側ではあたかも通常の関数を呼び出す感覚で、サーバー上の処理(ファイル読み込みなど)を実行できる点が大きなメリットかな、と思います。

型安全性とコードの分離

入力パラメータや戻り値に対して型チェックが効くため、予期せぬ型エラーが発生しにくく、開発効率と保守性が向上します。
また、サーバー側の処理(ここではファイルの読み込み)を明確に切り出すことで、クライアント側のコードと役割を分離できて、責務が整理された設計になります。

まとめ

TanStack Startは、Server Functionsでクライアントとサーバー間の処理を明示的に分離しながら、型安全な実装ができます!

Next.jsのServer Componentとの比較

Next.jsのServer Componentはコンポーネント全体をサーバー側でレンダリングする仕組みですが、TanStack StartのServer Functionsは特定の処理を切り出して呼び出せるため、より柔軟にコードの分離や再利用が可能です。

開発体験の向上

サーバー上でのみ実行する処理(例:ファイルI/Oや環境変数を使った外部APIの呼び出し)を明示的に分けることもできて、チーム内での開発やコードレビュー時にも理解しやすくなります。

このように、TanStack Startの設計は、モダンなフロントエンド開発における「サーバーとクライアントの役割分担」と「型安全性」を期待できると思っています!

xtone tech blog

Discussion