{"id":24289302,"url":"https://github.com/rla/alternative-router","last_synced_at":"2026-03-10T05:31:36.257Z","repository":{"id":11947606,"uuid":"14517931","full_name":"rla/alternative-router","owner":"rla","description":"An alternative HTTP router/dispatcher for SWI-Prolog.","archived":false,"fork":false,"pushed_at":"2021-01-23T16:58:17.000Z","size":25,"stargazers_count":11,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-22T11:28:11.804Z","etag":null,"topics":["route-handler","router","routing-engine","swi-prolog"],"latest_commit_sha":null,"homepage":"","language":"Prolog","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/rla.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":"2013-11-19T08:00:17.000Z","updated_at":"2025-02-24T01:54:19.000Z","dependencies_parsed_at":"2022-09-12T02:20:18.317Z","dependency_job_id":null,"html_url":"https://github.com/rla/alternative-router","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/rla/alternative-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Falternative-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Falternative-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Falternative-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Falternative-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rla","download_url":"https://codeload.github.com/rla/alternative-router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rla%2Falternative-router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30326066,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T05:25:20.737Z","status":"ssl_error","status_checked_at":"2026-03-10T05:25:17.430Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["route-handler","router","routing-engine","swi-prolog"],"created_at":"2025-01-16T10:51:50.732Z","updated_at":"2026-03-10T05:31:36.238Z","avatar_url":"https://github.com/rla.png","language":"Prolog","funding_links":[],"categories":["REST Frameworks"],"sub_categories":[],"readme":"# alternative-router\n\nThis is an alternative router/dispatcher to Swi-Prolog's\n[http_dispatch](http://www.swi-prolog.org/pldoc/man?section=httpdispatch) module. The\nmain motivation for creating this module was more convenient (less verbose) implementation\nof RESTful web services.\n\n[![Build Status](https://travis-ci.org/rla/alternative-router.svg)](https://travis-ci.org/rla/alternative-router)\n\n## Example\n\n    :- use_module(library(http/thread_httpd)).\n    :- use_module(library(arouter)).\n\n    :- route_get(hello/Name, handle_hello(Name)).\n\n    handle_hello(Name):-\n        format('Content-Type: text/plain; charset=UTF-8~n~n'),\n        format('Hello ~w', [Name]).\n\n    :- http_server(route, [port(8008)]).\n\nSave it to a file, run it, and then visit \u003chttp://localhost:8008/hello/world\u003e.\n\n## Path terms\n\nNormal path terms correspond directly to URL paths with the implicit root (`/`)\nsymbol. For example, `path/to/something` corresponds to URL\n`http://example.com/path/to/something`. To match the root itself, `/` alone\nmust be used. To match an URL path with a slash in the end, an empty atom\nhas to be used at the end of the path term. For example, to match URL\n`http://example.com/path/to/something/`, a path term `path/to/something/''`\nmust be used.\n\n## Using with http_dispatch\n\nMake fallback to `http_dispatch/1` like this:\n\n    handle_request(Request):-\n    (   route(Request)\n    -\u003e  true\n    ;   http_dispatch(Request)).\n\nand use `handle_request/1` as the handler in `http_server`.\n\n## Before-handler\n\nRoutes can have intermediate goals. The following example is cheking auth information\nbefore executing the handler:\n\n    :- route_get(api/resource, auth, handle_resource).\n\n    auth(Next):-\n    (   http_session_data(user(User)),\n        memberchk(role(admin), User)\n    -\u003e  call(Next)\n    ;   current_request(Request),\n        memberchk(path(Path), Request),\n        current_output(Out),\n        http_reply(forbidden(Path), Out, [])).\n\n    handle_resource:-\n        ...\n\nThe before-handler predicate calls its first argument when the request should pass it.\nOtherwise it should produce the response itself.\n\n## Route-path matching\n\nA path will match the route when:\n\n * Path and rule are `/`.\n * Path is an atomic value and the route is same atomic value or a variable.\n * Path is `/(P1, P2)`, route is `/(R1, R2)` and R1 matches P1 and R2 matches P2.\n\n## Route priority\n\nRoutes are tested in reverse to the adding order. Overlapping routes can be added. Structurally equivalent\nroutes cannot be added. Routes are overlapping when one route has atom in position of route variable in the\nother. Routes are structurally equivalent when:\n\n * Both routes are `/`.\n * Both routes are variables.\n * Both routes are same atomic values.\n * One route is `/(A1, B1)`, the other is `/(A2, B2)` and\n   A1, A2 are structurally equivalent and B1, B2 are structurally equivalent.\n\nStructural equivalence is used for detecting duplicate rules. This plays nice with the `make/0` goal.\n\n## Non-deterministic routing\n\nIn some cases another matching (overlapping) route might have to be tried. This can be\ndone by throwing `arouter_next` from the current route handler. Example:\n\n    :- route_get(something/specific, handle_specific).\n\n    handle_specific:-\n        ...\n\n    :- route_get(something/Generic, handle_generic(Generic)).\n\n    handle_generic(Generic):-\n        (   Generic = specific\n        -\u003e  throw(arouter_next)\n        ;   ...).\n\nThe handler `handle_specific` will handle the request in this case\nafter throwing `arouter_next` from the `handle_generic` handler (handlers\nare tried in reverse order of adding them).\n\n## List of predicates\n\n### Adding new routes\n\n`route_get(+Route, :Goal)` registers a new GET handler.\n\n`route_put(+Route, :Goal)` registers a new PUT handler.\n\n`route_del(+Route, :Goal)` registers a new DELETE handler.\n\n`route_post(+Route, :Goal)` registers a new POST handler.\n\n`route_get(+Route, :Before, :Goal)` registers a new GET handler with a before action.\n\n`route_put(+Route, :Before, :Goal)` registers a new PUT handler with a before action.\n\n`route_del(+Route, :Before, :Goal)` registers a new DELETE handler with a before action.\n\n`route_post(+Route, :Before, :Goal)` registers a new POST handler with a before action.\n\n`new_route(+Method, +Route, :Goal)` registers a new custom method handler.\n\n`new_route(+Method, +Route, :Before, :Goal)` registers a new custom method handler with a before action.\n\nAll predicates above will throw an error when the `Route` does not contain the\nsuitable term.\n\nRoute handler predicates can take variables from the route. Example:\n\n    :- http_get(post/show/Slug, post_show(Slug)).\n\n    post_show(Slug):-\n        ...\n\nHowever, they do not take the `Request` argument, unlike the `http_dispatch` handlers. To obtain the\ncurrent request, use the [http_current_request/1](http://www.swi-prolog.org/pldoc/doc_for?object=http_current_request/1)\npredicate.\n\n### Dispatching\n\n`route(+Request)` - takes given request and attempts to find suitable handler.\n\nRequest must contain terms `method(Method)` and `path(Path)`. Throws `handler_failed(Method, Path)` when\nhandler was found but it failed during execution.\n\n### Inspecting routes\n\nUse the `route(?Method, ?Route, ?Before, ?Goal)` predicate.\n\n### Removing routes\n\nUse the `route_remove(Method, Route)` predicate. Both arguments\ncan be unbound or partially instantiated.\n\n## Installation\n\nTo install as a package:\n\n    pack_install(arouter).\n\nTested with Swi-Prolog 7.x but should work with earlier versions too.\n\n## Full API documentation\n\nSee \u003chttp://packs.rlaanemets.com/alternative-router/doc/\u003e.\n\n## Running tests\n\nIn the package root, insert into swipl:\n\n    [tests/tests].\n    run_tests.\n\nOr if you cloned the repo:\n\n    make test\n\n## Debugging\n\nEnable debugging with:\n\n    ?- use_module(library(debug)).\n    ?- debug(arouter).\n\n## Changelog\n\n * 2021-01-23 version 2.0.0. HEAD method routes to GET handlers.\n * 2015-11-01 version 1.1.1. Attempt to preserve route order on `make`.\n * 2015-11-01 version 1.1.0. Non-deterministic routing.\n * 2014-05-08 version 1.0.0. Precise route matching semantics.\n * 2014-02-01 version 0.0.1\n\n## Bug reports/feature requests\n\nPlease send bug reports/feature request through the GitHub\nproject [page](https://github.com/rla/alternative-router).\n\n## License\n\nThe MIT license. See the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frla%2Falternative-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frla%2Falternative-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frla%2Falternative-router/lists"}