📝 [v3 Proposal]: Koa Style ctx.Request
and ctx.Response
Objects #2854
Description
Feature Proposal Description
Currently, the Ctx object handles almost all of the work that is performed in a Handler function. This can potentially lead to confusion about what parts of the API interact with the request and what parts interact with the response (eg Cookie
vs Cookies
).
Previously, we decided against eliminating the Ctx object entirely (#2764), but at the tail end of the discussion in Discord, it was brought up that we could avoid almost all breaking changes by following what Koa does: put the core implementation in Request and Response objects and have a generic Ctx API which provides convenient aliases for the request+response APIs.
In other words, the existing Ctx object stays exactly as-is, but ctx.Response() and ctx.Request() are reworked to hold more of the core functionality. As alluded to above, this allows users to more easily reason about what parts of the API touch the request vs response.
I am creating this proposal to attempt to take that idea and arrive at a concrete conclusion for whether we would like to implement this or not for v3 and beyond.
More reading: https://koajs.com/
Alignment with Express API
While inspired by Koa, the approach of splitting Ctx into distinct Request and Response objects brings Fiber closer to Express's res and req centered APIs.
HTTP RFC Standards Compliance
The proposal is not relevant to HTTP RFCs.
API Stability
This would be a breaking change, as it would change Ctx.Request()
and Ctx.Response()
to no longer directly return the underlying fasthttp Request and Response objects. Consequently, it will need to be implemented during a major version bump.
The only way around this would be to make the new Request and Response objects compose the fasthttp objects, but that might prove to be a clumsy solution.
Having a clear separation between Request and Response APIs allows for a more idiomatic design. With the current Ctx-only design, we already have examples of collisions between request-based and response-based actions. For example, Ctx.Get
and Ctx.GetResponseHeader
.
Feature Examples
package main
import (
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
// This syntax is still valid under the proposed changes
app.Get("/old-stuff", func(c fiber.Ctx) error {
if c.Accepts("text/plain") == "" {
return c.SendStatus(fiber.StatusNotAcceptable)
}
name := c.Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Cookie(cookie)
// ...
return c.SendString("Hello " + name)
})
// Ergonomic mix of Ctx and direct req/res usage
app.Get("/new-stuff", func(c fiber.Ctx) error {
if c.Accepts("text/plain") == "" {
return c.SendStatus(fiber.StatusNotAcceptable)
}
name := c.Req().Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Res().Cookie(cookie)
// ...
return c.SendString("Hello " + name)
})
// Directly using req/res for everything
app.Get("/new-stuff2", func(c fiber.Ctx) error {
if c.Req().Accepts("text/plain") == "" {
return c.Res().SendStatus(fiber.StatusNotAcceptable)
}
name := c.Req().Cookies("name")
cookie := new(fiber.Cookie)
cookie.Name = "userId"
cookie.Value = "1234"
c.Res().Cookie(cookie)
// ...
return c.Res().SendString("Hello " + name)
})
}
Checklist:
- I agree to follow Fiber's Code of Conduct.
- I have searched for existing issues that describe my proposal before opening this one.
- I understand that a proposal that does not meet these guidelines may be closed without explanation.