Zenn
🔀

Svelte + Hono + Turso でAIプランニングツール「Plathlo」を爆速開発した話

2025/02/25に公開
3

はじめに

フローチャート形式で様々なシナリオを想定したプランニングやタスクの進行管理ができる「Plathlo(プラスロ)」というサービスをリリースしたので、開発背景、技術選定、工夫した点など、技術的な側面にフォーカスして紹介します。

https://plathlo.com

Plathloとは?

https://x.com/kosei_28/status/1893716581575705004

Plathloは、タスク(判断が不要な作業)と分岐(シナリオ選択)を組み合わせることで、柔軟な計画設計を可能にするツールです。従来のタスク管理ツールとは異なり、以下のような特徴があります。

  • シナリオ分岐:
    進行状況や選択結果に応じて、複数のシナリオを事前に想定し、フローチャート上に表現できます。
  • 可視化:
    選択したシナリオ、実行予定のタスク、除外されたタスクなどが明確に可視化され、計画の全体像と現在地を容易に把握できます。
  • 並列化と合流:
    タスクの並列実行や、分岐したシナリオの合流も可能で、複雑なプロジェクトや複数のチームが関わるプロジェクトにも柔軟に対応します。
  • AIによるフローチャート生成:
    Gemini API (Gemini 2.0 Flash)を活用し、ユーザーが入力したテキスト情報に基づいてフローチャートを自動生成する機能を搭載しています。

これらの機能により、Plathloはプロジェクトの計画、実行、管理をより効率的かつ効果的にサポートします。

1週間での超高速開発を支えたアプローチ

Plathloは、1週間という短期間で個人で開発しました。このスピード開発を実現するため、以下のアプローチを採用しました。

  1. プロトタイピング重視:
    まず、フロントエンドのみのフローチャート編集画面をプロトタイプとして開発し、価値検証を行いました。その後、プロトタイプのコードをベースに本格的な開発へ移行しました。
  2. UIデザインは実装しながら:
    Figmaなどのデザインツールは使わず、直接コードを書きながらUIを最適化。これにより、開発時間を大幅に短縮しつつ、実用的なUIを実現しました。
  3. ドッグフーディング:
    自身がPlathloを日常的に使用することで、ユーザビリティの課題や改善点を早期に発見し、迅速に修正しました。

技術スタック

  • フロントエンド
    • Svelte 5 / SvelteKit
    • Svelte Flow
    • Panda CSS
    • Zag
  • バックエンド
    • Hono
    • Drizzle ORM
  • データベース
    • Turso
  • 認証
    • Better Auth
  • AI
    • Gemini API (Gemini 2.0 Flash)
  • ホスティング
    • Cloudflare Pages

Svelte

Svelteを採用したのは、非常に簡単に高速・軽量なWebアプリケーションを構築できるためです。仮想DOMを使用せずコンパイル時にパフォーマンスが最適化されるため、Reactのような再レンダリングに起因するパフォーマンス低下をほとんど気にする必要がありません。

Svelte 5では、Runesが導入され、より宣言的な記法で記述でき、コールバック関数をPropsとして渡せるため、イベント処理の実装が非常に容易になりました。Plathlo開発中にもZag v1.0がリリースされるなど、Svelte 5のエコシステムも整備されてきており、開発がより快適になっています。

また、SvelteKitのファイルベースルーティングの設計が非常に良くできており、サーバーサイドのコードを完全に分離できるため、非常に直感的かつ安全に開発を進めることができます。

Hono

Honoは、軽量で高速なWebフレームワークです。RPC機能の活用によって型安全な通信を実現し、今回の高速開発を支えました。

SvelteKitに直接Honoをマウントすることで、APIサーバーとアプリ本体のデプロイを1つに統合し、管理を容易にしました。SvelteKitのhooks.server.ts内でHonoのリクエストハンドラを処理する設定により、シームレスな統合を実現しています。

SvelteKitにはサーバーサイドでネットワークを介さずに直接エンドポイントを呼び出せるfetch関数が用意されているため、HonoのRPC機能との親和性が非常に高いです。

Turso

Tursoは、libSQL (SQLite)ベースの分散データベースです。無料枠が大きく、スケーラビリティに優れています。

SQLiteベースのデータベースを採用することによって、ローカルではコンテナなどを用いなくても、インメモリやファイルで簡単にデータベースを用意できるため、環境構築の手間を大幅に削減することができます。特に、テスト用に独立したデータベースを用意する際に非常に便利です。

当初、Cloudflare D1の利用も検討しましたが、現状では10GBまでの上限があり、将来的なスケールを考慮した結果、より柔軟な運用が可能なTursoを選択しました。

運用コストを抑える

個人開発の場合、とにかく運用コストを抑えることも重要です。

Cloudflare PagesとTursoを利用することで、ホスティングやデータベースのコストを最小限に抑えることができます。SvelteKitは公式でCloudflare Pagesに対応しており、Svelteの採用はCloudflare Pagesの採用から逆算した結果でもあります。

これらの工夫により、スケールしてもコストは最小限に抑えられ、予測もしやすい設計となっています。

技術的な工夫

Better Authによる認証

認証にはBetter Authというライブラリを利用しました。Better AuthはスキーマやORM、フレームワークなどを柔軟に選択できる上で、非常に簡単なAPIで利用することができるため、他のライブラリに対する優位性が高いと感じています。

セキュリティは非常に重要な要素であるため、IDaaSを利用することも選択肢の1つではありますが、運用コストやデータベースとの連携の容易さなどを考慮すると、採用に慎重になってしまうこともあります。

Better Authなどの認証ライブラリを採用したとしても、Googleログインなどのソーシャルログインを利用し、自前での認証情報の管理を最小限にすることで、ある程度容易にセキュリティを確保することができます。

より型安全なHono RPCの実現

Hono RPCのレスポンス型の工夫によって予測不可能なエラーなどに対する型安全性が向上し、これにより破壊的な変更を躊躇することなく行えるようになりました。

ハンドラ内で明示的に返していないレスポンスの型情報は付与されないため、Honoによって提供される型には、500エラーやミドルウェアのzod validatorが返す400エラーなどは一切含まれません。このままでは、本来実装すべきエラーハンドリングがなくても型エラーが発生しないため、実装漏れが発生する可能性が高くなります。

これに対し、以下のようにHonoのレスポンスの型を拡張することで、考慮するべきレスポンスのステータスコードを示すことができるようにしました。

import { Hono, type Schema } from 'hono';
import type { HonoBase } from 'hono/hono-base';
import type { Endpoint, ResponseFormat } from 'hono/types';
import type {
    ClientErrorStatusCode,
    ServerErrorStatusCode,
    StatusCode
} from 'hono/utils/http-status';

type SchemaWithUnknownResponse<
    T extends Schema,
    U extends StatusCode = ClientErrorStatusCode | ServerErrorStatusCode
> =
    T extends Record<infer K, Schema>
        ? K extends string
            ? {
                    [P in K]: T[P] extends Record<infer K2, Endpoint>
                        ? {
                                [M in K2]:
                                    | T[P][M]
                                    | {
                                            input: T[P][M]['input'];
                                            output: unknown;
                                            outputFormat: ResponseFormat;
                                            status: U;
                                      };
                            }
                        : never;
                }
            : never
        : never;

type WithUnknownResponse<
    T,
    U extends StatusCode = ClientErrorStatusCode | ServerErrorStatusCode
> =
    T extends HonoBase<infer E, infer S, infer B>
        ? HonoBase<E, SchemaWithUnknownResponse<S, U>, B>
        : never;

export const app = new Hono();
export type App = WithUnknownResponse<typeof app>;

この例では、すべてのエラーステータスコードに対してunknown型を返すようにしています。

特定のステータスコードに対して明示的な型を指定することもできますが、そうしてしまうと全てのハンドラがその型に対応しないレスポンスを返さないようにする必要があるため、かえって型の不整合が発生しやすくなります。

ほとんどの場合、エラーハンドリングはステータスコードによる分岐のみで十分であり、レスポンスボディの使用を避けるように設計できるため、unknown型であっても問題はありません。

AI生成の精度向上

現在はアップデートにより、AI生成の精度が向上しており、不正なデータが出力されることはほとんどありません。また、出力させるデータ構造を工夫することで、並列や分岐の合流も可能になりました。ぜひ試してみてください!

AIによるフローチャート生成では、Structured outputを利用して型安全性は保証されていたものの、ID参照の誤りなどを含む不正なデータが出力されるという問題が頻発していました。内部のデータ構造ではノードとエッジを分離して管理しており、依存関係が複雑になっていたため、LLMが正確に解釈することが困難になっていたのではないかと考えられます。

これを改善するために、AIに生成させるデータ構造を工夫し、子ノードが親ノードのIDを直接参照する形に変更しました。この変更により、AI生成では並列や分岐の合流が不可能という制限はありますが、実用的なレベルの出力を得ることができました。

それでも不正なデータが出力される場合はありますが、プロンプトの調整や今後のモデル精度向上などで改善していきたいと考えています。

今後の展望

Plathloは、Svelte, Hono, Tursoを中心としたモダンな技術スタックを採用することで、個人開発ながら高速かつ効率的な開発を実現しました。運用コストを最小限に抑えつつ、スケーラビリティも考慮したアーキテクチャにより、長期的な運用が可能な設計となっています。

今後は、ユーザーからのフィードバックを基に機能追加や改善を継続し、より使いやすいサービスへと進化させていくとともに、AI生成の精度向上や高度なフローチャート編集機能の実装にも取り組んでいく予定です。

ご意見、ご感想、バグ報告などがありましたら、お気軽にご報告ください。

https://plathlo.com

3

Discussion

ログインするとコメントできます