Skip to content

Coming from Fiber

Fiber users will feel right at home — Kruda has the same Express-like API, but with Wing transport (epoll+eventfd) that's 26% faster.

Quick Comparison

ConceptFiberKruda
Create appfiber.New()kruda.New()
Context*fiber.Ctx*kruda.Ctx
Routeapp.Get("/path", handler)app.Get("/path", handler)
JSON responsec.JSON(obj)c.JSON(obj)
Status codec.Status(404).JSON(obj)c.Status(404).JSON(obj)
Query paramc.Query("key")c.Query("key")
Path paramc.Params("id")c.Param("id")
Body parserc.BodyParser(&obj)Typed handler C[T]
Middlewareapp.Use(middleware)app.Use(middleware)
Groupapp.Group("/api")app.Group("/api")
Listenapp.Listen(":3000")app.Listen(":3000")

Hello World

Fiber:

go
package main

import "github.com/gofiber/fiber/v2"

func main() {
    app := fiber.New()
    app.Get("/", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{"message": "hello"})
    })
    app.Listen(":3000")
}

Kruda:

go
package main

import "github.com/go-kruda/kruda"

func main() {
    app := kruda.New()
    app.Get("/", func(c *kruda.Ctx) error {
        return c.JSON(kruda.Map{"message": "hello"})
    })
    app.Listen(":3000")
}

Almost identical! The main difference is the import path.

Key Differences

1. No Context Reuse Bugs

Fiber's *fiber.Ctx is pooled and reused — storing it across goroutines causes data races:

go
// Fiber — DANGEROUS
app.Get("/", func(c *fiber.Ctx) error {
    go func() {
        // c is reused by another request — data race!
        fmt.Println(c.Query("name"))
    }()
    return nil
})

Kruda copies all strings on access — safe by default:

go
// Kruda — SAFE
app.Get("/", func(c *kruda.Ctx) error {
    name := c.Query("name") // string is copied, safe to use anywhere
    go func() {
        fmt.Println(name) // no data race
    }()
    return nil
})

2. Typed Handlers Replace BodyParser

Fiber:

go
type CreateUser struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

app.Post("/users", func(c *fiber.Ctx) error {
    var input CreateUser
    if err := c.BodyParser(&input); err != nil {
        return c.Status(422).JSON(fiber.Map{"error": err.Error()})
    }
    // manually validate...
    return c.JSON(createUser(input))
})

Kruda:

go
type CreateUser struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
}

app.Post("/users", func(c *kruda.Ctx) error {
    var input CreateUser
    if err := c.Bind(&input); err != nil {
        return err
    }
    return c.JSON(createUser(input))
})
// Or use typed handlers for compile-time safety:
// kruda.Post[CreateUser, User](app, "/users", handler)

3. Pluggable Transport

Fiber is locked to fasthttp (no HTTP/2, no TLS 1.3). Kruda lets you switch:

go
// Default on Linux: Wing transport (epoll+eventfd, fastest)
// Default on macOS: fasthttp
app := kruda.New()

// Explicit fasthttp:
app := kruda.New(kruda.FastHTTP())

// Need HTTP/2 or TLS? Switch to net/http:
app := kruda.New(kruda.NetHTTP())

// Auto-select: TLS config → automatically uses net/http
app := kruda.New(kruda.WithTLS("cert.pem", "key.pem"))

4. Route Groups with Method Chaining

Fiber:

go
api := app.Group("/api/v1")
api.Use(jwtMiddleware)
api.Get("/users", listUsers)
api.Post("/users", createUser)

Kruda:

go
app.Group("/api/v1").
    Guard(jwtMiddleware).
    Get("/users", listUsers).
    Post("/users", createUser).
    Done()

Middleware Migration

FiberKruda
fiber.Logger() (built-in)middleware.Logger() (built-in)
fiber.Recover()middleware.Recovery() (built-in)
fiber.CORS() (built-in)middleware.CORS() (built-in)
fiber.RequestID()middleware.RequestID() (built-in)
fiber.Timeout()middleware.Timeout() (built-in)
jwtware.New() (contrib)jwt.New() (contrib)
websocket.New() (contrib)ws.New() (contrib)
limiter.New() (contrib)ratelimit.New() (contrib)
compress.New() (contrib)compress.New() (contrib)

What You Gain

  • Wing transport — raw epoll+eventfd on Linux, 26% faster than Fiber (846K vs 670K req/s plaintext)
  • Type-safe handlers — no more BodyParser + manual validation
  • No context reuse bugs — safe string copies by default
  • HTTP/2 support — switch to net/http when needed
  • Auto OpenAPI — generated from typed handlers
  • Built-in DI — optional dependency injection
  • Auto CRUDkruda.Resource[Product, string](app, "/products", service) generates full REST API

Released under the MIT License.