Goエコシステムには、Gin、Echo、Chi、Fiberなど、さまざまなフレームワークがあります。これらの中で、シンプルさとパフォーマンスの点で最も人気があるのはFiberです。
FiberはExpress.js(Node.jsフレームワーク)に触発されており、Node.js開発者にとってはFiberを始める際に「ああ、なるほど!」という瞬間があります。Fiberについて詳しく見ていき、RESTful APIを構築してみましょう。
前回のブログでは、Ginフレームワークを使用してRESTful APIを構築する方法を探りました。まだチェックしていない場合は、こちらからご覧いただけます。
Fiberについて ⚡️
- 通常のnet/httpパッケージの代わりにfasthttpを使用。
💡 fasthttpパッケージは、Goの標準net/httpパッケージと比べて高性能で低メモリのHTTPサーバーおよびクライアントを提供します。効率性を重視して設計されており、スループットの大幅な向上とレイテンシの低減を実現しています。詳細はこちら
- Expressに触発された構造で、Node.js開発者にとって直感的で学びやすい。
- ロギング、CORS、レート制限などのミドルウェアサポート。
- JSON解析、リクエスト解析、静的ファイルの提供の組み込みサポート。
- WebSockets、テンプレートエンジン、およびテストのサポートが標準で備わっています。
Fiber特有の機能~
fiber.New(): 新しいFiberアプリインスタンスを作成します。カスタムエラーハンドリングなど、構成オプションを渡えることができます。c.BodyParser(&struct): リクエストボディをGoの構造体に自動的に解析します。c.Params("key"): URLパラメータを取得します(例:/books/:id)。c.Query("key"): クエリ文字列の値を取得します(例:?search=golang)。app.Listen(":8080"): 指定したポートでHTTPサーバーを起動します。- ルートハンドラー: 特定のHTTPメソッドとパスのルートを定義します。例:
app.Get()、app.Post()。
サーバーの構築 🚀
簡単なライブラリの本をメモリに保存して管理するRESTful APIを構築します。APIは以下のエンドポイントをサポートします:
- GET /books → すべての本を取得(検索とフィルタリング付き)
- POST /books → 新しい本を追加
- GET /books/:id → 特定の本の詳細を取得
- PUT /books/:id → 本の情報を更新
- DELETE /books/:id → 特定の本を削除
さっそく始めましょう!
Goがシステムにインストールされていることを確認してください。 プロジェクト用の新しいディレクトリを作成し、Goモジュールを初期化し、Fiberパッケージをインストールします。
~ $ mkdir books-api
~ $ cd books-api
~/books-api $ go mod init books-api
~/books-api $ go get github.com/gofiber/fiber/v2
1. シンプルに始める
セットアップをテストするために、最小限のFiberサーバーから始めましょう。main.goというファイルを作成します:
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New() // 新しいFiberアプリを作成する
// 簡単なルートを定義する
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "当社のBooks APIへようこそ! 📚",
})
})
log.Fatal(app.Listen(":8080")) // ポート8080でサーバーを起動する
}
サーバーを次のコマンドで実行します:
~/books-api $ go run main.go
開発中にライブリロードのために
airを設定できます。air
ブラウザを開き、http://localhost:8080 に移動します。次の出力が表示されるはずです:
{
"message": "当社のBooks APIへようこそ! 📚"
}
2. ブックモデルとインメモリストアの定義
💡 この例では簡略化のためインメモリストアを使用しています。本番環境では、GORMのようなORMを備えたデータベース(例:PostgreSQL、MySQL、MongoDB)を使用するのが一般的です。
package main
import (
"errors"
"log"
"strconv"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
)
// Book は当図書館の書籍を表します
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Genre string `json:"genre"`
Pages int `json:"pages"`
Available bool `json:"available"`
PublishedYear int `json:"published_year"`
}
// インメモリの書籍ストア
var library = []Book{
{ID: 1, Title: "The Go Programming Language", Author: "Alan Donovan", Genre: "Technology", Pages: 380, Available: true, PublishedYear: 2015},
{ID: 2, Title: "Clean Code", Author: "Robert Martin", Genre: "Technology", Pages: 464, Available: false, PublishedYear: 2008},
{ID: 3, Title: "The Pragmatic Programmer", Author: "Dave Thomas", Genre: "Technology", Pages: 352, Available: true, PublishedYear: 1999},
}
var nextBookID = 4 // 新しい本のための次のID
3. APIエンドポイントの記述
3.1. GET /books
このエンドポイントは、ジャンルや在庫状況による検索・フィルタリングをオプションで指定して、すべての書籍を取得します。
func getAllBooks(c *fiber.Ctx) error {
result := library
// 検索およびフィルタリングロジックについては、完全なコードを参照してください
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"books": result,
"total": len(result),
})
}
3.2. GET /books/:id
このエンドポイントは、IDによって特定の書籍を取得します。
func findBookByID(id int) (*Book, int, error) {
for i, book := range library {
if book.ID == id {
return &book, i, nil
}
}
return nil, -1, errors.New("書籍が見つかりません")
}
func getBook(c *fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.Atoi(idStr)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "無効な書籍IDです",
})
}
book, _, err := findBookByID(id)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": "書籍が見つかりません",
})
}
return c.Status(fiber.StatusOK).JSON(book)
}
3.3. 新しい本の追加
func createBook(c *fiber.Ctx) error {
var newBook Book
// JSONボディをBook構造体にパース
if err := c.BodyParser(&newBook); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "JSONのパースに失敗しました",
"details": err.Error(),
})
}
// 基本的なバリデーション
if newBook.Title == "" || newBook.Author == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "タイトルと著者は必須です",
})
}
// IDを割り当ててライブラリに追加
newBook.ID = nextBookID
nextBookID++
library = append(library, newBook)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"message": "書籍が正常に追加されました",
"book": newBook,
})
}
更新および削除エンドポイントを確認するには、完全なコードを参照できます。
4. すべてを配線する 🔌
それでは、main.goファイルにグローバルエラーハンドラー、ミドルウェア、ルート定義をまとめて実装しましょう:
func main() {
// カスタムエラーハンドラーを持つFiberアプリを作成
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
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
})
},
})
// ミドルウェア
app.Use(logger.New())
app.Use(cors.New())
// ルート
app.Get("/books", getAllBooks)
app.Post("/books", createBook)
app.Get("/books/:id", getBook)
app.Put("/books/:id", updateBook)
app.Delete("/books/:id", deleteBook)
// サーバー起動
log.Fatal(app.Listen(":8080"))
}
詳細なテストには、Postman や Thunder client などのツールの使用をご検討ください。
まとめ
- Fiberは高性能かつ低メモリ使用を実現するため、fasthttpを基盤に構築されています。
- Goの速度と効率性を保ちつつ、Express.jsのようなシンプルさを求める場合に最適な選択肢です。
- FiberはロギングやCORS対応のミドルウェアを内蔵し、リクエスト解析とルーティング定義を簡潔かつ容易に実現します。
- グローバルなErrorHandler設定やミドルウェアを活用することで、カスタムエラー処理が容易です。
- WebSockets、静的ファイル提供、テンプレートエンジン、マルチパートフォーム解析をネイティブサポート。
- Fiberの詳細は公式ドキュメントでご確認ください。