Fiber
Express.jsライクなGo Webフレームワーク。Node.jsからの移行者に人気で、ストリーミングとWebSocketのネイティブサポートを強化。
GitHub概要
スター37,153
ウォッチ286
フォーク1,825
作成日:2020年1月16日
言語:Go
ライセンス:MIT License
トピックス
expressexpressjsfastfiberflexibleframeworkfriendlygogolanghacktoberfesthacktoberfest2020nodejsperformancerest-apiweb
スター履歴
データ取得日時: 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"))
}