<!--
  Core Concepts
  From the Iris book, chapter 2, covering Iris 14.0.0 on Go 1.27.
  Import path: github.com/kataras/iris/v14
  Canonical page: https://iris-go.com/docs/core-concepts/
-->
# Chapter 2: Core Concepts

Every Iris program is built from four cooperating parts: an **Application**
that owns the server lifecycle, a **Router** that maps requests to code, a
pooled **Context** that carries one request from start to finish, and plain
**handlers** chained into a middleware pipeline. This chapter builds the
mental model of how those parts fit together, so that the rest of the book
reads as detail rather than surprise. Everything shown here is the v14 API,
verified against the framework source.

## Background

Chapter 1 showed a request and a response from a distance. The parts of
Iris this chapter describes exist to serve that exchange, so it is worth
pinning down the vocabulary first.

**The anatomy of an HTTP exchange.** Each request names a method, a
path, headers and possibly a body. The method is the verb: `GET` reads,
`POST` creates or submits, `PUT` replaces, `PATCH` partially updates,
`DELETE` removes. The path (`/users/42`) names the thing the verb acts
on. Headers are key-value metadata lines: who is asking
(`Authorization`), what format the client wants back (`Accept`), what
it is sending (`Content-Type`). The body carries the payload itself:
form fields, a JSON document, an uploaded file. The response mirrors
this with a status code, headers and a body. Status codes come in
classes: `2xx` means success, `3xx` redirects the client elsewhere,
`4xx` means the client made a mistake (`404` unknown path, `400`
malformed input), and `5xx` means your server failed (`500` is the
generic failure). You will choose methods and status codes constantly,
so this small vocabulary pays for itself quickly.

**Router and handler.** A handler is a function you write that serves
one request: it reads what it needs and writes a response. The router
is the dispatcher in front of all your handlers: it looks at each
request's method and path and decides which handler runs. "Routing" is
that decision; a "route" is one registered rule, such as
`GET /users/{id}` runs `getUser`. Middleware (detailed later in this
chapter) is a handler that runs before or after other handlers to do
shared work such as logging or authentication.

**Why object pooling.** A busy server may handle tens of thousands of
requests per second, and Go serves each one on its own goroutine, a
lightweight thread that Go can run by the million. If the framework
allocated a fresh context object for every request, it would create
garbage at the same rate, and Go's garbage collector would burn CPU
cleaning it up. Object pooling is the fix: keep a bin of used context
objects, wipe one clean for each new request, and drop it back in the
bin afterwards, the way a restaurant washes plates instead of buying
new ones for every meal. The only cost is a rule you must respect:
never keep a context after your handler returns, because the same
object will be reused for another request. The Context section below
restates that rule where it matters.

## The Big Picture

```
+---------------------------------------------------+
|  net/http server            app.Listen / app.Run  |
+---------------------------------------------------+
|  Application                iris.go               |
|    embeds the root Router builder                 |
+---------------------------------------------------+
|  Router                     core/router           |
|    routes, groups (Router/Child), the mux         |
+---------------------------------------------------+
|  Middleware pipeline        UseRouter / Use       |
+---------------------------------------------------+
|  Handlers                   func(ctx iris.Context)|
|    one pooled Context per request                 |
+---------------------------------------------------+
```

A request enters through a standard `net/http` server, is bound to a
`Context` taken from a pool, travels through the router's middleware and the
matched route's handler chain, and leaves as a written response. The
`Context` is then reset and returned to the pool. There is no reflection and
no magic in the hot path: a handler is a plain function, a chain is a slice
of functions, and `ctx.Next()` moves an index forward.

## The Application

`iris.New` returns a `*iris.Application`, a concrete struct defined in
`iris.go`. Its routing surface is not its own: the struct embeds the root
router builder, so every routing, middleware, and dependency-injection
method you call on `app` comes from the embedded `*router.RouterBuilder`.

```go
type Application struct {
    *router.RouterBuilder // routing, middleware, DI methods

    // unexported: options, context pool, logger, closers...
}
```

What the `Application` itself adds is lifecycle management:

| Method                               | Purpose                                                                                               |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| `app.Listen(addr)`                   | Build and serve on a TCP address with sane defaults; a failed start is logged and exits with status 1 |
| `app.Run(iris.ServerOptions{...})`   | Build and serve with explicit server options; same exit rule as `Listen`                              |
| `app.Build()`                        | Construct the router and internal state manually                                                      |
| `app.Shutdown(ctx)`                  | Gracefully stop the server and registered closers                                                     |
| `app.Logger()`                       | The application's structured logger                                                                   |
| `app.EditOptions(fn)`                | Modify options before the application is built                                                        |
| `app.RegisterOnBuild(before, after)` | Hooks around the build step                                                                           |

`app.Listen(":8080")` is a shortcut for `app.Run` with a TCP network,
keep-alive, and socket sharding preconfigured. After `Build`, the
application is a standard `http.Handler`, which is why it composes cleanly
with the Go ecosystem and with `httptest` (see
[Chapter 16](https://iris-go.com/docs/testing/)).

A minimal but complete program:

```go
package main

import (
    "github.com/kataras/iris/v14"
)

func main() {
    app := iris.New(iris.Options{
        Name:     "hello",
        LogLevel: "debug",
    })

    app.Get("/", func(ctx iris.Context) {
        ctx.Text("Hello, Iris!")
    })

    app.Listen(":8080")
}
```

`iris.Options` configures the application (`Name`, `LogLevel`, `Host`, plus
nested `Router` and `Context` option structs). A partial literal merges
with the defaults: unset fields are filled in, never zeroed. Configuration
is covered in depth in [Chapter 11](https://iris-go.com/docs/configuration-management/).

For production applications the recommended entry point is the compile-time
checked `iris.NewBuilder` chain rather than bare `iris.New`; that is the
subject of [Chapter 3](https://iris-go.com/docs/iris-newbuilder/). Everything in this chapter
applies to both, because the builder produces the same `*iris.Application`.

## The Context

`iris.Context` is an alias for `*context.Context`, the object that
represents one request-response cycle. Contexts are pooled: at the start of
a request one is acquired from a `sync.Pool`, wired to the incoming
`*http.Request` and a wrapped response writer, and after the last handler
returns it is reset and released back to the pool. This is why you must not
keep a reference to `ctx` in a goroutine that outlives the handler: by the
time it runs, the same object may be serving another request.

### It is a context.Context

`iris.Context` implements the standard `context.Context` interface
(`Deadline`, `Done`, `Err`, `Value`). You pass it directly into services,
database drivers, and HTTP clients, and request cancellation propagates all
the way down:

```go
func (api *BooksAPI) list(ctx iris.Context) {
    // ctx satisfies context.Context: pass it straight down.
    books, err := api.svc.Books(ctx)
    ctx.List(books, err)
}
```

For background work that must survive the response, derive a detached
context with `ctx.WithoutCancel()`: it is no longer canceled when the
request ends, but keeps the request's values.

### Method families at a glance

The Context API is large but regular. Almost everything falls into three
families:

| Family           | Representative methods                                                                                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Request reading  | `Method`, `Path`, `Host`, `RemoteAddr`, `Header(name)`, `ContentType()`, `MediaType()`, `Query()`, `Params()`, `Body()`, `ReadJSON`, `ReadForm`, `ReadQuery`, `ReadBody`, `Cookie`, `CookieValue` |
| Response writing | `StatusCode`, `SetHeader`, `AddHeader`, `SetContentType`, `JSON`, `Text`, `HTML`, `Writef`, `Write`, `Redirect`, `SendFile`, `ServeFile`, `SetCookie`, `ResponseHeader`, `ResponseWriter()`       |
| Flow control     | `Next`, `Skip`, `StopExecution`, `IsStopped`, `StopWithStatus`, `StopWithError`, `StopWithJSON`, `StopWithText`, `Values()`, `SetErr`, `GetErr`                                                   |

Two naming rules keep the request and response sides apart:

- `ctx.Header(name)` reads a **request** header. `ctx.SetHeader` writes a
  **response** header, and `ctx.ResponseHeader(name)` reads one back.
- `ctx.ContentType()` and `ctx.MediaType()` describe the **request** body.
  The response-side counterparts are `ctx.ResponseContentType()` and
  `ctx.ResponseMediaType()`.

Path and query values come through typed accessors that return a value
directly, with a type-specific default on failure instead of a tuple:
`GetString` returns `""`, `GetInt` returns `-1`, `GetBool` returns `false`.

```go
package main

import (
    "github.com/kataras/iris/v14"
)

func search(ctx iris.Context) {
    term := ctx.Params().GetString("term") // "" when missing
    page := ctx.Query().GetInt("page")     // -1 when missing or invalid
    if page <= 0 {
        page = 1
    }

    ctx.SetHeader("X-Page-Size", "20") // response header
    ctx.JSON(iris.Map{"term": term, "page": page})
}

func main() {
    app := iris.New()
    app.Get("/search/{term}", search)
    app.Listen(":8080")
}
```

### Request-scoped values

Middleware communicates with later handlers through `ctx.Values()`, a
per-request key-value store, freed together with the context:

```go
func requireShelf(ctx iris.Context) {
    shelf := loadShelf(ctx) // ... 404 and return when not found
    ctx.Values().Set("shelf", shelf)
    ctx.Next()
}

func listBooks(ctx iris.Context) {
    // Type-safe retrieval with the generic method.
    shelf, ok := ctx.ValueAs[*Shelf]("shelf")
    if !ok {
        // The route is not behind requireShelf: a wiring bug, not a client one.
        errors.Internal.Log(ctx, "shelf value missing from the request store")
        return
    }
    ctx.List(shelf.Books, nil)
}
```

Reading request bodies (`ReadJSON`, `ReadForm`, and the `ctx.BindJSON[T]()`
method with automatic validation) is covered in
[Chapter 5](https://iris-go.com/docs/reading-requests/); response rendering in
[Chapter 6](https://iris-go.com/docs/response-handling/).

## The Router

The router has two lives. At design time it is a **builder**: you register
routes, groups, and middleware on it. At build time (`app.Build`, called by
`Listen`/`Run`) it compiles everything into a **mux**: the internal
`ServeMux` that performs the actual matching of method, subdomain, and path
for every request. Registration cost therefore never shows up at serve
time.

("Mux" is short for multiplexer: the single component every request
enters, which fans each one out to the correct handler chain. Go's
standard library uses the same word for its `http.ServeMux`.)

### Routes

Routes are registered with HTTP-verb methods (`Get`, `Post`, `Put`,
`Delete`, `Patch`, `Head`, `Options`), with `Handle` for explicit or
multiple methods, with `Any` for all methods, or with `Resource` for
`GET`+`HEAD`:

```go
app.Get("/about", about)
app.Post("/contact", contact)
app.Handle("GET,POST", "/form", form)
```

Path segments can be dynamic, typed parameters called macros:

```go
app.Get("/users/{id:uuid}", getUser)      // validated UUID
app.Get("/posts/{page:int}", listPosts)   // integer
app.Get("/files/{p:path}", serveTree)     // wildcard, may contain /
app.Get("/tags/{name}", tagged)           // untyped = string

// Macro functions refine a type:
app.Get("/products/{id:int min(1)}", product)
```

A parameter that fails its type check does not match the route; in
builder-built applications a malformed typed parameter renders a `400
INVALID_ARGUMENT` response. The full macro catalog and custom macro
registration live in [Chapter 4](https://iris-go.com/docs/routing-controllers/).

### Groups: Router and Child

`Router(relativePath, ...)` creates a child router mounted under a path
prefix. A child inherits its parent's middleware, context options, and
dependency container; new routes it registers land in the one shared route
registry. Three call styles cover most needs:

```go
// 1. Plain group, optionally with middleware handlers.
api := app.Router("/api/v1")
api.Get("/health", health)

users := api.Router("/users") // nested: /api/v1/users
users.Get("/", listUsers)
users.Get("/{id:uuid}", getUser)

// 2. Function form: configure the group in one place.
app.Router("/admin", func(r iris.Router) {
    r.Use(requireAdmin)
    r.Get("/stats", stats)
})

// 3. Controller form: a constructor resolved by dependency injection
//    (see the last section of this chapter).
app.Router("/books", NewBooksAPI)
```

`Child(relativePath, handlers...)` is the underlying primitive that clones
the builder for a sub-path; `Router` builds on it and additionally accepts
functions and controllers. Subdomain groups exist too:
`app.Subdomain("admin")` returns a router whose routes match only
`admin.<host>` requests.

### When nothing matches

If the mux finds no route, the request runs the no-route chain (default:
`404 Not Found`). If the path exists but the method does not, the request
runs the no-method chain (default: `405 Method Not Allowed`), but only
when you have asked for it. `Router.FireMethodNotAllowed` defaults to
false, and until you set it a wrong-method request lands in the no-route
chain like any other miss. Both chains are replaceable:

```go
app.HandleNoRoute(func(ctx iris.Context) {
    errors.NotFound.Message(ctx, "no route matches this path")
})
app.HandleNoMethod(func(ctx iris.Context) {
    errors.MethodNotAllowed.Message(ctx, "this path does not accept that method")
})
```

Those defaults are the plain status text on an `iris.New()` application.
An application built with `iris.NewBuilder()` fills both chains with
`errors.Handler` from `rest/errors`, so a miss answers a JSON
`NOT_FOUND` or `METHOD_NOT_ALLOWED` instead. It fills them when the
application is built and only when they are empty, so on a builder
application your own handlers have to be registered from a
`Configurator` passed to `iris.NewBuilder`, or on the unbuilt value
`iris.Default()` returns; a `HandleNoRoute` called on the built
application does not replace the JSON answer. Structured error responses
via the `rest/errors` package are the topic of [Chapter
9](https://iris-go.com/docs/error-handling/).

## Handlers and the Middleware Pipeline

A handler is any `func(ctx iris.Context)`, the `iris.Handler` type. There
is exactly one signature; middleware and final handlers are the same kind
of function, distinguished only by position in the chain. A handler either
finishes the response or calls `ctx.Next()` to hand over to the next
handler. Code after `ctx.Next()` runs on the way back out, which is how
timing, logging, and cleanup middleware work:

```go
package main

import (
    "time"

    "github.com/kataras/iris/v14"
    "github.com/kataras/iris/v14/rest/errors"
)

func timing(ctx iris.Context) {
    start := time.Now()
    ctx.Next() // run the rest of the chain
    ctx.Application().Logger().Debugf("%s %s took %s",
        ctx.Method(), ctx.Path(), time.Since(start))
}

func requireToken(ctx iris.Context) {
    if ctx.Header("Authorization") == "" {
        errors.Unauthenticated.Message(ctx, "missing Authorization header")
        return // no Next: the chain ends here
    }
    ctx.Next()
}

func main() {
    app := iris.New()
    app.Use(timing)
    app.Get("/private", requireToken, func(ctx iris.Context) {
        ctx.Text("secret")
    })
    app.Listen(":8080")
}
```

```
request --> timing --> requireToken --> handler
                                          |
response <-- timing <------------------ (return)
```

Not calling `ctx.Next()` ends the chain quietly; `ctx.StopExecution()` (and
the `StopWith*` shortcuts) additionally marks the context stopped so that
any later `Next` calls are ignored. `ctx.IsStopped()` reports that state.

### Use, UseRouter, UseRelevant

Where you attach middleware determines which requests it sees. The three
registration methods, verified against `core/router/router_builder.go`:

| Method        | On the root app                                  | On a child router                                        |
| ------------- | ------------------------------------------------ | -------------------------------------------------------- |
| `UseRouter`   | Before routing: every request, including 404/405 | Same, guarded by the child's path prefix (and subdomain) |
| `Use`         | All matched routes, plus the 404 and 405 chains  | Matched routes of that child and its children only       |
| `UseRelevant` | Matched routes only, never 404/405               | Same as `Use`                                            |

The root-versus-child difference in `Use` is the one that surprises people:
`app.Use(m)` also runs `m` for requests that match no route at all, because
the root's `Use` handlers are prepended to the no-route and no-method
chains. On a child router, `Use` delegates to `UseRelevant`, so it affects
only routes registered under that child. When you want root middleware that
skips 404s, say so explicitly with `app.UseRelevant(m)`.

`UseRouter` runs even earlier: before the mux looks at the path. That makes
it the right place for access logging, request IDs, rate limiting, and
anything that must observe every request. From a child, `UseRouter` still
registers globally but wraps the handler in a prefix check, so it fires
only for requests under that child's path.

Two more variants exist: `UseOnce` and `UseRelevantOnce` insert a handler
or replace an already-registered instance of it, which keeps middleware
idempotent when setup code may run twice. And below all of this,
`app.WrapRouter` wraps the entire router at the `net/http` level for
integrations that need raw `http.Handler` semantics.

Order matters: a route's chain is assembled when the route is registered,
so register middleware before the routes it should affect.

## The Request Lifecycle

From accepted connection to released context:

\[Diagram: A request passes the router wrappers, borrows a Context from the pool, runs the UseRouter chain into the mux, and either branches to a redirect or the 404 and 405 chains or enters the matched route's frozen chain before the Context is reset and returned to the pool.]

Step 3 and step 8 are the pooling boundary. Between them the `Context` is
exclusively yours; outside them it does not exist from your code's point of
view. Step 5 is pure matching against the compiled mux: the trie compares
segment count and static segments, and a typed parameter such as
`{id:int}` is *not* checked here. That check is a handler the router
prepends to the matched route's own chain, which is why it appears as
handler 0 of step 6. No route's handlers run before the router has decided,
which is what makes the `UseRouter` versus `Use` distinction meaningful.

Graceful shutdown belongs to the same lifecycle at application scale:
`app.Shutdown(ctx)` stops the server and runs everything registered with
`app.AddCloser`. Deployment patterns around it are covered in
[Chapter 19](https://iris-go.com/docs/deployment-devops/).

## Where Dependency Injection Sits

Handlers keep the single `func(ctx iris.Context)` signature; dependencies
do not appear as handler parameters. Instead, constructors declare what
they need, and the router resolves those constructors from registered
services when routes are set up: at startup, never per request.

The application-level primitives:

```go
package main

import (
    "log"

    "github.com/kataras/iris/v14"
)

type Greeter struct{ Prefix string }

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

    if err := app.RegisterDependency(&Greeter{Prefix: "Hello, "}); err != nil {
        log.Fatal(err)
    }

    // HandlerFunc resolves the function's parameters from the
    // registered dependencies and returns a plain iris.Handler.
    app.Get("/{name}", app.HandlerFunc(func(g *Greeter) iris.Handler {
        return func(ctx iris.Context) {
            ctx.Text(g.Prefix + ctx.Params().GetString("name"))
        }
    }))

    app.Listen(":8080")
}
```

The same mechanism powers the controller style you saw in the router
section: `app.Router("/books", NewBooksAPI)` calls the constructor with
arguments resolved from the registered services, then invokes the returned
struct's `Routing(r iris.Router)` method once to register its routes:

```go
type BooksAPI struct {
    svc *library.Service
}

func NewBooksAPI(svc *library.Service) *BooksAPI {
    return &BooksAPI{svc: svc}
}

func (api *BooksAPI) Routing(r iris.Router) {
    r.Get("/", api.list)
    r.Get("/{id:uuid}", api.get)
}
```

In builder-based applications the services are registered through the
`Services(...)` step of the `iris.NewBuilder` chain, and any service
implementing `Init(ctx context.Context) error` is initialized once inside
`Build()`, after injection. The full resolution rules (constructors,
struct field binding, per-router containers) are the subject of
[Chapter 8](https://iris-go.com/docs/dependency-injection/).

## Summary

- The **Application** (`iris.go`) owns lifecycle: build, serve, shutdown.
  Its routing API comes from the embedded root `*router.RouterBuilder`,
  and after `Build` it is a standard `http.Handler`.
- The **Context** is pooled per request and implements `context.Context`,
  so it flows into services and database calls. Its methods split into
  request reading, response writing, and flow control; `Header` reads the
  request while `SetHeader`/`ResponseHeader` handle the response.
- The **Router** is a builder at design time and a compiled mux at serve
  time. Groups come from `Router`/`Child`, unmatched requests run the
  `HandleNoRoute`/`HandleNoMethod` chains.
- **Middleware** placement is semantic: `UseRouter` runs before routing
  for every request, root `Use` covers all routes plus 404/405, child
  `Use` (and `UseRelevant`) covers matched routes only.
- **Dependency injection** happens at startup: constructors such as
  `NewBooksAPI(svc)` are resolved from registered services, and handlers
  keep their plain signature.

With the model in place, the next chapter introduces the recommended way
to assemble production applications: the compile-time checked builder
chain.

## Further Reading

- [MDN: HTTP request methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods):
  what each verb means and which are safe to repeat.
- [MDN: HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status):
  the full status-code catalog with plain-English meanings.
- [MDN: HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers):
  reference for the metadata lines on both sides of the exchange.
- [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110):
  the authoritative specification behind methods, headers and status
  codes.
- [net/http (pkg.go.dev)](https://pkg.go.dev/net/http): the standard
  library server Iris builds on; after `Build` the application is one
  of its `http.Handler`s.
- [sync.Pool (pkg.go.dev)](https://pkg.go.dev/sync#Pool): the Go
  primitive behind context pooling.
