{"id":19217094,"url":"https://github.com/8ctopus/nano-router","last_synced_at":"2025-07-22T05:04:17.945Z","repository":{"id":62549411,"uuid":"485288128","full_name":"8ctopus/nano-router","owner":"8ctopus","description":"An experimental PSR-7, PSR-17 router","archived":false,"fork":false,"pushed_at":"2024-11-26T04:52:32.000Z","size":193,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-14T12:19:35.757Z","etag":null,"topics":["php","psr-17","psr-7","router"],"latest_commit_sha":null,"homepage":"","language":"PHP","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/8ctopus.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-04-25T08:37:58.000Z","updated_at":"2024-11-28T06:52:32.000Z","dependencies_parsed_at":"2023-12-28T07:47:58.866Z","dependency_job_id":"68806a41-f29d-4c20-9167-94a105e7305d","html_url":"https://github.com/8ctopus/nano-router","commit_stats":{"total_commits":76,"total_committers":1,"mean_commits":76.0,"dds":0.0,"last_synced_commit":"e46392ca9551b2bfc6a1e5bc271b41129cf8725d"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/8ctopus/nano-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/8ctopus%2Fnano-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/8ctopus%2Fnano-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/8ctopus%2Fnano-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/8ctopus%2Fnano-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/8ctopus","download_url":"https://codeload.github.com/8ctopus/nano-router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/8ctopus%2Fnano-router/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266430672,"owners_count":23927166,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":["php","psr-17","psr-7","router"],"created_at":"2024-11-09T14:20:05.500Z","updated_at":"2025-07-22T05:04:17.325Z","avatar_url":"https://github.com/8ctopus.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nano router\n\n[![packagist](https://poser.pugx.org/8ctopus/nano-router/v)](https://packagist.org/packages/8ctopus/nano-router)\n[![downloads](https://poser.pugx.org/8ctopus/nano-router/downloads)](https://packagist.org/packages/8ctopus/nano-router)\n[![min php version](https://poser.pugx.org/8ctopus/nano-router/require/php)](https://packagist.org/packages/8ctopus/nano-router)\n[![license](https://poser.pugx.org/8ctopus/nano-router/license)](https://packagist.org/packages/8ctopus/nano-router)\n[![tests](https://github.com/8ctopus/nano-router/actions/workflows/tests.yml/badge.svg)](https://github.com/8ctopus/nano-router/actions/workflows/tests.yml)\n![code coverage badge](https://raw.githubusercontent.com/8ctopus/nano-router/image-data/coverage.svg)\n![lines of code](https://raw.githubusercontent.com/8ctopus/nano-router/image-data/lines.svg)\n\nAn experimental PSR-7, PSR-17 router\n\n## features\n\n- very fast (less than 2ms on simple routing)\n- uses PSR-7 and PSR-17 standards\n- no dependencies besides PSR-7/17\n\nWhile I consider it still experimental, I have been using it in production to host [legend.octopuslabs.io](https://legend.octopuslabs.io/) without any issues so far.\n\n## introduction for beginners\n\nThe purpose of a router is to match a user (client) http request to a specific function that will handle the user request and deliver a response to the client.\n\n[PSR-7](https://www.php-fig.org/psr/psr-7/) defines the request and response interfaces, while [PSR-17](https://www.php-fig.org/psr/psr-17/) defines the factories for creating them. In other words, factories are used to create the request and response objects.\n\nHere's some pseudo-code that explains the concept:\n\n```php\n$router = new Router();\n\n// add route\n$router-\u003eaddRoute(new Route(RouteType::Exact, 'GET', '/test.php', function (ServerRequestInterface $request) : ResponseInterface {\n    return new Response(200, ['Content-Type' =\u003e 'text/plain'], 'You\\'ve reached the test page');\n}));\n\n// create user http request\n$request = ServerRequestCreator::createFromGlobals($_SERVER, $_FILES, $_COOKIE, $_GET, $_POST);\n\n// resolve finds the function that handles the user request, calls it and returns its response\n$response = $router-\u003eresolve($request);\n\n// send response to client (echoes internally)\n(new SapiEmitter())\n    -\u003eemit($response);\n```\n\n## demo\n\nTo play with the demo, clone the repo, run `php -S localhost:80 demo/public/index.php -t demo/public/` and open your browser at `http://localhost`.\nAlternatively you can run the demo within a Docker container `docker-compose up \u0026`.\n\n## install\n\n- `composer require 8ctopus/nano-router`\n\n- if you don't have any preference for the PSR-7 implementation, install [HttpSoft](https://github.com/httpsoft) `composer require httpsoft/http-message httpsoft/http-emitter`\n\n- redirect all traffic (except existing files) to the router in `.htaccess` for those using Apache\n\n```apache\nRewriteEngine on\n\n# redirect all not existing files and directories to router\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^ index.php [END]\n```\n\nand for nginx (untested)\n\n```nginx\nlocation / {\n    try_files $uri $uri/ /index.php$is_args$args;\n}\n```\n\n- create `index.php`\n\n```php\nuse Oct8pus\\NanoRouter\\NanoRouter;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\n// use any PSR-7, PSR-17 implementations, here HttpSoft\nuse HttpSoft\\Emitter\\SapiEmitter;\nuse HttpSoft\\Message\\Response;\nuse HttpSoft\\Message\\ServerRequestFactory;\nuse HttpSoft\\Message\\Stream;\nuse HttpSoft\\ServerRequest\\ServerRequestCreator;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n$router = new NanoRouter(Response::class, ServerRequestFactory::class);\n\n$router\n    // add simple route\n    -\u003eaddRoute(new Route(RouteType::Exact, 'GET', '/test.php', function (ServerRequestInterface $request) : ResponseInterface {\n        $stream = new Stream();\n        $stream-\u003ewrite('test.php');\n\n        return new Response(200, [], $stream);\n    }))\n    // add starts with route\n    -\u003eaddRoute(new Route(RouteType::StartsWith, ['GET', 'POST'], '/test/', function (ServerRequestInterface $request) : ResponseInterface {\n        $stream = new Stream();\n        $stream-\u003ewrite('request target - '. $request-\u003egetRequestTarget());\n\n        return new Response(200, [], $stream);\n    }))\n    // add regex route\n    -\u003eaddRoute(new Route(RouteType::Regex, '*', '~/php(.*)/~', function (ServerRequestInterface $request) : ResponseInterface {\n        $stream = new Stream();\n        $stream-\u003ewrite('request target - '. $request-\u003egetRequestTarget());\n\n        return new Response(200, [], $stream);\n    }))\n    -\u003eaddErrorHandler(404, function (ServerRequestInterface $request) : ResponseInterface {\n        $stream = new Stream();\n        $stream-\u003ewrite('page not found - ' . $request-\u003egetRequestTarget());\n\n        return new Response(404, [], $stream);\n    })\n    -\u003eaddMiddleware('*', '~(.*)~', MiddlewareType::Post, function (ResponseInterface $response, ServerRequestInterface $request) : ResponseInterface {\n        return $response-\u003ewithHeader('X-Powered-By', '8ctopus');\n    });\n\n// create request from globals\n$request = ServerRequestCreator::createFromGlobals($_SERVER, $_FILES, $_COOKIE, $_GET, $_POST);\n\n// resolve request into a response\n$response = $router-\u003eresolve($request);\n\n// send response to client\n(new SapiEmitter())\n    -\u003eemit($response);\n```\n\n## exception handling\n\nBy default, `Throwable`, and everything inherited from it such as `Exception`, are caught by the router provided the exception occurs within the route code. That functionality can be disabled in the constructor by setting it to `false`.\n\n```php\nnew NanoRouter(Response::class, ServerRequestFactory::class, false, false);\n```\n\nThe `RouteException` class offers an elegant way to deal with http errors such as `404`, `401`, `429`, .... The router will automatically catch the exception and return the appropriate response to the client.\n\n```php\nthrow new RouteException('page not found', 404);\n\n// client sees 404 page\n```\n\n## advanced functionalities\n\nThere is more to it, it's just not in the readme yet, most of it can be experimented within the demo, such as:\n\n- pre and post middleware\n\n## run tests\n\n    composer test\n\n## clean code\n\n    composer fix(-risky)\n\n## todo ideas\n\n- add basePath\n- class wrapper for subroutes\n- should pre middleware only work on valid requests? now not valid routes are still going through the middleware probably we need both\n- add starts with middleware\n- check psr-15 middleware\n- how to easily route inside class?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F8ctopus%2Fnano-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F8ctopus%2Fnano-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F8ctopus%2Fnano-router/lists"}