Skip to content

KrudaFast by default, type-safe by design

High-performance Go web framework with typed handlers, auto CRUD, and custom async I/O transportNamed after ครุฑ, the Garuda of Thai mythology.

go get github.com/go-kruda/kruda
Kruda Garuda mascot carrying Go gopher

Declare the input. Kruda does the rest.

Path, query and JSON body bind into one struct. Validation tags run before your handler does, and the return type becomes the response and the OpenAPI schema.

Each tab is a complete program you can paste into main.go and run.

Read the typed handler guide
go
package main

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

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

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

func main() {
	app := kruda.New()

	kruda.Post[CreateUser, User](app, "/users",
		func(c *kruda.C[CreateUser]) (*User, error) {
			u := c.In
			return &User{ID: "1", Name: u.Name, Email: u.Email}, nil
		})

	app.Listen(":3000")
}
go
package main

import (
	"context"

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

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

// Users implements kruda.ResourceService[User, string].
type Users struct{ db map[string]User }

func (s *Users) List(ctx context.Context, page, limit int) ([]User, int, error) {
	out := make([]User, 0, len(s.db))
	for _, u := range s.db {
		out = append(out, u)
	}
	return out, len(out), nil
}

func (s *Users) Get(ctx context.Context, id string) (User, error) {
	return s.db[id], nil
}

func (s *Users) Create(ctx context.Context, u User) (User, error) {
	s.db[u.ID] = u
	return u, nil
}

func (s *Users) Update(ctx context.Context, id string, u User) (User, error) {
	s.db[id] = u
	return u, nil
}

func (s *Users) Delete(ctx context.Context, id string) error {
	delete(s.db, id)
	return nil
}

func main() {
	app := kruda.New()

	// GET /users, POST /users, GET/PUT/DELETE /users/:id
	kruda.Resource[User, string](app, "/users", &Users{db: map[string]User{}})

	app.Listen(":3000")
}
go
package main

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

func main() {
	app := kruda.New()

	app.Get("/", func(c *kruda.Ctx) error {
		return c.Text("Hello, Kruda!")
	}, kruda.Plaintext)

	app.Get("/users/:id", func(c *kruda.Ctx) error {
		return c.JSON(kruda.Map{"id": c.Param("id")})
	}, kruda.JSON)

	app.Listen(":3000")
}

2.06x Fiber throughput on validated typed input.

Bind 30 query fields, validate the typed input, and return 30-field JSON — with 36.2% lower p99 than Fiber at equal load.

Throughput Higher is better

Kruda v1.8.0533,210 req/s
Fiber v3.5.0259,170 req/s
Actix v4.15.0521,984 req/s

Corrected p99 at 129k req/s Lower is better

Kruda v1.8.01.970 ms
Fiber v3.5.03.090 ms
Actix v4.15.01.880 ms

21 Sep 2026 · 30-field typed binding + compiled validation · Linux / Wing / HTTP/1.1 loopback · 256 connections

Six balanced permutations per profile. Kruda and Actix were in the same P4 throughput range; Actix led at P8.

Bars start at zero. Capacity and common-load latency are separate measurements. Scoped lab results, not production, TLS or HTTP/2 guarantees.

See all results and test conditions

Released under the MIT License.