Fiber

Express.jsライクなGo Webフレームワーク。Node.jsからの移行者に人気で、ストリーミングとWebSocketのネイティブサポートを強化。

GoフレームワークWeb開発Express.js風高性能fasthttp

GitHub概要

gofiber/fiber

⚡️ Express inspired web framework written in Go

ホームページ:https://gofiber.io
スター37,153
ウォッチ286
フォーク1,825
作成日:2020年1月16日
言語:Go
ライセンス:MIT License

トピックス

expressexpressjsfastfiberflexibleframeworkfriendlygogolanghacktoberfesthacktoberfest2020nodejsperformancerest-apiweb

スター履歴

gofiber/fiber Star History
データ取得日時: 2025/7/17 05:31

フレームワーク

Fiber

概要

FiberはExpress.jsにインスパイアされたGo言語のWebフレームワークです。高速なfasthttpエンジンを基盤とし、Express.jsライクなAPIで開発体験を向上させます。

詳細

Fiberは2019年にGoの高速HTTPライブラリfasthttpを基盤として開発された、Express.jsライクなWebフレームワークです。Node.jsからGoに移行する開発者の学習コストを削減するため、Express.jsの使いやすいAPIと概念をGoで再現しています。ゼロアロケーション設計により、メモリ使用量を最小限に抑え、極めて高いパフォーマンスを実現しています。ルーティング、ミドルウェアシステム、静的ファイル配信、テンプレートエンジン、WebSocketサポート、レートリミッターなどの豊富な機能を標準搭載しています。また、Reactライクなコンポーネントシステムやリアルタイム通信機能も提供し、モダンなWebアプリケーション開発に必要な要素を包括的にサポートしています。ただし、net/httpとの互換性がないため、Go標準エコシステムの一部ツールは使用できない制約があります。

メリット・デメリット

メリット

  • Express.js風API: Node.js開発者にとって馴染みやすいAPI設計
  • 超高速パフォーマンス: fasthttpベースの極めて高速な処理性能
  • ゼロアロケーション: メモリ効率に優れた設計による低リソース消費
  • 豊富なミドルウェア: 認証、ロギング、CORS、圧縮など充実したミドルウェア
  • 簡単な学習: 直感的で分かりやすいAPI設計
  • WebSocket対応: リアルタイム通信機能の標準サポート
  • アクティブ開発: 活発なコミュニティと継続的な機能追加

デメリット

  • net/http非互換: Goの標準HTTPライブラリとの互換性なし
  • エコシステム制限: 一部のGoツール(gqlgen、go-swaggerなど)が使用不可
  • Learning Express: Express.js知識がない場合の追加学習コスト
  • Immutable制約: デフォルトでは値の不変性が保証されない
  • 依存性: fasthttpライブラリへの依存

主要リンク

書き方の例

Hello World

package main

import (
    "log"
    "github.com/gofiber/fiber/v2"
)

func main() {
    // Fiberアプリインスタンスを作成
    app := fiber.New()

    // GET /hello ルート
    app.Get("/hello", func(c *fiber.Ctx) error {
        return c.SendString("Hello, World!")
    })

    // サーバー起動
    log.Fatal(app.Listen(":3000"))
}

ルーティングとパラメータ

package main

import (
    "log"
    "github.com/gofiber/fiber/v2"
)

func main() {
    app := fiber.New()

    // 基本ルート
    app.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "Welcome to Fiber API",
        })
    })

    // パスパラメータ
    app.Get("/users/:id", func(c *fiber.Ctx) error {
        userID := c.Params("id")
        return c.JSON(fiber.Map{
            "user_id": userID,
            "name":    "ユーザー " + userID,
        })
    })

    // クエリパラメータ
    app.Get("/search", func(c *fiber.Ctx) error {
        query := c.Query("q")
        page := c.Query("page", "1") // デフォルト値

        return c.JSON(fiber.Map{
            "query": query,
            "page":  page,
            "results": []string{"結果1", "結果2"},
        })
    })

    // ワイルドカード
    app.Get("/files/*", func(c *fiber.Ctx) error {
        filename := c.Params("*")
        return c.JSON(fiber.Map{
            "filename": filename,
            "path":     "/files/" + filename,
        })
    })

    // HTTP メソッド
    app.Post("/users", createUser)
    app.Put("/users/:id", updateUser)
    app.Delete("/users/:id", deleteUser)

    log.Fatal(app.Listen(":3000"))
}

func createUser(c *fiber.Ctx) error {
    return c.JSON(fiber.Map{"message": "ユーザーを作成しました"})
}

func updateUser(c *fiber.Ctx) error {
    id := c.Params("id")
    return c.JSON(fiber.Map{"message": "ユーザー " + id + " を更新しました"})
}

func deleteUser(c *fiber.Ctx) error {
    id := c.Params("id")
    return c.JSON(fiber.Map{"message": "ユーザー " + id + " を削除しました"})
}

JSONボディパース

package main

import (
    "log"
    "github.com/gofiber/fiber/v2"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

func main() {
    app := fiber.New()

    // JSONボディパースとバリデーション
    app.Post("/users", func(c *fiber.Ctx) error {
        user := new(User)

        // JSONボディをパース
        if err := c.BodyParser(user); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "JSONの解析に失敗しました",
            })
        }

        // 簡単なバリデーション
        if user.Name == "" {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "名前は必須です",
            })
        }

        if user.Email == "" {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "メールアドレスは必須です",
            })
        }

        // ユーザー処理(データベース保存など)
        // ...

        return c.Status(fiber.StatusCreated).JSON(fiber.Map{
            "message": "ユーザーが作成されました",
            "user":    user,
        })
    })

    // フォームデータパース
    app.Post("/upload", func(c *fiber.Ctx) error {
        name := c.FormValue("name")
        file, err := c.FormFile("file")
        
        if err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "ファイルアップロードエラー",
            })
        }

        return c.JSON(fiber.Map{
            "name":     name,
            "filename": file.Filename,
            "size":     file.Size,
        })
    })

    log.Fatal(app.Listen(":3000"))
}

ミドルウェアの使用

package main

import (
    "fmt"
    "log"
    "time"
    
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/gofiber/fiber/v2/middleware/recover"
    "github.com/gofiber/fiber/v2/middleware/limiter"
)

func main() {
    app := fiber.New()

    // 標準ミドルウェア
    app.Use(logger.New())    // リクエストログ
    app.Use(recover.New())   // パニック回復
    app.Use(cors.New())      // CORS対応

    // レート制限
    app.Use(limiter.New(limiter.Config{
        Max:        20,                      // 20リクエスト/分
        Expiration: 1 * time.Minute,
        KeyGenerator: func(c *fiber.Ctx) string {
            return c.IP()
        },
        LimitReached: func(c *fiber.Ctx) error {
            return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
                "error": "レート制限に達しました",
            })
        },
    }))

    // カスタムミドルウェア
    app.Use(func(c *fiber.Ctx) error {
        fmt.Printf("リクエスト: %s %s\n", c.Method(), c.Path())
        c.Set("X-Custom-Header", "Fiber API")
        return c.Next()
    })

    // 認証ミドルウェア
    app.Use("/api", authMiddleware)

    // パブリックルート
    app.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "Fiber API サーバー",
        })
    })

    // プロテクトされたルート
    app.Get("/api/profile", func(c *fiber.Ctx) error {
        userID := c.Locals("userID")
        return c.JSON(fiber.Map{
            "user_id": userID,
            "message": "認証済みユーザー",
        })
    })

    log.Fatal(app.Listen(":3000"))
}

func authMiddleware(c *fiber.Ctx) error {
    token := c.Get("Authorization")
    
    if token == "" {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
            "error": "認証トークンが必要です",
        })
    }

    // トークン検証ロジック
    if token != "Bearer valid_token" {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
            "error": "無効なトークンです",
        })
    }

    // ユーザー情報をローカルストレージに保存
    c.Locals("userID", "123")
    
    return c.Next()
}

ルートグループ

package main

import (
    "log"
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/basicauth"
)

func main() {
    app := fiber.New()

    // 基本ルート
    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Fiber API")
    })

    // APIグループ(v1)
    v1 := app.Group("/api/v1")
    
    v1.Get("/users", func(c *fiber.Ctx) error {
        return c.JSON([]fiber.Map{
            {"id": 1, "name": "ユーザー1"},
            {"id": 2, "name": "ユーザー2"},
        })
    })

    v1.Get("/posts", func(c *fiber.Ctx) error {
        return c.JSON([]fiber.Map{
            {"id": 1, "title": "投稿1"},
            {"id": 2, "title": "投稿2"},
        })
    })

    // 管理者グループ(Basic認証)
    admin := app.Group("/admin")
    admin.Use(basicauth.New(basicauth.Config{
        Users: map[string]string{
            "admin": "secret",
        },
    }))

    admin.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("管理者ダッシュボード")
    })

    admin.Get("/users", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "全ユーザー管理",
            "users":   []string{"admin", "user1", "user2"},
        })
    })

    // ネストしたグループ
    api := app.Group("/api")
    v1API := api.Group("/v1")
    
    // グループレベルミドルウェア
    v1API.Use(func(c *fiber.Ctx) error {
        c.Set("API-Version", "v1.0")
        return c.Next()
    })

    v1API.Get("/info", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "version": "1.0",
            "status":  "active",
        })
    })

    log.Fatal(app.Listen(":3000"))
}

エラーハンドリング

package main

import (
    "errors"
    "log"
    "github.com/gofiber/fiber/v2"
)

func main() {
    app := fiber.New(fiber.Config{
        // カスタムエラーハンドラー
        ErrorHandler: func(c *fiber.Ctx, err error) error {
            // デフォルトエラーコード
            code := fiber.StatusInternalServerError

            // Fiber エラーの場合、コードを取得
            if e, ok := err.(*fiber.Error); ok {
                code = e.Code
            }

            // カスタムエラーレスポンス
            return c.Status(code).JSON(fiber.Map{
                "error":   true,
                "message": err.Error(),
                "code":    code,
            })
        },
    })

    // 正常なルート
    app.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{
            "message": "正常なレスポンス",
        })
    })

    // 400エラーを返すルート
    app.Get("/bad-request", func(c *fiber.Ctx) error {
        return fiber.NewError(fiber.StatusBadRequest, "不正なリクエストです")
    })

    // カスタムエラー
    app.Get("/custom-error", func(c *fiber.Ctx) error {
        return errors.New("カスタムエラーが発生しました")
    })

    // 404エラーをキャッチ
    app.Use(func(c *fiber.Ctx) error {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "error":   true,
            "message": "ページが見つかりません",
            "code":    404,
        })
    })

    log.Fatal(app.Listen(":3000"))
}

WebSocket接続

package main

import (
    "log"
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/contrib/websocket"
)

func main() {
    app := fiber.New()

    // WebSocketアップグレードミドルウェア
    app.Use("/ws", func(c *fiber.Ctx) error {
        if websocket.IsWebSocketUpgrade(c) {
            c.Locals("allowed", true)
            return c.Next()
        }
        return fiber.ErrUpgradeRequired
    })

    // WebSocketエンドポイント
    app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {
        // 接続情報を取得
        log.Println("WebSocket接続:", c.Params("id"))
        log.Println("許可:", c.Locals("allowed"))

        var (
            mt  int
            msg []byte
            err error
        )

        for {
            // メッセージを読み取り
            if mt, msg, err = c.ReadMessage(); err != nil {
                log.Println("読み取りエラー:", err)
                break
            }

            log.Printf("受信: %s", msg)

            // エコーバック
            if err = c.WriteMessage(mt, msg); err != nil {
                log.Println("書き込みエラー:", err)
                break
            }
        }
    }))

    // 静的ファイル(WebSocketクライアント用)
    app.Static("/", "./static")

    log.Println("サーバー起動: http://localhost:3000")
    log.Println("WebSocket: ws://localhost:3000/ws/123")
    log.Fatal(app.Listen(":3000"))
}