Skip to main content

Echo Swagger Integration

Introduction

Swagger (now part of the OpenAPI Initiative) has become an industry standard for documenting REST APIs. When building APIs with Echo, a high-performance web framework for Go, integrating Swagger can significantly enhance your development workflow and improve the usability of your API.

In this tutorial, you'll learn how to integrate Swagger documentation with your Echo-based API. This integration provides an interactive UI where developers can explore and test your API endpoints directly from the browser, making it easier for others to understand and use your services.

What is Swagger?

Swagger is a set of tools for documenting and consuming RESTful web services. It includes:

  • Swagger UI: An interactive documentation interface
  • Swagger Editor: For designing and documenting APIs
  • Swagger Codegen: For generating client libraries
  • OpenAPI Specification: The underlying format for describing APIs

By integrating Swagger with Echo, you create self-documenting APIs that are easier to understand, test, and maintain.

Prerequisites

Before getting started, ensure you have:

  1. Go installed (version 1.16 or later recommended)
  2. Basic familiarity with Echo framework
  3. An existing Echo API project or willingness to create one

Installation of Required Packages

First, let's install the necessary packages:

bash
# Install Echo framework if you haven't already
go get -u github.com/labstack/echo/v4

# Install Echo Swagger and its dependencies
go get -u github.com/swaggo/echo-swagger
go get -u github.com/swaggo/swag/cmd/swag

Make sure to also install the Swag command-line tool globally:

bash
go install github.com/swaggo/swag/cmd/swag@latest

Step-by-Step Integration

Step 1: Structure Your API Project

Let's start with a basic Echo API structure:

my-echo-api/
├── main.go
├── handlers/
│ └── user_handler.go
├── models/
│ └── user.go
├── docs/ <-- Swagger will generate files here
└── routes/
└── routes.go

Step 2: Add Swagger Annotations to Your Code

Swagger uses special comments to generate documentation. Here's an example of adding these annotations to your main.go file:

go
// main.go
package main

import (
"net/http"

"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
echoSwagger "github.com/swaggo/echo-swagger"
_ "your-project/docs" // This is where Swagger will generate its docs
)

// @title Echo API
// @version 1.0
// @description This is a sample server for an Echo API.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.example.com/support
// @contact.email [email protected]

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /api/v1
func main() {
e := echo.New()

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())

// Routes
setupRoutes(e)

// Start server
e.Logger.Fatal(e.Start(":8080"))
}

func setupRoutes(e *echo.Echo) {
// Swagger endpoint
e.GET("/swagger/*", echoSwagger.WrapHandler)

// API routes will be defined here
}

Step 3: Annotate Your API Handlers

Now, let's add annotations to a handler function. Here's an example of a user handler:

go
// handlers/user_handler.go
package handlers

import (
"net/http"
"strconv"

"github.com/labstack/echo/v4"
"your-project/models"
)

// GetUser godoc
// @Summary Get a user by ID
// @Description Get user details for a given ID
// @Tags users
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} models.User
// @Failure 404 {object} models.ErrorResponse
// @Router /users/{id} [get]
func GetUser(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
Message: "Invalid user ID",
})
}

// In a real app, you would fetch from a database
user := models.User{
ID: id,
Username: "johndoe",
Email: "[email protected]",
FullName: "John Doe",
}

return c.JSON(http.StatusOK, user)
}

Step 4: Define Your Models

Models should also be documented with Swagger annotations:

go
// models/user.go
package models

// User represents user information
type User struct {
ID int `json:"id" example:"1"`
Username string `json:"username" example:"johndoe"`
Email string `json:"email" example:"[email protected]"`
FullName string `json:"full_name" example:"John Doe"`
}

// ErrorResponse represents an error
type ErrorResponse struct {
Message string `json:"message" example:"User not found"`
}

Step 5: Set Up Your Routes

Now, let's define our routes in a separate file:

go
// routes/routes.go
package routes

import (
"github.com/labstack/echo/v4"
"your-project/handlers"
)

// SetupRoutes configures all API routes
func SetupRoutes(e *echo.Echo) {
// API v1 group
v1 := e.Group("/api/v1")

// User routes
v1.GET("/users/:id", handlers.GetUser)
// Add more routes as needed
}

And modify main.go to use this function:

go
// In main.go
import "your-project/routes"

// Change setupRoutes(e) to:
routes.SetupRoutes(e)

Step 6: Generate Swagger Documentation

Now that you've added annotations, run the Swag command to generate the Swagger files:

bash
swag init

This will create the docs directory with the necessary Swagger files.

Step 7: Implement Additional Endpoints

Let's add more endpoints to make our API more complete:

go
// handlers/user_handler.go (additional methods)

// CreateUser godoc
// @Summary Create a new user
// @Description Add a new user to the system
// @Tags users
// @Accept json
// @Produce json
// @Param user body models.CreateUserRequest true "User information"
// @Success 201 {object} models.User
// @Failure 400 {object} models.ErrorResponse
// @Router /users [post]
func CreateUser(c echo.Context) error {
var req models.CreateUserRequest

if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
Message: "Invalid request body",
})
}

// In a real app, you would save to a database
user := models.User{
ID: 123, // Generated ID
Username: req.Username,
Email: req.Email,
FullName: req.FullName,
}

return c.JSON(http.StatusCreated, user)
}

// And add to models/user.go:
// CreateUserRequest represents the request to create a user
type CreateUserRequest struct {
Username string `json:"username" example:"johndoe" binding:"required"`
Email string `json:"email" example:"[email protected]" binding:"required,email"`
FullName string `json:"full_name" example:"John Doe"`
}

Don't forget to add the new route:

go
// In routes/routes.go:
v1.POST("/users", handlers.CreateUser)

Step 8: Start Your Server and Test the Swagger UI

After completing the setup, run your application:

bash
go run main.go

Then navigate to http://localhost:8080/swagger/index.html in your browser. You should see the Swagger UI showing your API documentation with interactive features.

Real-World Example: Building a Task Management API

Let's create a more practical example of a task management API with proper Swagger documentation:

First, define your task model:

go
// models/task.go
package models

import "time"

// Task represents a task in the system
type Task struct {
ID int `json:"id" example:"1"`
Title string `json:"title" example:"Complete project report"`
Description string `json:"description" example:"Write up the quarterly project status report"`
DueDate time.Time `json:"due_date" example:"2023-12-31T23:59:59Z"`
Status string `json:"status" example:"pending"`
UserID int `json:"user_id" example:"5"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

// CreateTaskRequest represents the request to create a task
type CreateTaskRequest struct {
Title string `json:"title" example:"Complete project report" binding:"required"`
Description string `json:"description" example:"Write up the quarterly project status report"`
DueDate time.Time `json:"due_date" example:"2023-12-31T23:59:59Z"`
UserID int `json:"user_id" example:"5" binding:"required"`
}

// UpdateTaskRequest represents the request to update a task
type UpdateTaskRequest struct {
Title string `json:"title" example:"Complete project report"`
Description string `json:"description" example:"Write up the quarterly project status report"`
DueDate time.Time `json:"due_date" example:"2023-12-31T23:59:59Z"`
Status string `json:"status" example:"completed"`
}

Next, implement the task handlers:

go
// handlers/task_handler.go
package handlers

import (
"net/http"
"strconv"
"time"

"github.com/labstack/echo/v4"
"your-project/models"
)

// GetTasksList godoc
// @Summary Get all tasks
// @Description Get a list of all tasks
// @Tags tasks
// @Accept json
// @Produce json
// @Param status query string false "Filter by status (pending, completed, all)"
// @Success 200 {array} models.Task
// @Router /tasks [get]
func GetTasksList(c echo.Context) error {
status := c.QueryParam("status")

// In a real app, you would filter from a database
tasks := []models.Task{
{
ID: 1,
Title: "Complete API documentation",
Description: "Finish Swagger integration for Echo API",
DueDate: time.Now().Add(24 * time.Hour),
Status: "pending",
UserID: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
{
ID: 2,
Title: "Review pull requests",
Description: "Review and merge team PR's",
DueDate: time.Now().Add(48 * time.Hour),
Status: "pending",
UserID: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
},
}

if status != "" && status != "all" {
var filteredTasks []models.Task
for _, task := range tasks {
if task.Status == status {
filteredTasks = append(filteredTasks, task)
}
}
tasks = filteredTasks
}

return c.JSON(http.StatusOK, tasks)
}

// GetTask godoc
// @Summary Get a task by ID
// @Description Get task details for a given ID
// @Tags tasks
// @Accept json
// @Produce json
// @Param id path int true "Task ID"
// @Success 200 {object} models.Task
// @Failure 404 {object} models.ErrorResponse
// @Router /tasks/{id} [get]
func GetTask(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
Message: "Invalid task ID",
})
}

// In a real app, you would fetch from a database
task := models.Task{
ID: id,
Title: "Complete API documentation",
Description: "Finish Swagger integration for Echo API",
DueDate: time.Now().Add(24 * time.Hour),
Status: "pending",
UserID: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

return c.JSON(http.StatusOK, task)
}

// CreateTask godoc
// @Summary Create a new task
// @Description Add a new task to the system
// @Tags tasks
// @Accept json
// @Produce json
// @Param task body models.CreateTaskRequest true "Task information"
// @Success 201 {object} models.Task
// @Failure 400 {object} models.ErrorResponse
// @Router /tasks [post]
func CreateTask(c echo.Context) error {
var req models.CreateTaskRequest

if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, models.ErrorResponse{
Message: "Invalid request body",
})
}

// In a real app, you would save to a database
now := time.Now()
task := models.Task{
ID: 123, // Generated ID
Title: req.Title,
Description: req.Description,
DueDate: req.DueDate,
Status: "pending",
UserID: req.UserID,
CreatedAt: now,
UpdatedAt: now,
}

return c.JSON(http.StatusCreated, task)
}

Update your routes:

go
// routes/routes.go
func SetupRoutes(e *echo.Echo) {
// API v1 group
v1 := e.Group("/api/v1")

// User routes
v1.GET("/users/:id", handlers.GetUser)
v1.POST("/users", handlers.CreateUser)

// Task routes
v1.GET("/tasks", handlers.GetTasksList)
v1.GET("/tasks/:id", handlers.GetTask)
v1.POST("/tasks", handlers.CreateTask)
}

After implementing these changes, run swag init again to update your Swagger documentation.

Common Challenges and Solutions

Swagger Documentation Not Showing Up

If your Swagger UI page is empty or not showing your endpoints:

  1. Make sure you've run swag init after adding annotations
  2. Check that you've imported the docs package: _ "your-project/docs"
  3. Verify your annotations follow the correct format

Handling Different Response Types

To document different response types:

go
// @Success 200 {array} models.Task "Successful response"
// @Success 201 {object} models.Task "Created successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 404 {object} models.ErrorResponse "Not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"

Supporting Authentication

To document APIs that require authentication:

go
// @Security ApiKeyAuth
// @param Authorization header string true "Bearer token"

And add to your main.go:

go
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization

Summary

In this tutorial, you learned how to integrate Swagger documentation with your Echo API:

  1. Installing the necessary packages (echo-swagger and swag)
  2. Adding Swagger annotations to your code
  3. Generating Swagger documentation using the swag init command
  4. Building a RESTful API with proper documentation
  5. Testing your API using the Swagger UI interface

With Swagger integration, your Echo API now has interactive documentation that makes it easier for developers to understand and use your API. This not only improves developer experience but also serves as living documentation that stays in sync with your code.

Additional Resources

Exercises

  1. Add Swagger documentation to an existing Echo API project
  2. Implement a DELETE endpoint for tasks with proper Swagger annotations
  3. Add query parameters for pagination to the task list endpoint
  4. Implement and document API authentication using JWT tokens
  5. Create a complete CRUD API for another resource (like comments or categories)

By completing these exercises, you'll become proficient at creating well-documented APIs with Echo and Swagger.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)