elysia の使い方

Ergonomic Framework for Human

v1.4.28/週MITWeb API
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Elysia の使い方 — Bun 向け高速 Web フレームワーク完全ガイド

一言でいうと

Elysia は Bun ランタイム上で動作する、エンドツーエンドの型安全性を備えた高速 Web フレームワークです。Express の約18倍のパフォーマンスを謳い、TypeScript のエルゴノミクス(開発者体験)を極限まで追求した設計が特徴です。

どんな時に使う?

  • Bun で高速な REST API / GraphQL サーバーを構築したいとき — Bun のネイティブ HTTP サーバーを活かし、最小限のオーバーヘッドで API を構築できます
  • フロントエンドとバックエンドで型を共有したいとき — tRPC のようなエンドツーエンド型安全を、REST スタイルの API で実現できます(Eden というクライアントコネクタを提供)
  • Express / Fastify からの移行で、よりモダンかつ高速なフレームワークを探しているとき — プラグインエコシステムが充実しており、Swagger ドキュメント自動生成、JWT 認証、CORS などが公式プラグインとして揃っています

インストール

Elysia は Bun ランタイムが必須です。事前に Bun をインストールしてください。

# プロジェクトの雛形を作成(推奨)
bun create elysia app

# 既存プロジェクトに追加する場合
bun add elysia

# npm / yarn / pnpm でもインストール可能ですが、実行は Bun が必要です
npm install elysia
yarn add elysia
pnpm add elysia

注意: Elysia は Bun のネイティブ API に依存しているため、Node.js 単体では動作しません。

基本的な使い方

最もシンプルな Hello World サーバーです。

// src/index.ts
import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/', () => 'Hello, Elysia!')
  .get('/user/:id', ({ params: { id } }) => `User: ${id}`)
  .post('/user', ({ body }) => body, {
    body: t.Object({
      name: t.String(),
      email: t.String()
    })
  })
  .listen(3000)

console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}`)
bun run src/index.ts

上記の t はバリデーション用のスキーマビルダーです。次のセクションで詳しく解説します。

よく使う API

1. ルーティング(GET / POST / PUT / DELETE)

Elysia はメソッドチェーンでルートを定義します。コンテキストオブジェクトから paramsquerybodyheaders などに型安全にアクセスできます。

import { Elysia } from 'elysia'

const app = new Elysia()
  .get('/posts', () => {
    return [{ id: 1, title: 'Hello' }]
  })
  .get('/posts/:id', ({ params: { id } }) => {
    // id は自動的に string 型として推論される
    return { id, title: 'Hello' }
  })
  .post('/posts', ({ body }) => {
    return { message: 'Created', data: body }
  })
  .put('/posts/:id', ({ params: { id }, body }) => {
    return { message: `Updated ${id}`, data: body }
  })
  .delete('/posts/:id', ({ params: { id } }) => {
    return { message: `Deleted ${id}` }
  })
  .listen(3000)

2. バリデーション(t スキーマ)

Elysia は内部で TypeBox を採用しており、t オブジェクトでリクエスト / レスポンスのスキーマを宣言的に定義できます。定義したスキーマは ランタイムバリデーションTypeScript の型推論 の両方に使われます。

import { Elysia, t } from 'elysia'

const app = new Elysia()
  .post(
    '/user',
    ({ body }) => {
      // body は { name: string; age: number } として型推論される
      return { message: `Hello, ${body.name}! You are ${body.age} years old.` }
    },
    {
      body: t.Object({
        name: t.String({ minLength: 1 }),
        age: t.Number({ minimum: 0 })
      }),
      response: t.Object({
        message: t.String()
      })
    }
  )
  .get(
    '/search',
    ({ query }) => {
      // query.keyword は string 型として推論される
      return { results: [], keyword: query.keyword }
    },
    {
      query: t.Object({
        keyword: t.String(),
        page: t.Optional(t.Numeric({ default: 1 }))
      })
    }
  )
  .listen(3000)

バリデーションエラーが発生すると、Elysia は自動的に 422 Unprocessable Entity を返します。

3. ライフサイクルフック(onBeforeHandle / onAfterHandle / onError

リクエストのライフサイクルにフックを挿入して、認証チェックやロギングなどの横断的関心事を処理できます。

import { Elysia } from 'elysia'

const app = new Elysia()
  // 全ルートの前に実行される
  .onBeforeHandle(({ request }) => {
    console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`)
  })
  // レスポンス後に実行される
  .onAfterHandle(({ response }) => {
    console.log('Response sent:', typeof response)
  })
  // エラーハンドリング
  .onError(({ code, error }) => {
    if (code === 'NOT_FOUND') {
      return { message: 'Route not found' }
    }
    console.error(error)
    return { message: 'Internal Server Error' }
  })
  .get('/', () => 'Hello!')
  .listen(3000)

4. プラグインシステム(.use()

Elysia のプラグインは Elysia インスタンスそのもの を返す関数です。型情報が完全に伝播するため、プラグインが追加したデコレータやルートも型安全に利用できます。

import { Elysia } from 'elysia'
import { swagger } from '@elysiajs/swagger'
import { cors } from '@elysiajs/cors'

// カスタムプラグインの定義
const authPlugin = new Elysia({ name: 'auth' })
  .derive(({ headers }) => {
    const token = headers.authorization?.replace('Bearer ', '')
    return {
      userId: token ? decodeToken(token) : null
    }
  })

function decodeToken(token: string): string | null {
  // 実際のトークンデコード処理
  return 'user-123'
}

const app = new Elysia()
  .use(swagger())       // /swagger に自動ドキュメント生成
  .use(cors())           // CORS 設定
  .use(authPlugin)       // カスタム認証プラグイン
  .get('/profile', ({ userId }) => {
    // userId は authPlugin の derive により型推論される
    if (!userId) return { error: 'Unauthorized' }
    return { userId }
  })
  .listen(3000)

主要な公式プラグイン:

プラグインパッケージ名用途
Swagger@elysiajs/swaggerOpenAPI ドキュメント自動生成
CORS@elysiajs/corsCORS ヘッダー設定
JWT@elysiajs/jwtJWT 認証
Static@elysiajs/static静的ファイル配信
Bearer@elysiajs/bearerBearer トークン抽出

5. グループ化と Eden(エンドツーエンド型安全クライアント)

グループ化 でルートをモジュール分割し、Eden でフロントエンドからバックエンドの型を直接参照できます。

// server.ts
import { Elysia, t } from 'elysia'

const userRoutes = new Elysia({ prefix: '/user' })
  .get('/', () => [{ id: 1, name: 'Alice' }])
  .get('/:id', ({ params: { id } }) => ({ id, name: 'Alice' }), {
    params: t.Object({ id: t.Numeric() })
  })
  .post('/', ({ body }) => ({ ...body, id: 2 }), {
    body: t.Object({
      name: t.String(),
      email: t.String()
    })
  })

const app = new Elysia()
  .use(userRoutes)
  .listen(3000)

// 型をエクスポート
export type App = typeof app
// client.ts(フロントエンド側)
import { treaty } from '@elysiajs/eden'
import type { App } from './server'

const api = treaty<App>('http://localhost:3000')

// 完全に型安全な API 呼び出し
const { data, error } = await api.user.get()
// data は { id: number; name: string }[] として推論される

const { data: user } = await api.user({ id: 1 }).get()
// user は { id: number; name: string } として推論される

const { data: newUser } = await api.user.post({
  name: 'Bob',
  email: 'bob@example.com'
})
// newUser は { name: string; email: string; id: number } として推論される

類似パッケージとの比較

特徴ElysiaHonoExpressFastify
ランタイムBun(必須)Bun / Node / Deno / EdgeNodeNode
型安全性◎ エンドツーエンド○ RPC モード対応△ 手動定義○ スキーマ定義
パフォーマンス◎ 非常に高速◎ 高速△ 低速○ 高速
バリデーション組み込み(TypeBox)Zod 等を別途利用別途ライブラリ必要組み込み(JSON Schema)
エコシステム○ 成長中○ 成長中◎ 非常に豊富◎ 豊富
クライアント連携Eden(公式)hc(公式)なしなし
学習コスト低い低い低い中程度

選定の目安:

  • Bun を使うことが確定しており、最高のパフォーマンスと型安全性を求めるなら → Elysia
  • マルチランタイム対応が必要なら → Hono
  • 既存の Node.js エコシステムを活用したいなら → Fastify / Express

注意点・Tips

⚠️ Bun ランタイムが必須

Elysia は Bun のネイティブ HTTP サーバー(Bun.serve)に依存しています。Node.js や Deno では動作しません。CI/CD 環境やデプロイ先でも Bun が利用可能であることを確認してください。

⚠️ メソッドチェーンの順序が型推論に影響する

Elysia の型推論はメソッドチェーンの順序に依存します。derivestate で追加したコンテキストは、それ以降に定義されたルートでのみ型として認識されます。

// ❌ authPlugin の derive より前に定義されたルートでは userId が使えない
const app = new Elysia()
  .get('/before', ({ userId }) => userId) // 型エラー
  .use(authPlugin)
  .get('/after', ({ userId }) => userId)  // OK

💡 t.Numeric()t.Number() の違い

URL パラメータやクエリ文字列は常に string として届きます。t.Numeric() を使うと、文字列を自動的に数値に変換してくれます。

// params.id は number 型になる
.get('/user/:id', ({ params }) => params.id, {
  params: t.Object({ id: t.Numeric() })
})

💡 group でルートを整理する

大規模アプリケーションでは、ルートをファイル分割して use で結合するパターンが推奨されます。各ファイルで new Elysia({ prefix: '/xxx' }) を使うことで、自然にモジュール分割できます。

💡 Swagger ドキュメントを活用する

@elysiajs/swagger を追加するだけで、t スキーマから OpenAPI ドキュメントが自動生成されます。スキーマを丁寧に定義しておけば、フロントエンドチームとの API 仕様共有が格段に楽になります。

import { Elysia } from 'elysia'
import { swagger } from '@elysiajs/swagger'

const app = new Elysia()

比較記事