Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion adapter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ This directory contains framework-specific adapters for `oaswrap/spec` that prov
| Framework | Adapter | Go Module | Description |
|-----------|---------|-----------|-------------|
| [Chi](https://github.com/go-chi/chi) | [`chiopenapi`](./chiopenapi) | `github.com/oaswrap/spec/adapter/chiopenapi` | Lightweight router with middleware support |
| [Echo](https://github.com/labstack/echo) | [`echoopenapi`](./echoopenapi) | `github.com/oaswrap/spec/adapter/echoopenapi` | High performance, extensible, minimalist framework |
| [Echo v4](https://github.com/labstack/echo) | [`echoopenapi`](./echoopenapi) | `github.com/oaswrap/spec/adapter/echoopenapi` | High performance, extensible, minimalist framework |
| [Echo v5](https://github.com/labstack/echo) | [`echov5openapi`](./echov5openapi) | `github.com/oaswrap/spec/adapter/echov5openapi` | Echo v5 with updated Context API |
| [Fiber](https://github.com/gofiber/fiber) | [`fiberopenapi`](./fiberopenapi) | `github.com/oaswrap/spec/adapter/fiberopenapi` | Express-inspired framework built on Fasthttp |
| [Gin](https://github.com/gin-gonic/gin) | [`ginopenapi`](./ginopenapi) | `github.com/oaswrap/spec/adapter/ginopenapi` | Fast HTTP web framework with zero allocation |
| [net/http](https://pkg.go.dev/net/http) | [`httpopenapi`](./httpopenapi) | `github.com/oaswrap/spec/adapter/httpopenapi` | Standard library HTTP package |
Expand Down
244 changes: 244 additions & 0 deletions adapter/echov5openapi/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# echov5openapi

[![Go Reference](https://pkg.go.dev/badge/github.com/oaswrap/spec/adapter/echov5openapi.svg)](https://pkg.go.dev/github.com/oaswrap/spec/adapter/echov5openapi)
[![Go Report Card](https://goreportcard.com/badge/github.com/oaswrap/spec/adapter/echov5openapi)](https://goreportcard.com/report/github.com/oaswrap/spec/adapter/echov5openapi)

A lightweight adapter for the [Echo v5](https://github.com/labstack/echo) web framework that automatically generates OpenAPI 3.x specifications from your routes using [`oaswrap/spec`](https://github.com/oaswrap/spec).

> **Note:** This adapter is for Echo v5. For Echo v4, use [`echoopenapi`](../echoopenapi).

## Echo v5 Key Changes

Echo v5 includes several breaking changes from v4:

- **Handler signature changed**: `func(c echo.Context) error` -> `func(c *echo.Context) error` (Context is now a pointer)
- **Logger**: Now uses Go's standard `log/slog` instead of custom interface
- **Route returns**: Methods return `RouteInfo` instead of `*Route`
- **Static file methods**: Now accept middleware parameters

## Features

- **Seamless Integration** - Works with your existing Echo v5 routes and handlers
- **Automatic Documentation** - Generate OpenAPI specs from route definitions and struct tags
- **Type Safety** - Full Go type safety for OpenAPI configuration
- **Multiple UI Options** - Swagger UI, Stoplight Elements, ReDoc, Scalar or RapiDoc served automatically at `/docs`
- **YAML Export** - OpenAPI spec available at `/docs/openapi.yaml`
- **Zero Overhead** - Minimal performance impact on your API

## Installation

```bash
go get github.com/oaswrap/spec/adapter/echov5openapi
```

## Quick Start

```go
package main

import (
"log"

"github.com/labstack/echo/v5"
"github.com/oaswrap/spec/adapter/echov5openapi"
"github.com/oaswrap/spec/option"
)

func main() {
e := echo.New()

// Create a new OpenAPI router
r := echov5openapi.NewRouter(e,
option.WithTitle("My API"),
option.WithVersion("1.0.0"),
option.WithSecurity("bearerAuth", option.SecurityHTTPBearer("Bearer")),
)
// Add routes
v1 := r.Group("/api/v1")

v1.POST("/login", LoginHandler).With(
option.Summary("User login"),
option.Request(new(LoginRequest)),
option.Response(200, new(LoginResponse)),
)

auth := v1.Group("", AuthMiddleware).With(
option.GroupSecurity("bearerAuth"),
)
auth.GET("/users/:id", GetUserHandler).With(
option.Summary("Get user by ID"),
option.Request(new(GetUserRequest)),
option.Response(200, new(User)),
)

log.Printf("OpenAPI docs available at: %s", "http://localhost:3000/docs")

if err := e.Start(":3000"); err != nil {
log.Fatal(err)
}
}

type LoginRequest struct {
Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"`
}

type LoginResponse struct {
Token string `json:"token"`
}

type GetUserRequest struct {
ID string `param:"id" required:"true"`
}

type User struct {
ID string `json:"id"`
Name string `json:"name"`
}

// Note: Echo v5 uses *echo.Context instead of echo.Context
func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
// Simulate authentication logic
authHeader := c.Request().Header.Get("Authorization")
if authHeader != "" && authHeader == "Bearer example-token" {
return next(c)
}
return c.JSON(401, map[string]string{"error": "Unauthorized"})
}
}

func LoginHandler(c *echo.Context) error {
var req LoginRequest
if err := c.Bind(&req); err != nil {
return c.JSON(400, map[string]string{"error": "Invalid request"})
}
// Simulate login logic
return c.JSON(200, LoginResponse{Token: "example-token"})
}

func GetUserHandler(c *echo.Context) error {
var req GetUserRequest
if err := c.Bind(&req); err != nil {
return c.JSON(400, map[string]string{"error": "Invalid request"})
}
// Simulate fetching user by ID
user := User{ID: req.ID, Name: "John Doe"}
return c.JSON(200, user)
}
```

## Migrating from Echo v4

If you're migrating from the v4 adapter (`echoopenapi`), here are the main changes:

1. **Update import path**:
```go
// Before (v4)
import "github.com/oaswrap/spec/adapter/echoopenapi"

// After (v5)
import "github.com/oaswrap/spec/adapter/echov5openapi"
```

2. **Update handler signatures**:
```go
// Before (v4)
func MyHandler(c echo.Context) error { ... }

// After (v5)
func MyHandler(c *echo.Context) error { ... }
```

3. **Update middleware signatures**:
```go
// Before (v4)
func MyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { ... }
}

// After (v5)
func MyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error { ... }
}
```

## Documentation Features

### Built-in Endpoints
When you create an echov5openapi router, the following endpoints are automatically available:

- **`/docs`** - Interactive UI documentation
- **`/docs/openapi.yaml`** - Raw OpenAPI specification in YAML format

If you want to disable the built-in UI, you can do so by passing `option.WithDisableDocs()` when creating the router:

```go
r := echov5openapi.NewRouter(c,
option.WithTitle("My API"),
option.WithVersion("1.0.0"),
option.WithDisableDocs(),
)
```

### Supported Documentation UIs
Choose from multiple UI options, powered by [`oaswrap/spec-ui`](https://github.com/oaswrap/spec-ui):

- **Stoplight Elements** - Modern, clean design (default)
- **Swagger UI** - Classic interface with try-it functionality
- **ReDoc** - Three-panel responsive layout
- **Scalar** - Beautiful and fast interface
- **RapiDoc** - Highly customizable

```go
r := echov5openapi.NewRouter(c,
option.WithTitle("My API"),
option.WithVersion("1.0.0"),
option.WithScalar(), // Use Scalar as the documentation UI
)
```

### Rich Schema Documentation
Use struct tags to generate detailed OpenAPI schemas. **Note: These tags are used only for OpenAPI spec generation and documentation - they do not perform actual request validation.**

```go
type CreateProductRequest struct {
Name string `json:"name" required:"true" minLength:"1" maxLength:"100"`
Description string `json:"description" maxLength:"500"`
Price float64 `json:"price" required:"true" minimum:"0" maximum:"999999.99"`
Category string `json:"category" required:"true" enum:"electronics,books,clothing"`
Tags []string `json:"tags" maxItems:"10"`
InStock bool `json:"in_stock" default:"true"`
}
```

For more struct tag options, see the [swaggest/openapi-go](https://github.com/swaggest/openapi-go?tab=readme-ov-file#features).

## Examples

Check out complete examples in the main repository:
- [Basic](https://github.com/oaswrap/spec/tree/main/examples/adapter/echov5openapi/basic)

## Best Practices

1. **Organize with Tags** - Group related operations using `option.Tags()`
2. **Document Everything** - Use `option.Summary()` and `option.Description()` for all routes
3. **Define Error Responses** - Include common error responses (400, 401, 404, 500)
4. **Use Validation Tags** - Leverage struct tags for request validation documentation
5. **Security First** - Define and apply appropriate security schemes
6. **Version Your API** - Use route groups for API versioning (`/api/v1`, `/api/v2`)

## API Reference

- **Spec**: [pkg.go.dev/github.com/oaswrap/spec](https://pkg.go.dev/github.com/oaswrap/spec)
- **Echo v5 Adapter**: [pkg.go.dev/github.com/oaswrap/spec/adapter/echov5openapi](https://pkg.go.dev/github.com/oaswrap/spec/adapter/echov5openapi)
- **Options**: [pkg.go.dev/github.com/oaswrap/spec/option](https://pkg.go.dev/github.com/oaswrap/spec/option)
- **Spec UI**: [pkg.go.dev/github.com/oaswrap/spec-ui](https://pkg.go.dev/github.com/oaswrap/spec-ui)

## Contributing

We welcome contributions! Please open issues and PRs at the main [oaswrap/spec](https://github.com/oaswrap/spec) repository.

## License

[MIT](../../LICENSE)
20 changes: 20 additions & 0 deletions adapter/echov5openapi/examples/basic/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module github.com/oaswrap/spec/adapter/echov5openapi/examples/basic

go 1.25.0

require (
github.com/labstack/echo/v5 v5.0.2
github.com/oaswrap/spec v0.3.6
github.com/oaswrap/spec/adapter/echov5openapi v0.0.0
)

require (
github.com/kr/text v0.2.0 // indirect
github.com/oaswrap/spec-ui v0.1.4 // indirect
github.com/swaggest/jsonschema-go v0.3.78 // indirect
github.com/swaggest/openapi-go v0.2.60 // indirect
github.com/swaggest/refl v1.4.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace github.com/oaswrap/spec/adapter/echov5openapi => ../..
50 changes: 50 additions & 0 deletions adapter/echov5openapi/examples/basic/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
github.com/bool64/dev v0.2.39 h1:kP8DnMGlWXhGYJEZE/J0l/gVBdbuhoPGL+MJG4QbofE=
github.com/bool64/dev v0.2.39/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v5 v5.0.2 h1:DwPe1Rla27Zf3QxbW+DxhPKRIbKHHTgHQyaLJC2gE3s=
github.com/labstack/echo/v5 v5.0.2/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
github.com/oaswrap/spec v0.3.6 h1:igKJvrrEYP/pK5I4TzEzYVcdbbr8eJ1gfALUXgZ/Oc8=
github.com/oaswrap/spec v0.3.6/go.mod h1:e6cGQJcVCkQozwsw8T0ydSWEgQPA/dHFmQME4KawOYU=
github.com/oaswrap/spec-ui v0.1.4 h1:XM2Z/ZS2Su90EtDSVuOHGr2+DLpVc2933mxkn6F4aeU=
github.com/oaswrap/spec-ui v0.1.4/go.mod h1:D8EnD6zbYJ3q65wdltw6QHXfw+nut5XwSSA1xtlSEQQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
github.com/swaggest/jsonschema-go v0.3.78 h1:5+YFQrLxOR8z6CHvgtZc42WRy/Q9zRQQ4HoAxlinlHw=
github.com/swaggest/jsonschema-go v0.3.78/go.mod h1:4nniXBuE+FIGkOGuidjOINMH7OEqZK3HCSbfDuLRI0g=
github.com/swaggest/openapi-go v0.2.60 h1:kglHH/WIfqAglfuWL4tu0LPakqNYySzklUWx06SjSKo=
github.com/swaggest/openapi-go v0.2.60/go.mod h1:jmFOuYdsWGtHU0BOuILlHZQJxLqHiAE6en+baE+QQUk=
github.com/swaggest/refl v1.4.0 h1:CftOSdTqRqs100xpFOT/Rifss5xBV/CT0S/FN60Xe9k=
github.com/swaggest/refl v1.4.0/go.mod h1:4uUVFVfPJ0NSX9FPwMPspeHos9wPFlCMGoPRllUbpvA=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading