-
Notifications
You must be signed in to change notification settings - Fork 3
Open
Description
Hi 👋
First of all — thank you for this awesome package! It's clean, intuitive, and makes OpenAPI integration much easier in Go web projects. 🙌
Problem
When defining routes using .With(...), we need to manually specify the request and response types, even though our handler already "knows" them.
Example:
v1.Get("/users/{id}", GetUserHandler).With(
option.Summary("Get user by ID"),
option.Request(new(GetUserRequest)),
option.Response(200, new(User)),
)This leads to a bit of repetition and can become error-prone in larger projects.
Proposal
Introduce a generic helper like Register[Req, Res] to simplify handler registration and OpenAPI binding.
Example usage:
Register[GetUserRequest, User](v1, "GET", "/users/{id}", GetUserHandler,
option.Summary("Get user by ID"),
)Generic handler for fiber example:
func GetUserHandler(c *fiber.Ctx, req *GetUserRequest) (*User, error) {
return &User{ID: req.ID, Name: "John"}, nil
}The helper would:
- Register the route using
fiber.Router.Add(...) - Auto-bind request and response types to the OpenAPI spec:
route.With( option.Request(new(Req)), option.Response(200, new(Res)), )
- Accept extra
option.Optionvalues for further customization
ExampleRegister[Req, Res] implementation for fiber:
package fiberopenapi
import (
"github.com/gofiber/fiber/v2"
"github.com/oaswrap/spec"
"github.com/oaswrap/spec/option"
)
// Generic handler function signature
type Handler[Req any, Res any] func(c *fiber.Ctx, input *Req) (*Res, error)
// Register binds a typed handler to a route, and registers request/response types with spec
func Register[Req any, Res any](
r *spec.Router,
method string,
path string,
handler Handler[Req, Res],
opts ...option.Option,
) {
// Wrap the generic handler into a standard Fiber handler
fiberHandler := func(c *fiber.Ctx) error {
var req Req
// Parse path parameters
if err := c.ParamsParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "invalid path parameters"})
}
// Parse request body if any
if err := c.BodyParser(&req); err != nil {
// Optional: log or ignore body parse errors when not needed
}
res, err := handler(c, &req)
if err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(res)
}
// Register with both Fiber and spec
route := r.Add(path, method, fiberHandler)
// Automatically bind request/response types to the spec
route.With(
option.Request(new(Req)),
option.Response(200, new(Res)),
)
// Apply additional user-provided options
for _, opt := range opts {
route.With(opt)
}
}Benefits
- DRY: No need to repeat types already defined in your handler
- Cleaner handlers: Just define your logic and let registration handle the rest
- Type-safe: Compile-time binding of input/output types
- Flexible: Still allows
.With(...)options as needed - Reusable: The generic handler(ie
func GetUserHandler(c *fiber.Ctx, req *GetUserRequest) (*User, error)) is reusable from other handlers!
Tradeoffs
- Requires adding a thin abstraction around route registration
- For each supported web framework, similar
Register[Req, Res]method is needed
I'd love to hear your thoughts — and I’d be happy to contribute a PR if this is something you'd like to support in oaswrap/spec.
Thanks again for your work!
Metadata
Metadata
Assignees
Labels
No labels