Web framework for Go

Every step exposes only what is still legal.

Iris assembles an application through eight interfaces. Each one hands you the next, and each one is narrower than the last. A step called out of turn is a compile error, not something you find in production on a Friday.

go get github.com/kataras/iris/v14@latest

Go 1.27 or newer. BSD-3-Clause. Free, and always has been.

The compiler is the first reviewer

What the chain looks like, and what it refuses

On the left, a production application in one expression. On the right, the same chain with one step out of order. Nothing runs. Nothing gets as far as a test.

Builds

Go
iris.NewBuilder().
    Prefix("/api").
    AllowOrigin("*").
    Compression(true).
    LogRequests(true).
    Health(true, "production", "kataras").
    Errors(errors.NewOptions().
        MapErrors(errors.NotFound, catalog.ErrNotFound)).
    Services(catalog.NewRepository, catalog.NewService).
    RouterMiddlewares().
    Middlewares().
    API("/products", api.NewProductsAPI).
    Build().
    Listen(":8080")

CORS, compression, access logs, a health endpoint, the error map, your services and your API groups. One expression, checked at compile time.

Does not build

Go
iris.NewBuilder().
    Prefix("/api").
    API("/products", api.NewProductsAPI). // not yet
    Build()
Terminal
$ go build ./...
./main.go:12:35: iris.NewBuilder().Prefix("/api").API undefined
	(type iris.CorsBuilder has no field or method API)

Prefix returns CorsBuilder, which exposes AllowOrigin and Cors. There is no API on it to call.

Streaming, and the AI backends built on it

The parts of a live response that break in production

Server-Sent Events are three lines of Fprintf until something buffers them, a write deadline ends the answer mid-sentence, or a client disappears without closing the connection and the work runs on for nobody. The sse package handles those, and ai puts a model's output on top of it. The error branch is rest/errors.HandleError, the same one every other handler uses.

  • Nothing commits until the first Send. An authorization check that fails after Open still returns an ordinary status code with an ordinary body.
  • That first event unbuffers the response. Compression and the recorder an access log installs step aside, the absolute WriteTimeout is cleared, and the headers that stop a proxy buffering go out.
  • A 15s heartbeat notices a client that vanished. Without a write that fails, a closed lid stays invisible for about fifteen minutes of TCP retransmission.
  • One handler serves both AI shapes. Streaming when the request asks for it, one JSON document when it does not, with nothing in between changing.
Go
stream, err := sse.Open(ctx) // writes nothing yet.
if errors.HandleError(ctx, err) {
    return
}
defer stream.Close(nil) // the terminal frame, on success too.

for token := range tokens {
    if err := stream.Send(sse.Event{Name: "token", Data: []byte(token)}); err != nil {
        return // the client is gone.
    }
}
  1. A model's output

    sse.Read · ai.Classify · ai.Relay

    Your loop, or a provider stream relayed as it arrives. A failure gets a class before anything is written: fatal, transient, rate limited, exhausted, canceled.

  2. ai the vocabulary

    text · reasoning · tool · tool-result · usage · error · data

    Seven part kinds and three encoders (SSEJSON, NDJSON, PlainText), none of them a vendor schema. Text and reasoning deltas coalesce at 100ms.

  3. sse the transport

    Open · Send · Close

    Open writes nothing. The first Send commits and unbuffers the response, a 15s heartbeat notices a client that vanished, and Close writes done on success too.

  4. One endpoint, two answers

    text/event-stream · one JSON document

    Options.Stream is a *bool. Left nil, the Accept header decides. Nothing between Open and Close changes.

These two packages come out of a running product rather than a specification. The framework's author also builds Plexon AI, a desktop assistant whose Go backend runs on Iris and streams from Anthropic, OpenAI and a longer list of model providers through one loop. Keeping that loop model-agnostic is what split ai, the vocabulary, from sse, the transport.

iris.NewBuilder

Eight interfaces, each narrower than the last

The builder does not hand back one object with forty methods on it. Each step returns a different interface, and that interface exposes only the steps that are still legal. Follow it down and the aperture closes.

The chain in the framework's own words: Prefix or NoPrefix, AllowOrigin, Compression, LogRequests, Health or NoHealth, Errors, Deferrables, Services, Middlewares, API, Build.Errors, Deferrables, RouterMiddlewares and API return their own interface, so those can repeat.

And then handlers get short

Decoding, validation and error rendering happen once

Configure them at the top of the application and a handler stops repeating them. This one is six lines because the other twenty live somewhere else, in one place, where you can change them all at once.

Go
func (api *ProductsAPI) create(ctx iris.Context) {
    input, ok := ctx.BindJSON[catalog.ProductInput]()
    if !ok {
        return // 400 already written: malformed JSON or failed Validate.
    }

    id, err := api.svc.Create(ctx, input)
    ctx.Created(id, err) // 201, or the centrally mapped error.
}

Batteries included

The parts you would otherwise assemble yourself

In the tree, not in your go.mod

31 middleware packages ship with the framework

Every one of these is versioned with Iris and tested against it. The builder installs several of them for you; the rest are one import away.

Compression covers gzip, deflate, br, snappy, s2 and zstd. What each one does, and 78 runnable examples in the repository.

Fifth Edition · 438 pages

The documentation is a book, and the book is free

24 chapters from a first route to a tested application in production. Written against 14.0.0, with every example verified in the framework source rather than remembered.

No registration, no email address and no payment. If it helps you, sponsoring the project is what keeps it maintained.

Who stands behind it

Ten years in the open

Iris has been developed in public since 2016, funded by the people who use it. The record is all here: who backs it, every commit, and every issue ever filed.

Curious about speed? We benchmark Iris against other frameworks in the open, at kataras/server-benchmarks. The harness, the handlers and the raw results are all in that repository, so you can read the method and run it on your own hardware.

Twenty-five chapters, about 85,000 words. Try BindJSON, graceful shutdown or error map.