Skip to main content
Every response method lives on *Context and writes to the current request’s response buffer. Call exactly one response method per handler — subsequent calls overwrite the previous ones.

JSON

ctx.JSON(code, obj) serializes any value to JSON and sets Content-Type: application/json; charset=utf-8.
// From a struct
app.Get("/user", func(ctx *lightning.Context) {
    type User struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    ctx.JSON(lightning.StatusOK, &User{Name: "Alice", Age: 30})
})

// From lightning.Map (shorthand for map[string]any)
app.Get("/ping", func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{
        "message": "pong",
    })
})

Text

ctx.Text(code, text) sends a plain text response with Content-Type: text/plain.
app.Get("/health", func(ctx *lightning.Context) {
    ctx.Text(lightning.StatusOK, "ok")
})

XML

ctx.XML(code, obj) marshals a struct to XML and sets Content-Type: application/xml.
type Product struct {
    ID    int    `xml:"id"`
    Name  string `xml:"name"`
    Price int    `xml:"price"`
}

app.Get("/product", func(ctx *lightning.Context) {
    ctx.XML(lightning.StatusOK, &Product{ID: 1, Name: "Widget", Price: 499})
})

Redirects

ctx.Redirect(code, url) issues an HTTP redirect. Use 301 for permanent redirects and 302 for temporary ones.
// Permanent redirect
app.Get("/old-path", func(ctx *lightning.Context) {
    ctx.Redirect(lightning.StatusMovedPermanently, "/new-path")
})

// Temporary redirect
app.Get("/promo", func(ctx *lightning.Context) {
    ctx.Redirect(lightning.StatusFound, "/sale")
})
You can also use the named constants lightning.StatusMovedPermanently (301) and lightning.StatusFound (302).

File

ctx.File(filepath) serves a file from the local filesystem. It returns an error if the file cannot be read.
app.Get("/download", func(ctx *lightning.Context) {
    if err := ctx.File("./files/report.pdf"); err != nil {
        ctx.JSONError(lightning.StatusNotFound, "file not found")
    }
})

Structured API Helpers

These helpers enforce a consistent JSON envelope for application-level responses. They are most useful in API handlers where all responses — success and failure alike — should share the same top-level shape.

ctx.Success(data)

Sends HTTP 200 with:
{"code": 0, "message": "ok", "data": <data>}
app.Get("/items", func(ctx *lightning.Context) {
    items := []string{"apple", "banana", "cherry"}
    ctx.Success(items)
})

ctx.Fail(code, message)

Sends HTTP 200 with an application-level error code and message:
{"code": <code>, "message": "<message>"}
app.Post("/purchase", func(ctx *lightning.Context) {
    ctx.Fail(9999, "network error")
})
ctx.Fail always returns HTTP 200. The code field is an application-level error code, not an HTTP status code. Use this pattern when your API clients always expect 200 and inspect the body for errors.

ctx.JSONError(code, message)

Sends the given HTTP status code with a JSON body matching that code:
{"code": <code>, "message": "<message>"}
app.Get("/admin", func(ctx *lightning.Context) {
    ctx.JSONError(lightning.StatusForbidden, "access denied")
    // HTTP 403, body: {"code": 403, "message": "access denied"}
})
Use ctx.JSONError when you want the HTTP status code and the body code to match — for example, for standard REST error responses.

Custom Response Headers

Set, add, and delete headers

app.Get("/data", func(ctx *lightning.Context) {
    // Replace (or create) a header
    ctx.SetHeader("X-Request-Id", "abc-123")

    // Append a value to a header (allows multiple values for the same key)
    ctx.AddHeader("X-Tag", "first")
    ctx.AddHeader("X-Tag", "second")

    // Remove a header entirely
    ctx.DelHeader("X-Powered-By")

    ctx.JSON(lightning.StatusOK, lightning.Map{"status": "ok"})
})

Cookies

Set a response cookie with ctx.SetCookie(key, value):
app.Post("/login", func(ctx *lightning.Context) {
    ctx.SetCookie("session_id", "tok-abc123xyz")
    ctx.JSON(lightning.StatusOK, lightning.Map{"message": "logged in"})
})