Skip to content

swaggoでAPIドキュメント作成 ​

API設計書の作成方法について説明します。swagによってコメントからswaggerを自動生成するようにしています。

cf.

ライブラリインストール ​

下記コマンドでswagおよびhttp-swaggerをインストールします:

shell
go install github.com/swaggo/swag/cmd/swag@latest
go get -u github.com/swaggo/http-swagger

下記コマンドでswaggerドキュメントを初期化します:

shell
swag init -o ./api -g cmd/server/main.go

実装 ​

cmd/server/main.go ​

アプリケーションの概要およびメタ情報を追記します。

go
package main

import (
	"easyapp/internal/bootstrap"
	"fmt"
	"log"
	"net/http"
)

// @title Easy App
// @version 1.0.0
// @description サンプルのREST APIです。
//
// @BasePath /api/v1
func main() {

	fmt.Println("Server started at http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", bootstrap.NewServer()))
}

bootstrap/server.go ​

swaggerページへのルーティングを設定します。

go
package bootstrap

import (
	"easyapp/internal/application/usecase"
	"easyapp/internal/infrastructure"
	"easyapp/internal/infrastructure/repository"
	"easyapp/internal/presentation/handler"
	"easyapp/internal/presentation/router"
	"net/http"

	_ "easyapp/api" // apiパッケージ配下のドキュメントをimport

	httpSwagger "github.com/swaggo/http-swagger" // http-swaggerをimport
)

// 依存性の注入を行い、アプリケーションの構築を行います。
func NewServer() http.Handler {

	// データベースに接続
	db := infrastructure.ConnectDB()

	// 各handlerに紐づくルーティングを設定
	m := http.NewServeMux()

	// swagger UIのルーティング
	m.Handle("/swagger/", httpSwagger.WrapHandler)

	// user
	router.SetUserHandlerRouting(
		m,
		handler.NewUserHandler(
			usecase.NewUserUsecase(
				repository.NewUserRepository(
					db,
				),
			),
		),
	)

	return m
}

handler/presentation/user_handler.go ​

各APIのインターフェース仕様を関数部分に記載します:

go
package handler

import (
	"easyapp/internal/application/usecase"
	"easyapp/internal/application/usecase/params"
	"easyapp/internal/presentation/handler/httperror"
	"easyapp/internal/presentation/handler/model"
	"encoding/json"
	"net/http"
)

// 認証のhandlerです。
type UserHandler struct {
	userUsecase usecase.UserUsecase
}

func NewUserHandler(userUsecase usecase.UserUsecase) UserHandler {
	return UserHandler{userUsecase: userUsecase}
}

// @Summary 認証
// @Description 認証処理を行います。リクエストに不備があった場合はエラーレスポンスを返します。
// @Tags User
// @Accept json
// @Produce json
// @Param loginRequest body model.loginRequest true "認証向けのリクエストモデル"
// @Success 200 {object} model.loginResponse "正常に処理された場合"
// @Failure 400 {object} httperror.badRequestErrorResponse "リクエスト不正エラーが発生した場合"
// @Failure 422 {object} httperror.validationErrorResponse "バリデーションエラーが発生した場合"
// @Failure 500 {object} httperror.systemErrorResponse "システムエラーが発生した場合"
// @Router /login [post]
func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) {

	req, err := model.NewLoginRequest(r)
	if err != nil {
		httpStatus, res := httperror.ToHttpErrorResponse(err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(httpStatus)
		json.NewEncoder(w).Encode(res)
		return
	}

	out, err := h.userUsecase.Login(r.Context(), params.NewLoginInput(req.Name, req.Password))
	if err != nil {
		httpStatus, res := httperror.ToHttpErrorResponse(err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(httpStatus)
		json.NewEncoder(w).Encode(res)
		return
	}

	res := model.NewLoginResponse(out.Valid())
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(res)
}

// @Summary ユーザ情報取得
// @Description ユーザ情報を取得します。
// @Tags User
// @Accept json
// @Produce json
// @Param getUserRequest query model.getUserRequest false "ユーザ情報取得向けのリクエストモデル"
// @Success 200 {object} model.getUserResponse "正常に処理された場合"
// @Failure 422 {object} httperror.businessErrorResponse "業務エラーが発生した場合"
// @Failure 500 {object} httperror.systemErrorResponse "システムエラーが発生した場合"
// @Router /users [get]
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {

	req := model.NewGetUserRequest(r)

	out, err := h.userUsecase.GetUser(r.Context(), params.NewGetUserInput(req.Name))
	if err != nil {
		httpStatus, res := httperror.ToHttpErrorResponse(err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(httpStatus)
		json.NewEncoder(w).Encode(res)
		return
	}

	res := model.NewGetUserResponse(out.Name(), out.Age())
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(res)
}

model/user_model.go ​

各モデルクラスのexample記載します:

go
package model

import (
	"encoding/json"
	"net/http"
)

// 認証向けのリクエストモデルです。
type loginRequest struct {
	Name     string `json:"name" example:"nob"`        // ユーザ名
	Password string `json:"password" example:"passwd"` // パスワード
}

func NewLoginRequest(r *http.Request) (loginRequest, error) {

	var req loginRequest
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&req); err != nil {
		return loginRequest{}, err
	}
	return req, nil
}

// 認証向けのレスポンスモデルです。
type loginResponse struct {
	Valid bool `json:"valid" example:"true"` // 認証可否
}

func NewLoginResponse(valid bool) loginResponse {
	return loginResponse{Valid: valid}
}

// ユーザ情報取得向けのリクエストモデルです。
type getUserRequest struct {
	Name string `json:"name" example:"nob"` // ユーザ名
}

func NewGetUserRequest(r *http.Request) getUserRequest {
	return getUserRequest{Name: r.URL.Query().Get("name")}
}

// ユーザ情報取得向けのレスポンスモデルです。
type getUserResponse struct {
	Name string `json:"name" example:"nob"` // ユーザ名
	Age  int    `json:"age" example:"13"`   // 年齢
}

func NewGetUserResponse(name string, age int) getUserResponse {
	return getUserResponse{Name: name, Age: age}
}

httperror/httperror.go ​

例外発生時レスポンスモデルのexampleを記載します:

go
package httperror

// 業務エラーレスポンスモデルです。想定内のエラーが発生した場合に返るエラーです。
type businessErrorResponse struct {
	Message string `json:"message" example:"no such user"` // エラーメッセージ
}

動作確認 ​

下記コマンドでswaggerドキュメントを生成します:

shell
swag init -o ./api -g cmd/server/main.go

アプリを起動します:

shell
go run cmd/server/main.go

アプリ起動後、http://localhost:8080/swagger/index.html でswaggerドキュメントを確認できます。