Body Parser

Nimbus handlers have full access to the underlying *http.Request via c.Request. You parse request bodies using Go's standard library — there is no magic, just familiar patterns.

Parsing JSON bodies

Use encoding/json to decode the request body into a struct or map. The body is available at c.Request.Body.

type CreateUserInput struct {
    Name  string
    Email string
}

func createUser(c *http.Context) error {
    var input CreateUserInput
    if err := json.NewDecoder(c.Request.Body).Decode(&input); err != nil {
        return c.JSON(400, map[string]string{"error": "invalid json"})
    }
    defer c.Request.Body.Close()

    return c.JSON(201, map[string]string{
        "name":  input.Name,
        "email": input.Email,
    })
}

Parsing form data

For application/x-www-form-urlencoded bodies (standard HTML forms), call c.Request.ParseForm() then read values with c.Request.FormValue(key).

func handleForm(c *http.Context) error {
    if err := c.Request.ParseForm(); err != nil {
        return c.JSON(400, map[string]string{"error": "bad form data"})
    }

    name := c.Request.FormValue("name")
    email := c.Request.FormValue("email")

    return c.JSON(200, map[string]string{
        "name":  name,
        "email": email,
    })
}

Parsing multipart form data

For multipart/form-data (file uploads and mixed fields), call c.Request.ParseMultipartForm(maxMemory). The maxMemory parameter controls how much is buffered in memory (excess goes to temp files).

func handleMultipart(c *http.Context) error {
    // 10 MB max in memory
    if err := c.Request.ParseMultipartForm(10 << 20); err != nil {
        return c.JSON(400, map[string]string{"error": "bad multipart data"})
    }

    title := c.Request.FormValue("title")
    description := c.Request.FormValue("description")

    return c.JSON(200, map[string]string{
        "title":       title,
        "description": description,
    })
}

Binding JSON to a struct

A common pattern is to define a request struct and a helper function that decodes and validates in one step.

func bindJSON(r *http.Request, dest any) error {
    decoder := json.NewDecoder(r.Body)
    decoder.DisallowUnknownFields()
    return decoder.Decode(dest)
}

type UpdatePostInput struct {
    Title   string
    Content string
}

func updatePost(c *http.Context) error {
    var input UpdatePostInput
    if err := bindJSON(c.Request, &input); err != nil {
        return c.JSON(400, map[string]string{"error": err.Error()})
    }
    return c.JSON(200, input)
}

Content-Type detection

Check the Content-Type header to decide how to parse the body. This lets a single handler accept both JSON and form data.

func flexibleHandler(c *http.Context) error {
    ct := c.Request.Header.Get("Content-Type")

    switch {
    case strings.HasPrefix(ct, "application/json"):
        var data map[string]any
        json.NewDecoder(c.Request.Body).Decode(&data)
        return c.JSON(200, data)

    case strings.HasPrefix(ct, "application/x-www-form-urlencoded"):
        c.Request.ParseForm()
        return c.JSON(200, map[string]string{
            "name": c.Request.FormValue("name"),
        })

    default:
        return c.JSON(415, map[string]string{
            "error": "unsupported content type",
        })
    }
}

Tips

  • Always close or drain c.Request.Body to avoid resource leaks.
  • Use io.LimitReader to cap body size before decoding for safety.
  • Use decoder.DisallowUnknownFields() to reject unexpected JSON keys.
  • For validation after parsing, see the Validation docs.