{"id":24083279,"url":"https://github.com/mitranim/rout","last_synced_at":"2026-06-11T08:31:51.756Z","repository":{"id":57567839,"uuid":"341799816","full_name":"mitranim/rout","owner":"mitranim","description":"Imperative router for Go HTTP servers. Procedural control flow with declarative syntax. Doesn't need middleware.","archived":false,"fork":false,"pushed_at":"2023-09-05T14:07:59.000Z","size":160,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-20T15:57:03.386Z","etag":null,"topics":["go","golang","router","routing"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/mitranim/rout","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mitranim.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-24T06:18:42.000Z","updated_at":"2023-08-01T09:08:56.000Z","dependencies_parsed_at":"2023-02-19T07:31:16.888Z","dependency_job_id":null,"html_url":"https://github.com/mitranim/rout","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/mitranim/rout","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Frout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Frout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Frout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Frout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mitranim","download_url":"https://codeload.github.com/mitranim/rout/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitranim%2Frout/sbom","scorecard":{"id":650483,"data":{"date":"2025-08-11","repo":{"name":"github.com/mitranim/rout","commit":"eb727dd21dc0478fa429a9f443dea91535daec3b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/21 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":"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":"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":"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":"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":"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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: unlicense:0","Info: FSF or OSI recognized license: The Unlicense: unlicense:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":"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-21T13:23:38.163Z","repository_id":57567839,"created_at":"2025-08-21T13:23:38.163Z","updated_at":"2025-08-21T13:23:38.163Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34190583,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"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","golang","router","routing"],"created_at":"2025-01-09T23:56:16.239Z","updated_at":"2026-06-11T08:31:51.732Z","avatar_url":"https://github.com/mitranim.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Overview\n\nExperimental router for Go HTTP servers. Imperative control flow with declarative syntax. Doesn't need middleware.\n\nVery simple, small, dependency-free, reasonably fast.\n\nRecommended in conjunction with [`github.com/mitranim/goh`](https://github.com/mitranim/goh), which implements various \"response\" types that satisfy `http.Handler`.\n\nAPI docs: https://pkg.go.dev/github.com/mitranim/rout.\n\nPerformance: a moderately-sized routing table of a production app can take a few microseconds, with very minimal allocations.\n\nExamples: see below.\n\n## TOC\n\n* [Why](#why)\n* [Usage](#usage)\n* [Caveats](#caveats)\n\n## Why\n\n* You want a router because \"manual\" routing requires too much code.\n* Most routing libraries are fatally flawed. They sacrifice imperative control flow, then invent \"middleware\" to work around the resulting problems. Imperative flow is precious. Treasure it. Don't let it go.\n\n`rout` is an evolution of \"manual\" routing that avoids common router flaws:\n\n* Control flow is still imperative. It _doesn't need middleware_: simply call A before/after B.\n* No \"mounting\". Routing always uses full URL paths: `/a/b/c` instead of `\"/a\" \"/b\" \"/c\"`. This makes the code _searchable_.\n* Correct \"not found\" and \"method not allowed\" semantics out of the box.\n* Supports multiple ways of pattern matching: exact, prefix, OAS-style pattern, and regexp. Patterns are compiled once and cached.\n\nThe resulting code is very dense, simple, and clear.\n\n## Usage\n\n```golang\nimport (\n  \"net/http\"\n\n  \"github.com/mitranim/goh\"\n  \"github.com/mitranim/rout\"\n)\n\ntype (\n  Rew = http.ResponseWriter\n  Req = *http.Request\n  Han = http.Handler\n)\n\n// Top-level handler, oversimplified. See docs on `Rou`.\nvar handler http.Handler = rout.RouFunc(routes)\n\n/*\nThis is executed for every request.\n\nUnknown paths cause the router to return error 404. Unknown methods on known\npaths cause the router to return error 405. The error is handled by YOUR code,\nwhich is an important advantage; see the handlers above.\n*/\nfunc routes(rou rout.Rou) {\n  rou.Pat(`/`).Get().Han(pageIndex)\n  rou.Pat(`/articles`).Get().Han(pageArticles)\n  rou.Pat(`/articles/{}`).Get().ParamHan(pageArticle)\n  rou.Sta(`/api`).Sub(routesApi)\n  rou.Get().Handler(fileServer)\n}\n\nvar fileServer = http.FileServer(http.Dir(`public`))\n\n// This is executed for every request that gets routed to it.\nfunc routesApi(rou rout.Rou) {\n  /**\n  Enable CORS only for this route. This would usually involve middleware.\n  With `rout`, you just call A before B.\n  */\n  allowCors(rou.Rew.Header())\n\n  rou.Sta(`/api/articles`).Sub(routesApiArticles)\n}\n\n// This is executed for every request that gets routed to it.\nfunc routesApiArticles(rou rout.Rou) {\n  rou.Pat(`/api/articles`).Methods(func(rou rout.Rou) {\n    rou.Get().Han(apiArticleFeed)\n    rou.Post().Han(apiArticleCreate)\n  })\n  rou.Pat(`/api/articles/{}`).Methods(func(rou rout.Rou) {\n    rou.Get().ParamHan(apiArticleGet)\n    rou.Patch().ParamHan(apiArticleUpdate)\n    rou.Delete().ParamHan(apiArticleDelete)\n  })\n}\n\n// Oversimplified for example's sake.\nfunc allowCors(head http.Header)                  {}\nfunc pageIndex(req Req) Han                       { return goh.StringOk(`ok`) }\nfunc pageArticles(req Req) Han                    { return goh.StringOk(`ok`) }\nfunc pageArticle(req Req, args []string) Han      { return goh.StringOk(`ok`) }\nfunc apiArticleFeed(req Req) Han                  { return goh.StringOk(`ok`) }\nfunc apiArticleCreate(req Req) Han                { return goh.StringOk(`ok`) }\nfunc apiArticleGet(req Req, args []string) Han    { return goh.StringOk(`ok`) }\nfunc apiArticleUpdate(req Req, args []string) Han { return goh.StringOk(`ok`) }\nfunc apiArticleDelete(req Req, args []string) Han { return goh.StringOk(`ok`) }\n```\n\n## Changelog\n\n### v0.8.0\n\n`ErrStatus` no longer falls back on status 500. Callers of `ErrStatus` must check if the status is 0 and implement their own fallback. Use the newly added `ErrStatusFallback` for the old behavior.\n\n### v0.7.1\n\nRenamed `Pat.Append` to `Pat.AppendTo` for consistency with other libraries.\n\n### v0.7.0\n\nAdded `Rou.Mut` for introspection. It stores the matched `Endpoint` after a successful match. Minor breaking change: `Rou.Done` is removed, as the boolean is now part of `Mut`. There is no measurable performance regression.\n\n### v0.6.3\n\nFix panic in `ErrStatus` when unwrapping non-comparable error values.\n\n### v0.6.2\n\nOn successful match, `Rou` no longer uses panics to break the flow. Instead it continues execution, but flips a hidden flag that causes it to ignore all further routes. This avoids some weird gotchas related to nil panics previously used by this library.\n\nPerformance: this forces a single tiny allocation (`new(bool)`), but appears to marginally improve routing performance.\n\n### v0.6.1\n\nBugfix for parametrized pattern matching in method-only routes.\n\n### v0.6.0\n\n* Support OAS-style patterns such as `/one/{}/two`.\n  * Add `Pat`.\n  * Add `Rou.Pat`.\n* Add tools for introspection via \"dry runs\":\n  * `Visit`\n  * `Visitor`\n  * `RegexpVisitor`\n  * `PatternVisitor`\n  * `Ident`\n  * `IdentType`\n  * `NopRew`\n* Various breaking renamings for brevity:\n  * `Router` → `Rou`.\n  * `Exact` → `Exa`.\n  * `Begin` → `Sta`.\n  * `Regex` → `Reg`.\n* Export lower-level pattern-matching tools via `Match`.\n\n### v0.5.0\n\nLexicon change: \"Res\" → \"Han\" for anything that involves `http.Handler`.\n\nAdd support for `*http.Response` via `Respond`, `Router.Res`, `Router.ParamRes`. Expressing responses with `http.Handler` remains the preferred and recommended approach.\n\n### v0.4.4\n\nOptimize error creation: hundreds of nanoseconds → tens of nanoseconds.\n\n### v0.4.3\n\nExported `ErrStatus`.\n\n### v0.4.2\n\n`WriteErr` and `Router.Serve` now perform deep error unwrapping to obtain the HTTP status code of an error.\n\n### v0.4.1\n\n`Router.Res` and `Router.ParamRes` are now variadic, accepting multiple funcs. They try the funs sequentially until one of the funcs returns a non-nil handler. Also added `Coalesce` which provides similar behavior without a router.\n\n### v0.4.0\n\nSupport multiple ways of URL matching:\n\n  * `Router.Regex` → by regexp (supports capture groups)\n  * `Router.Exact` → by exact match (no capture groups)\n  * `Router.Begin` → by prefix (no capture groups)\n\nBreaking: renamed `Router.Reg` → `Router.Regex` for symmetry with the other path pattern methods.\n\nMost routes can be expressed with exact or prefix matches. Regexps are needed\nonly for capturing args. This makes routing simpler, clearer, and less error-prone. It also significantly improves performance, compared to using regexps for everything.\n\n### v0.3.0\n\nAdded simple shortcuts:\n\n  * `WriteErr`\n  * `Router.Route`\n  * `Router.Serve`\n\nBreaking: `Route` has been replaced with `Router.Route`.\n\n### v0.2.1\n\n`Res` now implements `http.Handler`. This is not used internally, but could be handy for users.\n\n### v0.2.0\n\nAPI redesign: fewer types, simpler, more flexible.\n\n### v0.1.1\n\nMethod matching is now case-insensitive.\n\n### v0.1.0\n\nFirst tagged release.\n\n## License\n\nhttps://unlicense.org\n\n## Misc\n\nI'm receptive to suggestions. If this library _almost_ satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Frout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmitranim%2Frout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitranim%2Frout/lists"}