{"id":39600071,"url":"https://github.com/monadicstack/respond","last_synced_at":"2026-01-18T07:51:53.045Z","repository":{"id":51330117,"uuid":"325400400","full_name":"monadicstack/respond","owner":"monadicstack","description":"A Go package to simplify writing HTTP responses.","archived":false,"fork":false,"pushed_at":"2021-05-14T20:45:46.000Z","size":76,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-14T23:49:02.924Z","etag":null,"topics":["go","http","standard-library"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/monadicstack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-29T22:32:13.000Z","updated_at":"2021-05-14T20:43:22.000Z","dependencies_parsed_at":"2022-09-05T20:11:02.889Z","dependency_job_id":null,"html_url":"https://github.com/monadicstack/respond","commit_stats":null,"previous_names":["robsignorelli/respond"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/monadicstack/respond","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monadicstack%2Frespond","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monadicstack%2Frespond/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monadicstack%2Frespond/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monadicstack%2Frespond/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/monadicstack","download_url":"https://codeload.github.com/monadicstack/respond/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monadicstack%2Frespond/sbom","scorecard":{"id":658054,"data":{"date":"2025-08-11","repo":{"name":"github.com/monadicstack/respond","commit":"eea229b6a620980bcfc624803c185b625274adb1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/12 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 8 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T15:13:23.130Z","repository_id":51330117,"created_at":"2025-08-21T15:13:23.130Z","updated_at":"2025-08-21T15:13:23.130Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28533231,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","http","standard-library"],"created_at":"2026-01-18T07:51:52.889Z","updated_at":"2026-01-18T07:51:52.974Z","avatar_url":"https://github.com/monadicstack.png","language":"Go","readme":"# Respond\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/monadicstack/respond)](https://goreportcard.com/report/github.com/monadicstack/respond)\n\nThis package reduces the verbosity associated with responding to HTTP\nrequests when using standard `net/http` handlers (with a bent towards JSON\nbased REST APIs). Most HTTP handling\nexamples using the standard library tend to be 25% handling logic and\n75% dealing with data marshaling, header management, status code updates,\netc. Respond tries to flip that ratio so more of your code is business\nlogic while still giving you robust, readable, maintainable response handling.\n\nYou focus on writing awesome code that does something meaningful\nand `respond` takes care of the messy code that figures out how to\nsend your result to the caller.\n\n### Getting Started\n\n```\ngo get -u github.com/monadicstack/respond\n```\n\n### Basic Example\n\nHere is a sample HTTP handler using the standard library.\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    userID := param(req, \"user\")\n    user, err := userRepo.FindById(userID)\n\n    if err != nil {\n\t    http.Error(w, err.Error(), 500)\n\t    return\n    }\n    jsonBytes, err := json.Marshal(user)\n    if err != nil {\n    \thttp.Error(w, err.Error(), 500)\n    \treturn\n    }\n    w.Header().Set(\"Content-Type\", \"application/json\")\n    w.WriteHeader(200)\n    w.Write(jsonBytes)\n}\n```\n\nHere is the same handler using the `respond` package. Create a\n`Responder` for your writer/request pair and use the aptly-named\nfunctions that line up to the type of HTTP response you want.\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    userID := param(req, \"user\")\n    user, err := userRepo.FindById(userID)\n\n    // If you want a 200 status...\n    response.Ok(user, err)\n}\n```\n\n### Success Responses\n\nResponders have named helpers for some of the more common success\ncodes you might want to respond with:\n\n```go\nresponse := respond.To(w, req)\n...\n\n// Responds w/ a 200 and 'someValue' as JSON \nresponse.Ok(someValue)\n\n// Responds w/ a 201 and 'someValue' as JSON \nresponse.Created(someValue)\n\n// Responds w/ a 202 and 'someValue' as JSON \nresponse.Accept(someValue)\n\n// Responds w/ a 204 and no body. \nresponse.NoContent()\n\n// Responds w/ a 304 and no body. \nresponse.NotModified()\n```\n\n### Error Handling\n\nThe `Responder` type has a bunch of helpful functions for responding\nwith meaningful error statuses and messages. All error messages\nsupport `printf` style formatting.\n\n```go\nresponse := respond.To(w, req)\n...\n// Status =\u003e 401\n// Body   =\u003e { \"status\": 401, \"message\": \"what, no credentials?\" }\nresponse.Unauthorized(\"what, no credentials?\")\n\n// Status =\u003e 403\n// Body   =\u003e { \"status\": 403, \"message\": \"missing 'badass' role\" }\nresponse.Forbidden(\"missing '%s' role\", someRole)\n\n// Status =\u003e 404\n// Body   =\u003e { \"status\": 404, \"message\": \"unable to find user [123]\" }\nresponse.NotFound(\"unable to find user [%s]\", userID)\n\n// Status =\u003e 500\n// Body   =\u003e { \"status\": 500, \"message\": \"what did you do!?!\" }\nresponse.InternalServerError(\"what did you do!?!\")\n\n// Status =\u003e 503\n// Body   =\u003e { \"status\": 503, \"message\": \"not working right now\" }\nresponse.ServiceUnavailable(\"not working right now\")\n\n```\nThese are just a few common ones. Check the docs or use auto-complete\nin your IDE to see which errors are supported.\n\nHere's a common pattern for utilizing these response functions if\nyou use named errors. It gives you strongly coded errors while\nleft-aligning your code to help keep your handler code more\nidiomatic. If you have more control over the errors you generate,\ntake a look at the next section to see how you can reduce this\nboilerplate even further.\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    userID := param(req, \"user\")\n    user, err := userRepo.FindById(userID)\n\n    switch err {\n    case ErrNoSuchAccount:\n        response.NotFound(\"no such users: %s\", userID)\n    case ErrDatabaseConn:\n        response.ServiceUnavailable(\"user database unavailable\")\n    default:\n        response.Ok(user, err)\n    }\n}\n```\n\n### Error Handling: Shorthand\n\nWhile it has its places, writing those `switch` statements to respond\nwith the correct status code can be a pain. All of the success\nresponse functions like `Ok()`, `Accepted()`, etc accept an optional\n`Error` argument (yes... by bastardizing variadic functions... sorry).\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    userID := param(req, \"user\")\n    user, err := userRepo.FindById(userID)\n\n    // When 'err' is nil, the result will be a 200 w/ the user\n    // data as JSON. When 'err' is non-nil, we'll ignore the\n    // user data and return a 4XX/5XX error w/ err's message.\n    response.Ok(user, err)\n}\n```\n\n#### How Does It Know Which 4XX/5XX Status To Use?\n\n`respond` uses Go 1.13 error unwrapping to detect if your error\nconforms to one of these three error interfaces:\n\n* `ErrorWithCode`\n* `ErrorWithStatus`\n* `ErrorWithStatusCode`\n\nThese are all `Error` interfaces that also contain either of\nthese functions:\n\n* `func Code() int`\n* `func Status() int`\n* `func StatusCode() int`\n  \nThis allows you to use your own custom error types, and as long\nas they have one of these 3 functions, `respond` will use that\nstatus code in the error.\n\nHere is a sample error you might write that satisfies the\n`ErrorWithCode` interface.\n\n```go\ntype NotFoundError struct {\n    message string\n}\nfunc (err NotFoundError) Error() string {\n    return err.message\n}\nfunc (err NotFoundError) Code() int {\n    return 404\n}\n```\n\nYour business logic can just naturally return meaningful errors.\n\n```go\nfunc (repo *UserRepo) FindById(userID string) (*User, error) {\n    user := User{}\n    row := repo.db.Query(\"...\")\n    err := row.Scan(...)\n\n    switch err {\n    case sql.ErrNoRows:\n        return nil, NotFoundError{message: \"no such user\"} \n    default:\n        return user, err\n}\n```\n\nNow your handler will return a 404 when the user doesn't exist and\nfall back to a good 'ol 500 on any other type of error. Like before,\nif there was no error, this will result in a 200 w/ the user data.\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    userID := param(req, \"user\")\n    user, err := userRepo.FindById(userID)\n\n    response.Ok(user, err)\n}\n```\n\n### Redirects\n\nDepending on what will make your handler more clear, you have two\noptions for triggering an HTTP redirect response:\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    fileID := param(req, \"file\")\n    file, ok := fileStore.GetFileInfo(fileID)\n    if !ok {\n        response.NotFound(\"file not found: %s\", fileID)\n        return\n    }\n\n    response.Redirect(\"https://%s.s3.amazonaws.com/%s/%s\",\n    \tfile.Bucket,\n    \tfile.Directory,\n    \tfile.Name,\n    )\n}\n```\n\nThis is fine when your redirects are simple, but the more complex\nyour substitutions are, the more you lose clarity because your focus\nis drawn to the URL substitution rather than the actual business logic.\n\nAlternately, you can use `RedirectTo()` and pass any value that implements\nthe `Redirector` interface; basically can return the fully-formed URL\nthat you want to redirect to. This can help clean up your handlers by\nmoving the URL building logic elsewhere, so your handler stays lean\nand mean:\n\n```go\nfunc MyHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    fileID := param(req, \"file\")\n    file, ok := fileStore.GetFileInfo(fileID)\n    if !ok {\n        response.NotFound(\"file not found: %s\", fileID)\n        return\n    }\n    response.RedirectTo(file)\n}\n```\n\n```go\ntype S3FileInfo struct {\n    Bucket    string\n    Directory string\n    Name      string\n}\n\nfunc (f S3FileInfo) Redirect() string {\n    return fmt.Sprintf(\"https://%s.s3.amazonaws.com/%s/%s\", \n        file.Bucket,\n        file.Directory,\n        file.Name,\n    )\n}\n```\n\nAs you can see, your handler is a bit cleaner, and it's easier to\nreason about what it's actually doing. Additionally, you can write\nURL formatting tests independent of your handler tests.\n\nOne final note. Both `Redirect()` and `RedirectTo()` have \"permanent\"\nvariants that result in a 308 HTTP status rather than a 307:\n`RedirectPermanent()` and `RedirectPermanentTo()`.\n\n### Responding With Images And Other Raw Files\n\nYou can use the `Serve()` and `Download()` functions to deliver\nraw file data rather than marshaled JSON. Both accept an `io.Reader`\nas the data source.\n\n```go\nfunc ProfilePictureHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    userID := param(req, \"user\")\n    image, err := pictureStore.ReadFile(userID + \".jpg\")\n    defer imageReader.Close()\n\n    // Deliver the picture \"inline\" for use in an \u003cimg\u003e tag.\n    response.Serve(\"profile.jpg\", image, err)\n\n    // OR\n\n    // Offer a download dialog for the image\n    response.Download(\"profile.jpg\", image, err)\n}\n```\n\nIn addition to writing the bytes, `respond` will apply the correct\n`Content-Type` and `Content-Disposition` headers based on the name/extension\nof the file you provide.\n\n### Raw Files By Implementing ContentReader\n\nIf you'd like to decouple yourself further from the `respond`\nlibrary when serving up raw files, you can continue to respond\nusing `Ok()` with your own structs/values as long as it implements\n`ContentReader` - which basically means that it has a `Content()` method\nthat returns an `io.Reader` with the raw data.\n\nInstead of marshaling the result value as JSON and responding\nwith those bytes, it will respond with the raw bytes your\nreader supplies.\n\n```go\nfunc ExportCSV(w http.ResponseWriter, req *http.Request) {\n    // This is an *Export which implements ContentReader\n    export := crunchTheNumbers() \n\n    // Respond with the raw CSV reader data and the following:\n    // Status = 200\n    // Content-Type = 'application/octet-stream'\n    // Content-Disposition = 'inline'\n    // Body = (whatever .Read() gave us)\n    respond.To(w, req).Ok(export)\n}\n\ntype Export struct {\n    csvData *bytes.Buffer\n}\n\nfunc (e Export) Content() io.Reader {\n    return e.csvData\n}\n```\n\nMost of the time, however, you probably don't want that generic\ncontent type. Additionally, there may be instances where you'd\nrather have the client trigger a download rather than consume\nthe content inline.\n\nTo rectify that, you can implement two optional interfaces to\ncustomize both behaviors:\n\n```go\n// Implement this to customize the \"Content-Type\" header.\ntype ContentTypeReader interface {\n    ContentType() string\n}\n\n// Implement this to allow an \"attachment\" disposition instead.\n// The value you return will be the default file name offered to\n// the client/user when downloading.\ntype ContentFileNameReader interface {\n    ContentFileName() string\n}\n```\n\nUpdating our example to customize both values, we end up\nwith the following:\n\n```go\nfunc ExportCSV(w http.ResponseWriter, req *http.Request) {\n    // This is an *Export which implements all 3 Content-based interfaces\n    export := crunchTheNumbers()\n\n    // Respond with the raw CSV reader data and the following:\n    // Status = 200\n    // Content-Type = 'text/csv'\n    // Content-Disposition = 'attachment; filename=\"super-important-report.csv\"'\n    // Body = (whatever .Read() gave us)\n    respond.To(w, req).Ok(export)\n}\n\n// ---\n\ntype Export struct {\n    RawData *bytes.Buffer\n}\n\nfunc (e Export) Content() io.Reader {\n    return e.csvData\n}\n\nfunc (e Export) ContentType() string {\n    return \"text/csv\" \n}\n\nfunc (e Export) ContentFileName() string {\n    return \"super-important-report.csv\"\n}\n```\n\n### Responding With HTML\n\nWhile most of `respond` was built to support building REST APIs,\nyou can just as easily respond w/ HTML if your application does\nserver-side rendering. Using the `HTML()` and `HTMLTemplate()`\nfunctions, you can easily send 200 responses w/ the `Content-Type`\nset to `text/html; charset=utf-8`.\n\nYou can respond with a pre-rendered block of HTML.\n\n```go\nfunc LoginHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    // ... do some work ...\n    \n    html := \"\u003ch1\u003eHello \" + username + \"\u003c/h1\u003e\"\n    response.HTML(html)\n}\n```\n\nOr you can use a standard Go `html/template`. The `Responder` will\nevaluate the template for you, and the resulting HTML will be\nstreamed to the HTTP response.\n\n```go\nvar loginTemplate := template.Must(template.Parse(`\n    \u003ch1\u003eHello {{ .Username }}\u003c/h1\u003e\n`))\n\nfunc LoginHandler(w http.ResponseWriter, req *http.Request) {\n    response := respond.To(w, req)\n\n    // ... do some work ...\n    \n    response.HTMLTemplate(loginTemplate, LoginContext{\n        Username: username,\n    })\n}\n```\n\n### FAQs\n\n#### Why Not Just Use Gin/Chi/Echo/Fiber/Buffalo/etc?\n\nIf you want to use one of these to help you deal with writing\nHTTP responses, you also need to buy into their router, their\nrequest data binding, their middleware, their context objects,\nand so forth.\n\nThe `respond` package lets you stick to the standard library for\nall of your HTTP needs. In doing so, you can still bring your own\nrouter/mux, middleware, binding, etc. `respond` is solely focused\non taking the \"stank\" out of marshaling data and writing response\nheaders/data.\n\n#### Do You Support Formats Other Than JSON\n\nNope. I've toyed with adding content negotiation so that if the\ncaller is asking for XML they can get it. Realistically, if you\njumped on the Go bandwagon to build HTTP service gateways you're\nlikely using JSON anyway, so this one is fairly low priority.\n\n#### Do You Support Other Template Engines for `HTMLTemplate()`?\n\nNot directly. If you plan to call `HTMLTemplate()` then you have\nto use standard Go templates - which is sufficient for most use\ncases.\n\nIf you do use another template engine like [Plush](https://github.com/gobuffalo/plush),\nyou can just invoke its evaluation function yourself and pass\nthe resulting HTML string to `HTML()`. It's not the most memory\nefficient, but it's probably more than sufficient for most workloads.\n\n\n```go\nresponder := respond.To(w, req)\n\nctx := plush.NewContext()\nctx.Set(\"username\", \"BobLoblaw\")\n\nhtml, err := plush.Render(loginTemplate, ctx)\nresponder.HTML(html, err)\n```\n\nYou can even tighten this up more since Go will pass along the\nmultiple return values properly:\n\n```go\nresponder := respond.To(w, req)\n\nctx := plush.NewContext()\nctx.Set(\"username\", \"BobLoblaw\")\n\nresponder.HTML(plush.Render(loginTemplate, ctx))\n```\n\n#### There's No Function For The HTTP Status Code I Want To Send Back\n\nTo keep the library as lean and mean as possible, `respond` only\nhas helpers for the most commonly used response status codes. If\nyou absolutely must send an \"I'm a Teapot\" response, use the\n`Reply()` function.\n\n```go\nresponder := respond.To(w, req)\n...\nresponder.Reply(http.StatusTeapot, \"I'm a teapot\")\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonadicstack%2Frespond","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonadicstack%2Frespond","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonadicstack%2Frespond/lists"}