S SmartDocs
シリーズ: Go go 101 行 · 更新日 2026-04-18

router.go

Go/Baasid/internal/router/router.go

// Package router registers all HTTP routes and middleware on a fresh Echo instance.
//
// Route layout mirrors the requirement document verbatim:
//
//   POST   /auth/login      — public
//   POST   /goods/add      — JWT required
//   GET    /goods          — JWT required
//   GET    /goods/:id      — JWT required
//   PUT    /goods/:id      — JWT required
//   DELETE /goods/:id      — JWT required
//
// Swagger UI is mounted at /swagger/* for interactive exploration.
package router

import (
	"net/http"

	"baasid/internal/auth"
	"baasid/internal/config"
	"baasid/internal/handlers"
	"baasid/internal/middleware"
	"baasid/internal/repository"

	"github.com/labstack/echo/v4"
	echomw "github.com/labstack/echo/v4/middleware"
	echoSwagger "github.com/swaggo/echo-swagger"

	"gorm.io/gorm"
)

// NewEcho constructs the Echo engine with all routes wired.
func NewEcho(cfg *config.Config, db *gorm.DB) *echo.Echo {
	e := echo.New()
	e.HideBanner = true
	e.HTTPErrorHandler = customHTTPErrorHandler

	// Recover from panics (e.g. programming mistakes) and log stack traces.
	e.Use(echomw.Recover())

	// Request logger — invaluable when debugging JWT or DB issues locally.
	e.Use(echomw.LoggerWithConfig(echomw.LoggerConfig{
		Format: "${time_rfc3339} ${status} ${method} ${uri} latency=${latency_human}\n",
	}))

	// CORS: permissive defaults for local dev + Swagger UI in the browser.
	e.Use(echomw.CORSWithConfig(echomw.CORSConfig{
		AllowOrigins: []string{"*"},
		AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodOptions},
		AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization},
	}))

	tokens := auth.NewTokenService(cfg.JWTSecret, "baasid", cfg.JWTExpiry)
	users := repository.NewUserRepository(db)
	goods := repository.NewGoodsRepository(db)

	authH := handlers.NewAuthHandler(users, tokens)
	goodsH := handlers.NewGoodsHandler(goods)

	// Simple liveness probe for Docker / k8s health checks (not in the spec).
	e.GET("/health", func(c echo.Context) error {
		return c.JSON(http.StatusOK, echo.Map{"status": "ok"})
	})

	// Public authentication endpoint — no JWT required.
	e.POST("/auth/login", authH.Login)

	// Protected API group: every sub-route runs JWTAuth first.
	protected := e.Group("", middleware.JWTAuth(tokens))
	protected.POST("/goods/add", goodsH.Add)
	protected.GET("/goods", goodsH.List)
	protected.GET("/goods/:id", goodsH.GetByID)
	protected.PUT("/goods/:id", goodsH.Update)
	protected.DELETE("/goods/:id", goodsH.Delete)

	// OpenAPI / Swagger UI (generated by swag into docs/ package).
	e.GET("/swagger/*", echoSwagger.WrapHandler)

	return e
}

// customHTTPErrorHandler renders echo.HTTPError as JSON when the client
// prefers JSON, which keeps Postman and Swagger responses consistent.
func customHTTPErrorHandler(err error, c echo.Context) {
	if c.Response().Committed {
		return
	}
	he, ok := err.(*echo.HTTPError)
	if !ok {
		he = &echo.HTTPError{Code: http.StatusInternalServerError, Message: http.StatusText(http.StatusInternalServerError)}
	}
	if he.Message == nil {
		he.Message = http.StatusText(he.Code)
	}
	// Echo stores Message as interface{} — unwrap strings vs typed DTOs.
	switch m := he.Message.(type) {
	case string:
		_ = c.JSON(he.Code, echo.Map{"message": m})
	default:
		_ = c.JSON(he.Code, m)
	}
}

関連記事