戻る

GoとFiberでRESTful APIを構築する

ニキータ・ハビャ

ニキータ・ハビャ

8分間の読み物

|

2 month前

Goエコシステムには、GinEchoChiFiberなど、さまざまなフレームワークがあります。これらの中で、シンプルさパフォーマンスの点で最も人気があるのはFiberです。

FiberはExpress.js(Node.jsフレームワーク)に触発されており、Node.js開発者にとってはFiberを始める際に「ああ、なるほど!」という瞬間があります。Fiberについて詳しく見ていき、RESTful APIを構築してみましょう。

前回のブログでは、Ginフレームワークを使用してRESTful APIを構築する方法を探りました。まだチェックしていない場合は、こちらからご覧いただけます。

Fiberについて ⚡️

Fiber特有の機能~

サーバーの構築 🚀

簡単なライブラリの本をメモリに保存して管理するRESTful APIを構築します。APIは以下のエンドポイントをサポートします:

さっそく始めましょう!

Goがシステムにインストールされていることを確認してください。 プロジェクト用の新しいディレクトリを作成し、Goモジュールを初期化し、Fiberパッケージをインストールします。

bash
~ $ 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というファイルを作成します:

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でサーバーを起動する
}

サーバーを次のコマンドで実行します:

bash
~/books-api $ go run main.go

開発中にライブリロードのためにairを設定できます。air

ブラウザを開き、http://localhost:8080 に移動します。次の出力が表示されるはずです:

json
{
    "message": "当社のBooks APIへようこそ! 📚"
}

2. ブックモデルとインメモリストアの定義

💡 この例では簡略化のためインメモリストアを使用しています。本番環境では、GORMのようなORMを備えたデータベース(例:PostgreSQL、MySQL、MongoDB)を使用するのが一般的です。

go
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

このエンドポイントは、ジャンルや在庫状況による検索・フィルタリングをオプションで指定して、すべての書籍を取得します。

go
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によって特定の書籍を取得します。

go
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. 新しい本の追加

go
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ファイルにグローバルエラーハンドラー、ミドルウェア、ルート定義をまとめて実装しましょう:

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"))
}

詳細なテストには、PostmanThunder client などのツールの使用をご検討ください。

まとめ