{"id":13878667,"url":"https://github.com/hanami/router","last_synced_at":"2025-07-16T14:32:45.436Z","repository":{"id":8958144,"uuid":"10696882","full_name":"hanami/router","owner":"hanami","description":"Ruby/Rack HTTP router","archived":false,"fork":false,"pushed_at":"2024-02-27T12:50:10.000Z","size":1038,"stargazers_count":359,"open_issues_count":6,"forks_count":89,"subscribers_count":30,"default_branch":"main","last_synced_at":"2024-04-14T05:31:24.243Z","etag":null,"topics":["hanami","http","httprouter","rest-api","router","ruby"],"latest_commit_sha":null,"homepage":"http://hanamirb.org","language":"Ruby","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/hanami.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"hanami"}},"created_at":"2013-06-14T20:07:26.000Z","updated_at":"2024-04-01T00:00:34.000Z","dependencies_parsed_at":"2023-01-13T15:05:27.523Z","dependency_job_id":"f2732838-ec98-4872-9e2d-d5779f2b38c3","html_url":"https://github.com/hanami/router","commit_stats":{"total_commits":487,"total_committers":61,"mean_commits":7.983606557377049,"dds":0.3162217659137577,"last_synced_commit":"97f75b8529574bd4ff23165460e82a6587bc323c"},"previous_names":["lotus/router"],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanami%2Frouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanami%2Frouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanami%2Frouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanami%2Frouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hanami","download_url":"https://codeload.github.com/hanami/router/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226138849,"owners_count":17579496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["hanami","http","httprouter","rest-api","router","ruby"],"created_at":"2024-08-06T08:01:56.234Z","updated_at":"2025-07-16T14:32:45.418Z","avatar_url":"https://github.com/hanami.png","language":"Ruby","readme":"# Hanami::Router\n\nRack compatible, lightweight, and fast HTTP Router for Ruby and [Hanami](http://hanamirb.org).\n\n## Status\n\n[![Gem Version](https://badge.fury.io/rb/hanami-router.svg)](https://badge.fury.io/rb/hanami-router)\n[![CI](https://github.com/hanami/router/actions/workflows/ci.yml/badge.svg)](https://github.com/hanami/router/actions?query=workflow%3Aci+branch%3Amain)\n[![Test Coverage](https://codecov.io/gh/hanami/router/branch/main/graph/badge.svg)](https://codecov.io/gh/hanami/router)\n[![Depfu](https://badges.depfu.com/badges/5f6b8e8fa3b0d082539f0b0f84d55960/overview.svg)](https://depfu.com/github/hanami/router?project=Bundler)\n\n## Contact\n\n* Home page: http://hanamirb.org\n* Mailing List: http://hanamirb.org/mailing-list\n* API Doc: http://rubydoc.info/gems/hanami-router\n* Bugs/Issues: https://github.com/hanami/router/issues\n* Chat: http://chat.hanamirb.org\n\n\n## Installation\n\n__Hanami::Router__ supports Ruby (MRI) 3.1.+\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem \"hanami-router\"\n```\n\nAnd then execute:\n\n```shell\n$ bundle\n```\n\nOr install it yourself as:\n\n```shell\n$ gem install hanami-router\n```\n\n## Getting Started\n\nCreate a file named `config.ru`\n\n```ruby\n# frozen_string_literal: true\nrequire \"hanami/router\"\n\napp = Hanami::Router.new do\n  get \"/\", to: -\u003e(env) { [200, {}, [\"Welcome to Hanami!\"]] }\nend\n\nrun app\n```\n\nFrom the shell:\n\n```shell\n$ bundle exec rackup\n```\n\n## Usage\n\n__Hanami::Router__ is designed to work as a standalone framework or within a\ncontext of a [Hanami](http://hanamirb.org) application.\n\nFor the standalone usage, it supports neat features:\n\n### A Beautiful DSL:\n\n```ruby\nHanami::Router.new do\n  root                to: -\u003e(env) { [200, {}, [\"Hello\"]] }\n  get \"/lambda\",      to: -\u003e(env) { [200, {}, [\"World\"]] }\n  get \"/dashboard\",   to: Dashboard::Index\n  get \"/rack-app\",    to: RackApp.new\n\n  redirect \"/legacy\", to: \"/\"\n\n  mount Api::App, at: \"/api\"\n\n  scope \"admin\" do\n    get \"/users\", to: Users::Index\n  end\nend\n```\n\n### Fixed string matching:\n\n```ruby\nHanami::Router.new do\n  get \"/hanami\", to: -\u003e(env) { [200, {}, [\"Hello from Hanami!\"]] }\nend\n```\n\n### String matching with variables:\n\n```ruby\nHanami::Router.new do\n  get \"/flowers/:id\", to: -\u003e(env) { [200, {}, [\"Hello from Flower no. #{ env[\"router.params\"][:id] }!\"]] }\nend\n```\n\n### Variables Constraints:\n\n```ruby\nHanami::Router.new do\n  get \"/flowers/:id\", id: /\\d+/, to: -\u003e(env) { [200, {}, [\":id must be a number!\"]] }\nend\n```\n\n### String matching with globbing:\n\n```ruby\nHanami::Router.new do\n  get \"/*match\", to: -\u003e(env) { [200, {}, [\"This is catch all: #{ env[\"router.params\"].inspect }!\"]] }\nend\n```\n\n### String matching with optional tokens:\n\n```ruby\nHanami::Router.new do\n  get \"/hanami(.:format)\" to: -\u003e(env) { [200, {}, [\"You\"ve requested #{ env[\"router.params\"][:format] }!\"]] }\nend\n```\n\n### Support for the most common HTTP methods:\n\n```ruby\nendpoint = -\u003e(env) { [200, {}, [\"Hello from Hanami!\"]] }\n\nHanami::Router.new do\n  get     \"/hanami\", to: endpoint\n  post    \"/hanami\", to: endpoint\n  put     \"/hanami\", to: endpoint\n  patch   \"/hanami\", to: endpoint\n  delete  \"/hanami\", to: endpoint\n  trace   \"/hanami\", to: endpoint\n  options \"/hanami\", to: endpoint\nend\n```\n\n### Root:\n\n```ruby\nHanami::Router.new do\n  root to: -\u003e(env) { [200, {}, [\"Hello from Hanami!\"]] }\nend\n```\n\n### Redirect:\n\n```ruby\nHanami::Router.new do\n  get \"/redirect_destination\", to: -\u003e(env) { [200, {}, [\"Redirect destination!\"]] }\n  redirect \"/legacy\",          to: \"/redirect_destination\"\n  redirect \"/learn-more\",      to: \"https://hanamirb.org/\"\n  redirect \"/chat\",            to: URI(\"xmpp://myapp.net/\")\nend\n```\n\n### Named routes:\n\n```ruby\nrouter = Hanami::Router.new(scheme: \"https\", host: \"hanamirb.org\") do\n  get \"/hanami\", to: -\u003e(env) { [200, {}, [\"Hello from Hanami!\"]] }, as: :hanami\nend\n\nrouter.path(:hanami) # =\u003e \"/hanami\"\nrouter.url(:hanami)  # =\u003e \"https://hanamirb.org/hanami\"\n```\n\n\n### Scopes:\n\n```ruby\nrouter = Hanami::Router.new do\n  scope \"animals\" do\n    scope \"mammals\" do\n      get \"/cats\", to: -\u003e(env) { [200, {}, [\"Meow!\"]] }, as: :cats\n    end\n  end\nend\n\n# and it generates:\n\nrouter.path(:animals_mammals_cats) # =\u003e \"/animals/mammals/cats\"\n```\n\n\n\n### Mount Rack applications:\n\nMounting a Rack application will forward all kind of HTTP requests to the app,\nwhen the request path matches the `at:` path.\n\n```ruby\nHanami::Router.new do\n  mount MyRackApp.new, at: \"/foo\"\nend\n```\n\n### Duck typed endpoints:\n\nEverything that responds to `#call` is invoked as it is:\n\n```ruby\nHanami::Router.new do\n  get \"/hanami\",     to: -\u003e(env) { [200, {}, [\"Hello from Hanami!\"]] }\n  get \"/rack-app\",   to: RackApp.new\n  get \"/method\",     to: ActionControllerSubclass.action(:new)\nend\n```\n\n### Implicit Not Found (404):\n\n```ruby\nrouter = Hanami::Router.new\nrouter.call(Rack::MockRequest.env_for(\"/unknown\")).status # =\u003e 404\n```\n\n### Explicit Not Found:\n\n```ruby\nrouter = Hanami::Router.new(not_found: -\u003e(_) { [499, {}, []]})\nrouter.call(Rack::MockRequest.env_for(\"/unknown\")).status # =\u003e 499\n```\n\n### Body Parsers\n\nRack ignores request bodies unless they come from a form submission.\nIf we have a JSON endpoint, the payload isn't available in the params hash:\n\n```ruby\nRack::Request.new(env).params # =\u003e {}\n```\n\nThis feature enables body parsing for specific MIME Types.\nIt comes with a built-in JSON parser and allows to pass custom parsers.\n\n#### JSON Parsing\n\n```ruby\n# frozen_string_literal: true\n\nrequire \"hanami/router\"\nrequire \"hanami/middleware/body_parser\"\n\napp = Hanami::Router.new do\n  patch \"/books/:id\", to: -\u003e(env) { [200, {}, [env[\"router.params\"].inspect]] }\nend\n\nuse Hanami::Middleware::BodyParser, :json\nrun app\n```\n\n```shell\ncurl http://localhost:2300/books/1    \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: application/json\"       \\\n  -d '{\"published\":\"true\"}'           \\\n  -X PATCH\n\n# =\u003e [200, {}, [\"{:published=\u003e\\\"true\\\",:id=\u003e\\\"1\\\"}\"]]\n```\n\nIf the json can't be parsed an exception is raised:\n\n```ruby\nHanami::Middleware::BodyParser::BodyParsingError\n```\n\n##### `multi_json`\n\nIf you want to use a different JSON backend, include `multi_json` in your `Gemfile`.\n\n#### Custom Parsers\n\n```ruby\n# frozen_string_literal: true\n\nrequire \"hanami/router\"\nrequire \"hanami/middleware/body_parser\"\n\n# See Hanami::Middleware::BodyParser::Parser\nclass XmlParser \u003c Hanami::Middleware::BodyParser::Parser\n  def mime_types\n    [\"application/xml\", \"text/xml\"]\n  end\n\n  # Parse body and return a Hash\n  def parse(body)\n    # parse xml\n  rescue SomeXmlParsingError =\u003e e\n    raise Hanami::Middleware::BodyParser::BodyParsingError.new(e)\n  end\nend\n\napp = Hanami::Router.new do\n  patch \"/authors/:id\", to: -\u003e(env) { [200, {}, [env[\"router.params\"].inspect]] }\nend\n\nuse Hanami::Middleware::BodyParser, XmlParser\nrun app\n```\n\n```shell\ncurl http://localhost:2300/authors/1 \\\n  -H \"Content-Type: application/xml\" \\\n  -H \"Accept: application/xml\"       \\\n  -d '\u003cname\u003eLG\u003c/name\u003e'               \\\n  -X PATCH\n\n# =\u003e [200, {}, [\"{:name=\u003e\\\"LG\\\",:id=\u003e\\\"1\\\"}\"]]\n```\n\n## Testing\n\n```ruby\n# frozen_string_literal: true\n\nrequire \"hanami/router\"\n\nrouter = Hanami::Router.new do\n  get \"/books/:id\", to: \"books.show\", as: :book\nend\n\nroute = router.recognize(\"/books/23\")\nroute.verb      # \"GET\"\nroute.endpoint  # =\u003e \"books.show\"\nroute.params    # =\u003e {:id=\u003e\"23\"}\nroute.routable? # =\u003e true\n\nroute = router.recognize(:book, id: 23)\nroute.verb      # \"GET\"\nroute.endpoint  # =\u003e \"books.show\"\nroute.params    # =\u003e {:id=\u003e\"23\"}\nroute.routable? # =\u003e true\n\nroute = router.recognize(\"/books/23\", {}, method: :post)\nroute.verb      # \"POST\"\nroute.routable? # =\u003e false\n```\n\n## Versioning\n\n__Hanami::Router__ uses [Semantic Versioning 2.0.0](http://semver.org)\n\n## Contributing\n\n1. Fork this repo to your account and clone it locally (`git clone git@github.com:your-pseudo/your-cloned-repo.git`)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Install the dependencies (`bundle install`)\n4. Run tests, they should all pass (`./script/ci`)\n5. Make your changes \u0026 check that the tests still pass. Add some test cases if needed.\n6. Commit your changes (`git commit -am 'Add some feature'`)\n7. Push to the branch (`git push origin my-new-feature`)\n8. Create new Pull Request on Github with some context on what you're trying to fix or to improve with this contribution\n\nThank you for contributing! \n\n## Copyright\n\nCopyright © 2014–2025 Hanami Team – Released under MIT License\n","funding_links":["https://github.com/sponsors/hanami"],"categories":["Ruby","Middlewares"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhanami%2Frouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhanami%2Frouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhanami%2Frouter/lists"}