Skip to content

Swagger Documentation

Generating API documentation.

Overview

Spine uses Swaggo to automatically generate Swagger documentation.

  • Extract API specs from code comments
  • Provide documentation via Swagger UI
  • API testing capabilities

Installation

bash
# Install Swag CLI
go install github.com/swaggo/swag/cmd/swag@latest

# Install required packages
go get github.com/swaggo/swag
go get github.com/swaggo/http-swagger

Project Configuration

Adding Comments to main.go

go
// main.go
package main

import (
    "myapp/controller"
    "myapp/routes"
    "myapp/service"

    "github.com/NARUBROWN/spine"
    "github.com/labstack/echo/v4"
    httpSwagger "github.com/swaggo/http-swagger"

    _ "myapp/docs"  // Import generated docs package
)

// @title My App API
// @version 1.0.0
// @description REST API based on Spine

// @host localhost:8080
// @BasePath /
func main() {
    app := spine.New()

    app.Constructor(
        service.NewUserService,
        controller.NewUserController,
    )

    routes.RegisterUserRoutes(app)

    // Register Swagger UI
    app.Transport(func(t any) {
        e := t.(*echo.Echo)
        e.GET("/swagger/*", echo.WrapHandler(httpSwagger.WrapHandler))
    })

    app.Run(":8080")
}

main.go Annotation Tags

TagDescriptionExample
@titleAPI TitleMy App API
@versionAPI Version1.0.0
@descriptionAPI DescriptionREST API based on Spine
@hostHost Addresslocalhost:8080
@BasePathBase Path/

Controller Documentation

Basic Format

go
// controller/user_controller.go
package controller

import (
    "context"

    "myapp/dto"
    "myapp/service"

    "github.com/NARUBROWN/spine/pkg/httperr"
    "github.com/NARUBROWN/spine/pkg/query"
)

type UserController struct {
    svc *service.UserService
}

func NewUserController(svc *service.UserService) *UserController {
    return &UserController{svc: svc}
}

// GetUser godoc
// @Summary Get User
// @Description key words: Get user info by ID
// @Tags users
// @Param id query int true "User ID"
// @Success 200 {object} dto.UserResponse
// @Failure 404 {object} ErrorResponse
// @Router /users [get]
func (c *UserController) GetUser(
    ctx context.Context,
    q query.Values,
) (dto.UserResponse, error) {
    id := int(q.Int("id", 0))

    user, err := c.svc.Get(ctx, id)
    if err != nil {
        return dto.UserResponse{}, httperr.NotFound("User not found.")
    }

    return user, nil
}

CRUD Complete Example

go
// GetUser godoc
// @Summary Get User
// @Description Get user info by ID
// @Tags users
// @Param id query int true "User ID"
// @Success 200 {object} dto.UserResponse
// @Failure 404 {object} ErrorResponse
// @Router /users [get]
func (c *UserController) GetUser(
    ctx context.Context,
    q query.Values,
) (dto.UserResponse, error) {
    // ...
}

// CreateUser godoc
// @Summary Create User
// @Description Create a new user
// @Tags users
// @Accept json
// @Produce json
// @Param body body dto.CreateUserRequest true "User creation request"
// @Success 200 {object} dto.UserResponse
// @Failure 400 {object} ErrorResponse
// @Router /users [post]
func (c *UserController) CreateUser(
    ctx context.Context,
    req dto.CreateUserRequest,
) (dto.UserResponse, error) {
    // ...
}

// UpdateUser godoc
// @Summary Update User
// @Description Update user info
// @Tags users
// @Accept json
// @Produce json
// @Param id query int true "User ID"
// @Param body body dto.UpdateUserRequest true "User update request"
// @Success 200 {object} dto.UserResponse
// @Failure 404 {object} ErrorResponse
// @Router /users [put]
func (c *UserController) UpdateUser(
    ctx context.Context,
    q query.Values,
    req dto.UpdateUserRequest,
) (dto.UserResponse, error) {
    // ...
}

// DeleteUser godoc
// @Summary Delete User
// @Description Delete a user
// @Tags users
// @Param id query int true "User ID"
// @Success 200
// @Failure 404 {object} ErrorResponse
// @Router /users [delete]
func (c *UserController) DeleteUser(
    ctx context.Context,
    q query.Values,
) error {
    // ...
}

Annotation Tag Reference

Basic Tags

TagDescriptionExample
@SummarySummary (One line)Get User
@DescriptionDetailed DescriptionGet user info by ID
@TagsGroup Tagusers
@RouterPath and Method/users [get]

Request Tags

TagDescriptionExample
@AcceptRequest Content-Typejson
@ProduceResponse Content-Typejson
@ParamParameter Definitionid query int true "User ID"

Response Tags

TagDescriptionExample
@SuccessSuccess Response200 {object} dto.UserResponse
@FailureFailure Response404 {object} ErrorResponse

@Param Format

@Param [Name] [Location] [Type] [Required] "[Description]"

Location (in)

LocationDescriptionExample
queryQuery String/users?id=1
pathURL Path/users/{id}
bodyRequest BodyJSON body
headerHeaderAuthorization
formDataForm DataFile Upload

Type

TypeDescription
int, integerInteger
stringString
bool, booleanBoolean
numberFloat/Double
objectObject (DTO)
arrayArray

Examples

go
// Query Parameter
// @Param id query int true "User ID"
// @Param name query string false "User name"
// @Param active query bool false "Active status"

// Request Body
// @Param body body dto.CreateUserRequest true "User creation request"

// Header
// @Param Authorization header string true "Bearer Token"

DTO Documentation

Request DTO

go
// dto/user_request.go
package dto

// CreateUserRequest User creation request
type CreateUserRequest struct {
    Name  string `json:"name" example:"Alice"`
    Email string `json:"email" example:"alice@example.com"`
}

// UpdateUserRequest User update request
type UpdateUserRequest struct {
    Name  string `json:"name" example:"Alice Updated"`
    Email string `json:"email" example:"alice.new@example.com"`
}

Response DTO

go
// dto/user_response.go
package dto

// UserResponse User response
type UserResponse struct {
    ID    int    `json:"id" example:"1"`
    Name  string `json:"name" example:"Alice"`
    Email string `json:"email" example:"alice@example.com"`
}

// ErrorResponse Error response
type ErrorResponse struct {
    Error string `json:"error" example:"User not found."`
}

DTO Tags

TagDescriptionExample
exampleExample Valueexample:"Alice"
enumsAllowed Valuesenums:"active,inactive"
minimumMinimum Valueminimum:"1"
maximumMaximum Valuemaximum:"100"
defaultDefault Valuedefault:"10"

Generating Documentation

Running Command

bash
// Run at project root
swag init

// Or specify main.go path
swag init -g main.go

Generated Result

myapp/
├── docs/
├── docs.go       # Go Code
├── swagger.json  # JSON Spec
└── swagger.yaml  # YAML Spec
├── main.go
└── ...

Generated docs/docs.go

go
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs

import "github.com/swaggo/swag"

const docTemplate = `{
    "swagger": "2.0",
    "info": {
        "title": "My App API",
        "version": "1.0.0"
    },
    ...
}`

var SwaggerInfo = &swag.Spec{
    Version:     "1.0.0",
    Title:       "My App API",
    Description: "REST API based on Spine",
    // ...
}

func init() {
    swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

Accessing Swagger UI

Running Server

bash
go run main.go

Browsing

http://localhost:8080/swagger/index.html

Auto Regeneration

To auto-regenerate docs on code change:

Using Makefile

makefile
# Makefile

.PHONY: swagger run

swagger:
	swag init -g main.go

run: swagger
	go run main.go
bash
make run

Using Script

bash
#!/bin/bash
# run.sh

swag init -g main.go
go run main.go
bash
chmod +x run.sh
./run.sh

Complete Example

Project Structure

myapp/
├── main.go
├── docs/
│   ├── docs.go
│   ├── swagger.json
│   └── swagger.yaml
├── controller/
│   └── user_controller.go
├── dto/
│   ├── user_request.go
│   └── user_response.go
├── service/
│   └── user_service.go
└── routes/
    └── routes.go

main.go

go
package main

import (
    "myapp/controller"
    "myapp/routes"
    "myapp/service"

    "github.com/NARUBROWN/spine"
    "github.com/labstack/echo/v4"
    httpSwagger "github.com/swaggo/http-swagger"

    _ "myapp/docs"
)

// @title My App API
// @version 1.0.0
// @description REST API based on Spine

// @host localhost:8080
// @BasePath /
func main() {
    app := spine.New()

    app.Constructor(
        service.NewUserService,
        controller.NewUserController,
    )

    routes.RegisterUserRoutes(app)

    // Register Swagger UI
    app.Transport(func(t any) {
        e := t.(*echo.Echo)
        e.GET("/swagger/*", echo.WrapHandler(httpSwagger.WrapHandler))
    })

    app.Run(":8080")
}

controller/user_controller.go

go
package controller

import (
    "context"

    "myapp/dto"
    "myapp/service"

    "github.com/NARUBROWN/spine/pkg/httperr"
    "github.com/NARUBROWN/spine/pkg/query"
)

type UserController struct {
    svc *service.UserService
}

func NewUserController(svc *service.UserService) *UserController {
    return &UserController{svc: svc}
}

// GetUser godoc
// @Summary Get User
// @Description Get user info by ID
// @Tags users
// @Param id query int true "User ID"
// @Success 200 {object} dto.UserResponse
// @Failure 404 {object} dto.ErrorResponse
// @Router /users [get]
func (c *UserController) GetUser(
    ctx context.Context,
    q query.Values,
) (dto.UserResponse, error) {
    id := int(q.Int("id", 0))

    user, err := c.svc.Get(ctx, id)
    if err != nil {
        return dto.UserResponse{}, httperr.NotFound("User not found.")
    }

    return user, nil
}

// CreateUser godoc
// @Summary Create User
// @Description Create a new user
// @Tags users
// @Accept json
// @Produce json
// @Param body body dto.CreateUserRequest true "User creation request"
// @Success 200 {object} dto.UserResponse
// @Failure 400 {object} dto.ErrorResponse
// @Router /users [post]
func (c *UserController) CreateUser(
    ctx context.Context,
    req dto.CreateUserRequest,
) (dto.UserResponse, error) {
    return c.svc.Create(ctx, req.Name, req.Email)
}

Key Takeaways

StepCommand/Action
1. Installgo install github.com/swaggo/swag/cmd/swag@latest
2. Add Comments// @Summary, // @Param, // @Router etc.
3. Generate Docsswag init
4. Register UIe.GET("/swagger/*", ...)
5. Accesshttp://localhost:8080/swagger/index.html

Next Steps