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})
})
| Method | Return type |
|---|
ctx.Param(key string) string | string |
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]string | all 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})
})
| Method | Return type |
|---|
ctx.Query(key string) string | string |
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][]string | all 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
}
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
})
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:
Data is scoped to the single request — it is cleared when the request completes.