Fiber - Golang learning step 5-3
- 公開日
- カテゴリ:WebFrameworks
- タグ:Golang,roadmap.sh,学習メモ
roadmap.sh > Go > Web Frameworks > Fiber の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
参考 URL
Fiber
Fiber は、Go で非常に人気のある軽量で高速な Web フレームワークです。初心者にとっても学びやすく、Node.js の Express に触発されて設計されているため、Web 開発の経験がある人にも馴染みやすいのが特徴です。
Fiber の特徴
- 高速パフォーマンス
- Fiber は、Go の標準 HTTP パッケージではなく、より高速な Fasthttp の上に構築されており、ゼロメモリアロケーションや最適化されたルーティングを提供します。そのため、パフォーマンスが非常に高く、リアルタイムアプリケーションや高負荷な環境でも利用されています。
- シンプルで直感的な API
- Express.js にインスパイアされた API を採用しており、ルーティングやミドルウェアの設計が直感的でわかりやすいです。初心者でもすぐに使い始められます。
- リッチなエコシステム
- Fiber は公式で多数のミドルウェアを提供しており、認証、CORS、ログ記録、エラーハンドリングなどを簡単に追加できます。
- 柔軟性
- 必要に応じて、Fiber を使いながら Go の標準ライブラリや他のライブラリを組み合わせることが可能です。
開発環境のセットアップ
- プロジェクトの作成
mkdir myapp
cd myapp
go mod init myapp
- Fiber のインストール
go get -u github.com/gofiber/fiber/v2
Fiber の基本構造
Fiber アプリケーションは次のような構造で記述します。下記コードをプロジェクトルートに main.go
として保存します。
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
// アプリケーションの初期化
app := fiber.New()
// ルートハンドラーの定義
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
// サーバーの起動
err := app.Listen(":3000")
if err != nil {
return
}
}
以下のコマンドでサーバーを起動します。
go run main.go
ブラウザで http://localhost:3000
にアクセスすると、「Hello, World!」が表示されます。
ルーティング
Fiber では、直感的なルーティングを設定できます。動的パラメータ、クエリパラメータ、HTTP メソッド、グループルーティングなど、さまざまなルーティング方法をサポートしています。
動的パラメータ
Fiber では、URL の一部をパラメータとして利用できます。
app.Get("/user/:id", func(c *fiber.Ctx) error {
id := c.Params("id") // 動的パラメータを取得
return c.JSON(fiber.Map{
"user_id": id,
})
})
URL /user/123
にアクセスすると、次の JSON が返されます・
{
"user_id": "123"
}
クエリパラメータ
クエリパラメータを取得して処理するのも簡単です。
app.Get("/search", func(c *fiber.Ctx) error {
keyword := c.Query("keyword") // クエリパラメータを取得
return c.JSON(fiber.Map{
"search": keyword,
})
})
URL /search?keyword=golang
にアクセスすると、次の JSON が返されます。
{
"search": "golang"
}
HTTP メソッド
Fiber では GET
、POST
、PUT
、DELETE
など、すべての主要な HTTP メソッドに対応しています。
以下は、POST リクエストで JSON データを受け取り、そのままレスポンスとして返す例です。
app.Post("/user", func(c *fiber.Ctx) error {
// リクエストボディをパース
var user struct {
Name string `json:"name"`
Age int `json:"age"`
}
if err := c.BodyParser(&user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request body",
})
}
return c.JSON(fiber.Map{
"id": 1,
"name": user.Name,
"age": user.Age,
})
})
ターミナルから curl コマンドで POST リクエストを送信します。
curl -X POST http://localhost:3000/user \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "age": 30}'
{
"id": 1,
"name": "John Doe",
"age": 30
}
グループルーティング
Fiber では、関連するルートをグループ化することで管理しやすくできます。
api := app.Group("/api")
api.Get("/users", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "User list"})
})
api.Get("/users/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{"message": "User details for " + id})
})
URL /api/users
にアクセスすると、次の JSON が返されます。
{
"message": "User list"
}
URL /api/users/123
にアクセスすると、次の JSON が返されます。
{
"message": "User details for 123"
}
ミドルウェア
Fiber でも、リクエストとレスポンスの間に共通の処理を挟む「ミドルウェア」を簡単に定義して使用できます。Fiber のミドルウェアは、以下のような用途でよく使われます。
- リクエストログの記録
- 認証や権限チェック
- エラーハンドリング
- リクエストやレスポンスのデータ加工
ミドルウェアの基本構造
Fiber のミドルウェアは次のような形で定義します。
func requestTimingMiddleware(c *fiber.Ctx) error {
start := time.Now() // リクエストの開始時間を記録
// 次のミドルウェアまたはハンドラーを実行
err := c.Next()
// リクエストの終了時間を記録し、処理時間を計算
duration := time.Since(start)
fmt.Printf("Path: %s | Method: %s | Duration: %s\n", c.Path(), c.Method(), duration)
return err
}
ミドルウェアの適用方法
1. 全体に適用する
Fiber では、Use
メソッドを使ってグローバルにミドルウェアを適用します。以下は、すべてのリクエストにカスタムミドルウェアを適用する例です。
app := fiber.New()
// グローバルミドルウェアを登録
app.Use(requestTimingMiddleware)
app.Get("/hello", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
/hello
にアクセスすると、ターミナルに次のようなログが出力されます。
Path: /hello | Method: GET | Duration: 28.375µs
2. 特定のルートだけに適用する
Fiber では、特定のルートに直接ミドルウェアを適用することも可能です。
app.Get("/special", requestTimingMiddleware, func(c *fiber.Ctx) error {
return c.SendString("Special route!")
})
/special
にアクセスすると、リクエスト処理時間が記録されます。/hello
や他のルートには影響しません。
3. グループに適用する
Fiber では、関連するルートをグループ化し、グループ全体にミドルウェアを適用することができます。
api := app.Group("/api")
// グループにミドルウェアを適用
api.Use(requestTimingMiddleware)
api.Get("/users", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "User list"})
})
api.Get("/users/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{"message": "User details for " + id})
})
/api/users
や/api/users/123
にアクセスすると、ターミナルに処理時間が記録されます。/hello
や他のルートには影響しません。
Path: /api/users | Method: GET | Duration: 91.123µs
Path: /api/users/:id | Method: GET | Duration: 34.583µs
エラーハンドリング
Fiber では、エラーハンドリングを簡単に実装できます。リクエストの処理中に発生したエラーをキャッチして、適切なステータスコードやエラーメッセージをレスポンスに設定できます。また、カスタムエラーハンドラーを使用して、全体的なエラー処理を統一することも可能です。
基本的なエラーハンドリング
Fiber の Ctx
を使って、エラーレスポンスを簡単に返せます。
例: JSON パースとエラーチェック
app.Post("/login", func(c *fiber.Ctx) error {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
// JSON データのパースとエラーチェック
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid JSON payload",
})
}
// 必須フィールドのチェック
if req.Username == "" || req.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Username and password are required",
})
}
// 成功レスポンス
return c.JSON(fiber.Map{
"status": "logged in",
})
})
curl コマンドで正常データを送信します。
curl -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "1234"}'
成功レスポンスが返ります。
{
"status": "logged in"
}
不正な JSON を送信した場合
curl -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-d 'invalid json'
失敗レスポンスが返ります。
{
"error": "Invalid JSON payload"
}
カスタムエラーハンドラー
Fiber では、グローバルなエラーハンドリングを設定することができます。以下は、発生したエラーに基づいてカスタムレスポンスを返す例です。
カスタムエラーハンドラーの設定
app := fiber.New(fiber.Config{
// カスタムエラーハンドラーを設定
ErrorHandler: func(c *fiber.Ctx, err error) error {
// ステータスコードを取得 (デフォルトは 500)
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
// エラーの内容をログに出力
fmt.Printf("Error occurred: code=%d, message=%v\n", code, err.Error())
// エラーレスポンスを返す
return c.Status(code).JSON(fiber.Map{
"error": "An error occurred",
})
},
})
// 正常なルート
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
// エラーを強制発生させるルート
app.Get("/error", func(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Bad Request Example")
})
app.Listen(":3000")
正常なリクエスト
curl http://localhost:3000/
# レスポンス
Hello, World!
エラーを発生させるリクエスト
curl http://localhost:3000/error
レスポンス
{
"error": "An error occurred"
}
ターミナルのログ
Error occurred: code=400, message=Bad Request Example
テスト
Go の標準テストパッケージ (testing
) と httptest
パッケージを使えば、Fiber のルートやミドルウェアの動作をテストできます。
以下に、カスタムエラーハンドラーとルート /error
をテストする例を示します。
サンプルコード(main.go
)
package main
import (
"fmt"
"net/http"
"github.com/gofiber/fiber/v2"
)
func NewServer() *fiber.App {
app := fiber.New(fiber.Config{
// カスタムエラーハンドラー
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
// エラーレスポンスを記録
fmt.Printf("Error occurred: code=%d, message=%s\n", code, err.Error())
return c.Status(code).JSON(fiber.Map{"error": err.Error()})
},
})
// 通常ルート
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
// エラーを発生させるルート
app.Get("/error", func(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Bad Request Example")
})
return app
}
func main() {
app := NewServer()
app.Listen(":3000")
}
テストコード(main_test.go
)
NewServer
関数を使ってサーバーインスタンスを作成し、テストを実行します。
package main
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHelloWorld(t *testing.T) {
app := NewServer()
req := httptest.NewRequest(http.MethodGet, "/", nil)
resp, _ := app.Test(req)
assert.Equal(t, http.StatusOK, resp.StatusCode)
// レスポンスボディの内容を読み取る
body := new(strings.Builder)
_, _ = io.Copy(body, resp.Body)
assert.Equal(t, "Hello, World!", body.String())
}
func TestErrorRoute(t *testing.T) {
app := NewServer()
req := httptest.NewRequest(http.MethodGet, "/error", nil)
resp, _ := app.Test(req)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
// レスポンスボディの内容を読み取る
body := new(strings.Builder)
_, _ = io.Copy(body, resp.Body)
assert.JSONEq(t, `{"error": "Bad Request Example"}`, body.String())
}
func TestNotFoundRoute(t *testing.T) {
app := NewServer()
req := httptest.NewRequest(http.MethodGet, "/notfound", nil)
resp, _ := app.Test(req)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
// レスポンスボディの内容を読み取る
body := new(strings.Builder)
_, _ = io.Copy(body, resp.Body)
assert.JSONEq(t, `{"error": "Cannot GET /notfound"}`, body.String())
}
必要なパッケージのインストール
テストに必要な testify
パッケージをインストールします。
go get github.com/stretchr/testify
go mod tidy
テストの実行
以下のコマンドでテストを実行します。
go test -v
実行結果
=== RUN TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
=== RUN TestErrorRoute
Error occurred: code=400, message=Bad Request Example
--- PASS: TestErrorRoute (0.00s)
=== RUN TestNotFoundRoute
Error occurred: code=404, message=Cannot GET /notfound
--- PASS: TestNotFoundRoute (0.00s)
PASS
ok myapp 0.266s
まとめ
- Fiber は Go で高速かつ軽量な Web フレームワーク
Fasthttp
上に構築され、高パフォーマンスを実現- シンプルで直感的な API により初心者にも学びやすい設計
- 公式の豊富なミドルウェアで柔軟な機能拡張が可能
- 基本構造は Express.js に似たシンプルなコードで記述可能
- ルーティング、ミドルウェア、エラーハンドリングなどの機能が充実
- Go の標準テストパッケージで簡単にテスト可能
[Next] Step 5-4: Gorilla
[Prev] Step 5-2: Echo