agentleFS
jkanindajkaninda/okapi-skills/response/SKILL.md

response

Skill3 starsChanged 3 months agoMITView on GitHub

For aborts, error handlers, and RFC 7807 Problem Details, see the error_handling/ skill. c.Redirect has no return value — write c.Redirect(...); return nil. c.Respond (alias c.Return) introspects a struct and writes status, headers, cookies, and body from its fields. Field rules: That last rule is easy to trip over: a stray field on the response struct silently becomes a header. Keep response structs to Status, Body, and explicitly tagged fields. The output format comes from the request's Accept header: okapi.HandleIO…

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

Files
SKILL.md

Okapi Response Helpers

For aborts, error handlers, and RFC 7807 Problem Details, see the error_handling/ skill.

JSON Responses

c.OK(data)            // 200
c.Created(data)       // 201
c.NoContent()         // 204, empty body
c.JSON(code, data)    // custom status

Other Formats

c.XML(code, data)
c.YAML(code, data)
c.Text(code, data) / c.String(code, data)   // String is an alias for Text
c.Data(code, contentType, []byte)           // raw bytes with an explicit content type
c.HTML(code, file, data)                    // parse and render a template file
c.HTMLView(code, templateStr, data)         // render an inline template string
c.Render(code, name, data)                  // named template via the configured Renderer
c.Redirect(code, location)                  // writes the redirect; returns nothing

c.Redirect has no return value — write c.Redirect(...); return nil.

File Serving

c.ServeFile(path)
c.ServeFileFromFS(filepath, fs)         // from any http.FileSystem
c.ServeFileAttachment(path, filename)   // Content-Disposition: attachment (download)
c.ServeFileInline(path, filename)       // Content-Disposition: inline

Structured Response (Body / Status / Header Pattern)

c.Respond (alias c.Return) introspects a struct and writes status, headers, cookies, and body from its fields.

type BookResponse struct {
    Status    int    // HTTP status code (defaults to 200 when absent/zero)
    Body      Book   // response payload
    RequestID string `header:"X-Request-ID"` // response header
    Session   string `cookie:"session"`      // response cookie (Path=/)
}

return c.Respond(&BookResponse{Status: 201, Body: book, RequestID: rid})

Field rules:

FieldBecomes
Status (int)HTTP status code
BodyResponse payload
tagged header:"X-Name"Response header
tagged cookie:"name"Set-Cookie with Path=/
anything elseA response header named after the field (or its json tag name)

That last rule is easy to trip over: a stray field on the response struct silently becomes a header. Keep response structs to Status, Body, and explicitly tagged fields.

The output format comes from the request's Accept header:

Accept containsWritten as
application/xmlXML
application/yaml / text/yaml / application/x-yamlYAML
application/jsonJSON
text/plain or text/htmlPlain text (c.String) — not rendered HTML
anything else / absentJSON

okapi.HandleIO and okapi.HandleO write their typed output through the same path.

The target must be a struct or a non-nil pointer to one; otherwise Okapi responds 500.

Response Control

c.WriteStatus(code)              // write the status code only
c.SetHeader(key, value)          // set a response header
c.SetCookie(name, value, maxAge, path, domain, secure, httpOnly) // path defaults to "/"
c.Response()                     // okapi.ResponseWriter (extended interface)
c.ResponseWriter()               // the underlying http.ResponseWriter

Write-Once Semantics

Once a response is committed — by any write helper, including an Abort* call — later calls to c.JSON, c.OK, c.XML, c.Text, c.Render, c.Data, c.Error, c.AbortNotModified, etc. are silent no-ops (logged at debug level). This prevents the double-body bug where a helper aborts without its return value being propagated and the caller then writes a success body:

func loadBook(c *okapi.Context, id int) (*Book, error) {
    b := find(id)
    if b == nil {
        return nil, c.AbortNotFound("book not found") // response already committed
    }
    return b, nil
}

o.Get("/books/{id:int}", func(c *okapi.Context) error {
    b, err := loadBook(c, c.Param("id"))
    if err != nil {
        return err        // preferred: propagate
    }
    return c.OK(b)        // no-op if the response was already written
})

ResponseWriter Extensions

okapi.ResponseWriter extends http.ResponseWriter with:

StatusCode() int                                   // status written, or 0
BytesWritten() int                                 // body bytes written
Close() error                                      // close the writer if supported
Hijack() (net.Conn, *bufio.ReadWriter, error)      // raw TCP (WebSockets, proxies)
Flush()                                            // flush buffered data (streaming, SSE, gzip)
Push(target string, opts *http.PushOptions) error  // HTTP/2 server push

Status Class Helpers

okapi.IsError(code)        // 4xx or 5xx
okapi.IsClientError(code)  // 4xx
okapi.IsServerError(code)  // 5xx

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.