Skip to main content
Every handler and middleware receives a *lightning.Context. It wraps the underlying HTTP request and response and provides a clean API for reading input, writing output, and sharing data across the middleware chain.
app.Get("/example", func(ctx *lightning.Context) {
    // ctx is your entire interface to the request and response
})
Two fields are always available directly:
ctx.Method  // e.g. "GET"
ctx.Path    // e.g. "/users/42"

Reading request data

URL parameters

URL parameters are the named segments defined in the route pattern (e.g. :id).
app.Get("/users/:id", func(ctx *lightning.Context) {
    id, err := ctx.ParamInt("id")
    if err != nil {
        ctx.JSONError(lightning.StatusBadRequest, "invalid id")
        return
    }
    ctx.JSON(lightning.StatusOK, lightning.Map{"id": id})
})
MethodReturn type
ctx.Param(key string) stringstring
ctx.ParamInt(key string) (int, error)int
ctx.ParamInt64(key string) (int64, error)int64
ctx.ParamUInt(key string) (uint, error)uint
ctx.ParamUInt64(key string) (uint64, error)uint64
ctx.ParamFloat32(key string) (float32, error)float32
ctx.ParamFloat64(key string) (float64, error)float64
ctx.Params() map[string]stringall params

Query parameters

app.Get("/search", func(ctx *lightning.Context) {
    q := ctx.Query("q")                     // string
    page, _ := ctx.QueryInt("page")         // int, default 0 when absent
    active, _ := ctx.QueryBool("active")    // bool
    all := ctx.Queries()                    // map[string][]string
    _ = q; _ = page; _ = active; _ = all
    ctx.JSON(lightning.StatusOK, lightning.Map{"q": q, "page": page})
})
MethodReturn type
ctx.Query(key string) stringstring
ctx.QueryBool(key string) (bool, error)bool
ctx.QueryInt(key string) (int, error)int
ctx.QueryInt8(key string) (int8, error)int8
ctx.QueryInt32(key string) (int32, error)int32
ctx.QueryInt64(key string) (int64, error)int64
ctx.QueryUInt(key string) (uint, error)uint
ctx.QueryUInt8(key string) (uint8, error)uint8
ctx.QueryUInt32(key string) (uint32, error)uint32
ctx.QueryUInt64(key string) (uint64, error)uint64
ctx.QueryFloat32(key string) (float32, error)float32
ctx.QueryFloat64(key string) (float64, error)float64
ctx.Queries() map[string][]stringall params
Query methods that parse a numeric type return 0 (not an error) when the key is absent from the URL.

Request body

app.Post("/users", func(ctx *lightning.Context) {
    // Raw bytes
    raw := ctx.RawBody()

    // As a string
    text := ctx.StringBody()

    // Decode JSON
    type CreateUserRequest struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    var req CreateUserRequest
    if err := ctx.JSONBody(&req); err != nil {
        ctx.JSONError(lightning.StatusBadRequest, "invalid body")
        return
    }

    _ = raw; _ = text
    ctx.JSON(lightning.StatusOK, lightning.Map{"name": req.Name})
})
Pass true as the second argument to JSONBody to also run struct validation (powered by go-playground/validator):
if err := ctx.JSONBody(&req, true); err != nil {
    ctx.JSONError(lightning.StatusBadRequest, err.Error())
    return
}

Headers

app.Get("/info", func(ctx *lightning.Context) {
    contentType := ctx.ContentType()            // Content-Type header
    auth := ctx.Header("Authorization")         // any header by name
    all := ctx.Headers()                        // map[string]string
    _ = contentType; _ = auth; _ = all
})

Cookies

app.Get("/profile", func(ctx *lightning.Context) {
    session := ctx.Cookie("session")    // *fasthttp.Cookie, or nil
    all := ctx.Cookies()                // []*fasthttp.Cookie
    _ = session; _ = all
})

Client metadata

app.Get("/meta", func(ctx *lightning.Context) {
    ua   := ctx.UserAgent()             // User-Agent string
    ref  := ctx.Referer()              // Referer header
    addr := ctx.RemoteAddr()           // client IP:port
    ct   := ctx.ContentType()          // Content-Type header
    ajax := ctx.IsAjax()               // true if X-Requested-With: XMLHttpRequest
    ws   := ctx.IsWebSocket()          // true if Upgrade: websocket
    langs := ctx.AcceptedLanguages()   // []string from Accept-Language

    _ = ua; _ = ref; _ = addr; _ = ct; _ = ajax; _ = ws; _ = langs
})

Writing responses

JSON

ctx.JSON(lightning.StatusOK, lightning.Map{"message": "hello"})

Plain text

ctx.Text(lightning.StatusOK, "pong")

HTML

Load templates once at startup with app.LoadHTMLGlob, then render by template name:
app.LoadHTMLGlob("templates/*.html")

app.Get("/home", func(ctx *lightning.Context) {
    ctx.HTML(lightning.StatusOK, "index.html", lightning.Map{
        "title": "Home",
    })
})

XML

type User struct {
    XMLName xml.Name `xml:"user"`
    Name    string   `xml:"name"`
}

app.Get("/user.xml", func(ctx *lightning.Context) {
    ctx.XML(lightning.StatusOK, User{Name: "Alice"})
})

File download

app.Get("/export", func(ctx *lightning.Context) {
    if err := ctx.File("/tmp/report.csv"); err != nil {
        ctx.JSONError(lightning.StatusInternalServerError, "file not found")
    }
})

Redirect

ctx.Redirect(lightning.StatusFound, "/login")

Error response

JSONError returns an HTTP error status code alongside a JSON body:
ctx.JSONError(lightning.StatusNotFound, "user not found")
// HTTP 404 {"code":404,"message":"user not found"}

Success and Fail helpers

Use ctx.Success and ctx.Fail to return a consistent envelope shape across your entire API without repeating the structure in every handler.
Success always returns HTTP 200 with code: 0:
ctx.Success(lightning.Map{"id": 1, "name": "Alice"})
// HTTP 200
// {"code":0,"message":"ok","data":{"id":1,"name":"Alice"}}
Fail always returns HTTP 200 with a non-zero application error code and a message:
ctx.Fail(9999, "network error")
// HTTP 200
// {"code":9999,"message":"network error"}
This pattern lets clients always check the HTTP status for transport errors and the code field for application errors.

Per-request data store

SetData, GetData, and DelData let you attach arbitrary values to the current request context. This is useful for passing data from middleware to downstream handlers without mutating global state.
// In middleware: set the authenticated user
app.Use(func(ctx *lightning.Context) {
    token := ctx.Header("Authorization")
    if token != "" {
        ctx.SetData("userID", 42)
    }
    ctx.Next()
})

// In a route handler: read what the middleware set
app.Get("/profile", func(ctx *lightning.Context) {
    userID, ok := ctx.GetData("userID").(int)
    if !ok {
        ctx.JSONError(lightning.StatusUnauthorized, "not authenticated")
        return
    }
    ctx.JSON(lightning.StatusOK, lightning.Map{"userID": userID})
})
GetData returns any, so you need a type assertion. DelData removes a key:
ctx.DelData("userID")
Data is scoped to the single request — it is cleared when the request completes.