> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lightning-go.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Middleware

> Chain middleware functions to run shared logic before and after every request in Lightning.

Middleware in Lightning is any function with the `HandlerFunc` signature:

```go theme={null}
type HandlerFunc func(*Context)
```

The `Middleware` type is an alias for `HandlerFunc`, so the two are interchangeable. Middleware and route handlers share the same type — a middleware is simply a handler that calls `ctx.Next()` to pass control to the next function in the chain.

## How the chain works

When a request arrives, Lightning executes the handlers in order:

1. Global middleware (registered with `app.Use`)
2. Group middleware (if the route belongs to a group)
3. Route-level middleware (extra handlers passed to `Get`, `Post`, etc.)
4. The final route handler

Each function in the chain calls `ctx.Next()` to advance to the next one. You can run logic **before** `ctx.Next()` (pre-processing) and logic **after** it (post-processing).

```go theme={null}
app.Use(func(ctx *lightning.Context) {
    // runs before the handler
    ctx.Next()
    // runs after the handler
})
```

## Global middleware

Register middleware that runs for every request with `app.Use`. You can pass multiple middlewares in a single call or chain multiple `Use` calls.

```go theme={null}
app := lightning.NewApp()

app.Use(func(ctx *lightning.Context) {
    fmt.Println("middleware 1 --->")
    ctx.Next()
    fmt.Println("<--- middleware 1")
})

app.Use(func(ctx *lightning.Context) {
    fmt.Println("middleware 2 --->")
    ctx.Next()
    fmt.Println("<--- middleware 2")
})

app.Get("/", func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{"message": "hello world"})
})
```

For a request to `/`, the execution order is:

```
middleware 1 --->
middleware 2 --->
  handler
<--- middleware 2
<--- middleware 1
```

## Route-level middleware

Pass extra handler functions before the final handler to `Get`, `Post`, or any other method registration. These run only for that specific route.

```go theme={null}
authCheck := func(ctx *lightning.Context) {
    token := ctx.Header("Authorization")
    if token == "" {
        ctx.JSONError(lightning.StatusUnauthorized, "missing token")
        return // do NOT call ctx.Next() — short-circuit the chain
    }
    ctx.Next()
}

app.Get("/dashboard", authCheck, func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{"page": "dashboard"})
})
```

## Writing a custom middleware

Here is a complete auth middleware that validates a bearer token before allowing the request to proceed.

```go theme={null}
func AuthMiddleware(ctx *lightning.Context) {
    token := ctx.Header("Authorization")
    if token != "Bearer secret-token" {
        ctx.JSONError(lightning.StatusUnauthorized, "unauthorized")
        return // stop the chain; do not call ctx.Next()
    }
    ctx.Next()
}
```

Attach it globally or per-route:

```go theme={null}
// Global — applies to every route
app.Use(AuthMiddleware)

// Route-level — applies only to this route
app.Get("/profile", AuthMiddleware, func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{"user": "alice"})
})
```

<Note>
  If you want to abort the request (e.g. due to a failed auth check), return from the middleware **without** calling `ctx.Next()`. Lightning will not advance the chain.
</Note>

## Built-in middleware

Lightning ships two middleware functions you can use immediately.

### Logger

`Logger()` logs each request after it completes, reporting the remote address, method, status code, path, elapsed time, and user agent.

```go theme={null}
app.Use(lightning.Logger())
```

Sample log output:

```
127.0.0.1:54321 GET 200 /users 3ms Mozilla/5.0 ...
```

`Logger` calls `ctx.Next()` internally and logs on the way back, so it measures total handler time correctly.

### Recovery

`Recovery()` catches any panic that occurs downstream, writes a stack trace to stderr, and returns a 500 response. This prevents a single panicking handler from crashing the entire server.

```go theme={null}
app.Use(lightning.Recovery())
```

You can supply a custom handler to control the 500 response:

```go theme={null}
app.Use(lightning.Recovery(func(ctx *lightning.Context) {
    ctx.Fail(9999, "something went wrong, please try again")
}))
```

### Using both together

`lightning.DefaultApp()` is a convenience constructor that registers `Logger` and `Recovery` for you:

```go theme={null}
app := lightning.DefaultApp()
// equivalent to:
// app := lightning.NewApp()
// app.Use(lightning.Logger())
// app.Use(lightning.Recovery())
```

## Complete example

<Tabs>
  <Tab title="Global middleware">
    ```go theme={null}
    package main

    import (
        "fmt"
        "github.com/go-labx/lightning"
    )

    func main() {
        app := lightning.NewApp()

        app.Use(lightning.Logger())
        app.Use(lightning.Recovery())

        app.Use(func(ctx *lightning.Context) {
            fmt.Println("request started")
            ctx.Next()
            fmt.Println("request finished, status:", ctx.Status())
        })

        app.Get("/ping", func(ctx *lightning.Context) {
            ctx.Text(lightning.StatusOK, "pong")
        })

        app.Run()
    }
    ```
  </Tab>

  <Tab title="Route-level middleware">
    ```go theme={null}
    package main

    import "github.com/go-labx/lightning"

    func requireAuth(ctx *lightning.Context) {
        if ctx.Header("Authorization") == "" {
            ctx.JSONError(lightning.StatusUnauthorized, "unauthorized")
            return
        }
        ctx.Next()
    }

    func main() {
        app := lightning.DefaultApp()

        // Public route — no auth
        app.Get("/ping", func(ctx *lightning.Context) {
            ctx.Text(lightning.StatusOK, "pong")
        })

        // Protected route — requireAuth runs first
        app.Get("/me", requireAuth, func(ctx *lightning.Context) {
            ctx.JSON(lightning.StatusOK, lightning.Map{"user": "alice"})
        })

        app.Run()
    }
    ```
  </Tab>
</Tabs>
