{"id":14966003,"url":"https://github.com/rawleyfowler/humming-bird","last_synced_at":"2025-10-25T13:30:55.364Z","repository":{"id":65375118,"uuid":"577988718","full_name":"rawleyfowler/Humming-Bird","owner":"rawleyfowler","description":"A fun and performant web application framework for Raku","archived":false,"fork":false,"pushed_at":"2024-07-05T14:26:06.000Z","size":243,"stargazers_count":45,"open_issues_count":3,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-31T07:12:02.007Z","etag":null,"topics":["api-rest","hacktoberfest","http","http-server","raku","rest","web-framework","webapp"],"latest_commit_sha":null,"homepage":"","language":"Raku","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/rawleyfowler.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}},"created_at":"2022-12-14T01:43:56.000Z","updated_at":"2025-01-03T09:03:12.000Z","dependencies_parsed_at":"2024-03-02T18:42:16.623Z","dependency_job_id":"a15c2a3f-0b62-4a05-91e0-e20976a0230b","html_url":"https://github.com/rawleyfowler/Humming-Bird","commit_stats":{"total_commits":139,"total_committers":7,"mean_commits":"19.857142857142858","dds":0.4316546762589928,"last_synced_commit":"8cfa6a1fcc2034d1de106340e315e61786fa90af"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawleyfowler%2FHumming-Bird","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawleyfowler%2FHumming-Bird/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawleyfowler%2FHumming-Bird/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rawleyfowler%2FHumming-Bird/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rawleyfowler","download_url":"https://codeload.github.com/rawleyfowler/Humming-Bird/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238147565,"owners_count":19424284,"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":["api-rest","hacktoberfest","http","http-server","raku","rest","web-framework","webapp"],"created_at":"2024-09-24T13:35:40.729Z","updated_at":"2025-10-25T13:30:55.359Z","avatar_url":"https://github.com/rawleyfowler.png","language":"Raku","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv style=\"text-align: center;\"\u003e\n\u003ch1\u003eHumming-Bird\u003c/h1\u003e\n\n\u003cimg src=\"https://user-images.githubusercontent.com/75388349/222969311-216081eb-fe47-4f97-bc49-d52fc8750a24.svg\" /\u003e\n\n![Zef Badge](https://raku.land/zef:rawleyfowler/Humming-Bird/badges/version?)\n[![SparrowCI](https://ci.sparrowhub.io/project/gh-rawleyfowler-Humming-Bird/badge)](https://ci.sparrowhub.io)\n\u003c/div\u003e\n\nHumming-Bird is a simple, composable, and performant web-framework for Raku on MoarVM.\nHumming-Bird was inspired mainly by [Sinatra](https://sinatrarb.com), and [Express](https://expressjs.com), and tries to keep\nthings minimal, allowing the user to pull in things like templating engines, and ORM's on their own terms.\n\n## Features\nHumming-Bird has 2 simple layers, at the lowest levels we have `Humming-Bird::Glue` which is a simple \"glue-like\" layer for interfacing with\n`Humming-Bird::Backend`'s. \nThen you have the actual application logic in `Humming-Bird::Core` that handles: routing, middleware, error handling, cookies, etc.\n\n- Powerful function composition based routing and application logic\n    - Routers\n    - Groups\n    - Middleware\n    - Advice (end of stack middleware)\n    - Simple global error handling\n    - Plugin system\n\n- Simple and helpful API\n    - get, post, put, patch, delete, etc\n    - Request content will be converted to the appropriate Raku data-type if possible\n    - Static files served have their content type infered\n    - Request/Response stash's for inter-layer route talking\n\n- Swappable backends\n\n**Note**: Humming-Bird is not meant to face the internet directly. Please use a reverse proxy such as Apache, Caddy or NGiNX.\n\n## How to install\nMake sure you have [zef](https://github.com/ugexe/zef) installed.\n\n#### Install latest\n```bash\nzef -v install https://github.com/rawleyfowler/Humming-Bird.git\n```\n\n#### Install stable\n```bash\nzef install Humming-Bird\n```\n\n## Performance\n\nSee [this](https://github.com/rawleyfowler/Humming-Bird/issues/43#issuecomment-1454252501) for a more detailed performance preview\nvs. Ruby's Sinatra using `Humming-Bird::Backend::HTTPServer`.\n\n## Examples\n\n#### Simple example:\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\n\nget('/', -\u003e $request, $response {\n    $response.html('\u003ch1\u003eHello World\u003c/h1\u003e');\n});\n\nlisten(8080);\n\n# Navigate to localhost:8080!\n```\n\n#### Simple JSON CRUD example:\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\nuse JSON::Fast; # A dependency of Humming-Bird\n\nmy %users = Map.new('bob', %('name', 'bob'), 'joe', %('name', 'joe'));\n\nget('/users/:user', -\u003e $request, $response {\n    my $user = $request.param('user');\n\n    if %users{$user}:exists {\n        $response.json(to-json %users{$user});\n    } else {\n        $response.status(404).html(\"Sorry, $user does not exist.\");\n    }\n});\n\npost('/users', -\u003e $request, $response {\n    my %user = $request.content; # Different from $request.body, $request.content will do its best to decode the data to a Map.\n    if my-user-validation-logic(%user) { # Validate somehow, i'll leave that up to you.\n        %users{%user\u003cname\u003e} = %user;\n        $response.status(201); # 201 created\n    } else {\n        $response.status(400).html('Bad request');\n    }\n});\n\nlisten(8080);\n```\n\n#### Using plugins\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\n\nplugin 'Logger'; # Corresponds to the pre-built Humming-Bird::Plugin::Logger plugin.\nplugin 'Config'; # Corresponds to the pre-built Humming-Bird::Plugin::Config plugin.\n\nget('/', sub ($request, $response) {\n    my $database_url = $request.config\u003cdatabase_url\u003e;\n    $response.html(\"Here's my database url :D \" ~ $database_url);\n});\n\nlisten(8080);\n```\n\n#### Routers\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\nuse Humming-Bird::Middleware;\n\n# NOTE: Declared routes persist through multiple 'use Humming-Bird::Core' statements\n# allowing you to declare routing logic in multiple places if you want. This is true\n# regardless of whether you're using the sub or Router process for defining routes.\nmy $router = Router.new(root =\u003e '/');\n\nplugin 'Logger';\n\n$router.get(-\u003e $request, $response { # Register a GET route on the root of the router\n    $response.html('\u003ch1\u003eHello World\u003c/h1\u003e');\n});\n\n$router.get('/foo', -\u003e $request, $response { # Register a GET route on /foo\n    $response.html('\u003cspan style=\"color: blue;\"\u003eBar\u003c/span\u003e');\n});\n\nmy $other-router = Router.new(root =\u003e '/bar');\n\n$other-router.get('/baz', -\u003e $request, $response { # Register a GET route on /bar/baz\n    $response.file('hello-world.html'); # Will render hello-world.html and infer its content type\n});\n\n# No need to register routers, it's underlying routes are registered with Humming-Bird on creation.\nlisten(8080); \n```\n\n#### Middleware\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\nuse Humming-Bird::Middleware;\n\nget('/logged', -\u003e $request, $response {\n    $response.html('This request has been logged!');\n}, [ \u0026middleware-logger ]); # \u0026middleware-logger is provided by Humming-Bird::Middleware\n\n# Custom middleware\nsub block-firefox($request, $response, \u0026next) {\n    return $response.status(400) if $request.header('User-Agent').starts-with('Mozilla');\n    $response.status(200);\n}\n\nget('/no-firefox', -\u003e $request, $response {\n    $response.html('You are not using Firefox!');\n}, [ \u0026middleware-logger, \u0026block-firefox ]);\n\nlisten(8080);\n```\n\nSince Humming-Bird `3.0.4` it may be more favorable to use plugins to register global middlewares.\n\n#### Swappable Backends\n```raku\nuse v6.d;\n\nuse Humming-Bird::Core;\n\nget('/', -\u003e $request, $response {\n    $response.html('This request has been logged!');\n});\n\n# Run on a different backend, assuming: \nlisten(:backend(Humming-Bird::Backend::MyBackend));\n```\n\nMore examples can be found in the [examples](https://github.com/rawleyfowler/Humming-Bird/tree/main/examples) directory.\n\n## Swappable backends\n\nIn Humming-Bird `3.0.0` and up you are able to write your own backend, please follow the API outlined by the `Humming-Bird::Backend` role,\nand view `Humming-Bird::Backend::HTTPServer` for an example of how to implement a Humming-Bird backend.\n\n## Plugin system\n\nHumming-Bird `3.0.4` (`4.0.0` created a breaking change for plugins) and up features the Humming-Bird Plugin system, this is a straight forward way to extend Humming-Bird with desired functionality before the server\nstarts up. All you need to do is create a class that inherits from `Humming-Bird::Plugin`, for instance `Humming-Bird::Plugin::Config`, expose a single method `register` which\ntakes arguments that align with the arguments specified in `Humming-Bird::Plugin.register`, for more arguments, take a slurpy at the end of your register method.\n\nIf the return value of a `Humming-Bird::Plugin.register` is a `Hash`, Humming-Bird will assume that you\nare returning helpers from your plugin, meaning the keys and values will be bound to `Humming-Bird::HTTPAction`.\nThis allows you to use functionality easily in your request/response life-cycles.\n\nHere is an example of a plugin:\n\n```raku\nuse JSON::Fast;\nuse Humming-Bird::Plugin;\nuse Humming-Bird::Core;\n\nunit class Humming-Bird::Plugin::Config does Humming-Bird::Plugin;\n\nmethod register($server, %routes, @middleware, @advice, **@args) {\n    my $filename = @args[0] // '.humming-bird.json';\n    my %config = from-json($filename.IO.slurp // '{}');\n\n    # The key will become the name, and the value will become a method on Humming-Bird::Glue::HTTPAction,\n    # allowing you to have helper methods available in your request/response life-cycle.\n    return %(\n        config =\u003e sub (Humming-Bird::Glue::HTTPAction $action) {\n            %config;\n        }\n    );\n\n    CATCH {\n        default {\n            warn 'Failed to find or parse your \".humming-bird.json\" configuration. Ensure your file is well formed, and does exist.';\n        }\n    }\n}\n```\n\nThis plugin embeds a `.config` method on the base class for Humming-Bird's Request and Response classes, allowing your config to be accessed during the request/response lifecycle.\n\nThen to register it in a Humming-Bird application:\n\n```raku\nuse Humming-Bird::Core;\n\nplugin 'Config', 'config/humming-bird.json'; # Second arg will be pushed to he **@args array in the register method.\n\nget('/', sub ($request, $response) {\n    $response.write($request.config\u003cfoo\u003e); # Echo back the \u003cfoo\u003e field in our JSON config.\n});\n\nlisten(8080);\n```\n\n## Design\n- Humming-Bird should be easy to pickup, and simple for developers new to Raku and/or web development.\n- Humming-Bird is not designed to be exposed to the internet directly. You should hide Humming-Bird behind a reverse-proxy like NGiNX, Apache, or Caddy.\n- Simple and composable via middlewares.\n\n## Things to keep in mind\n- This project is in active development, things will break.\n- You may run into bugs.\n- This project is largely maintained by one person.\n\n## Contributing\nAll contributions are encouraged! I know the Raku community is amazing, so I hope to see\nsome people get involved :D\n\nPlease make sure you squash your branch, and name it accordingly before it gets merged!\n\n#### Testing\n\nInstall App::Prove6\n\n```bash\nzef install --force-install App::Prove6\n```\n\nEnsure that the following passes:\n\n```bash\ncd Humming-Bird\nzef install . --force-install --/test\nprove6 -v -I. t/ it/\n```\n\n## License\nHumming-Bird is available under the MIT, you can view the license in the `LICENSE` file\nat the root of the project. For more information about the MIT, please click\n[here](https://mit-license.org/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frawleyfowler%2Fhumming-bird","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frawleyfowler%2Fhumming-bird","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frawleyfowler%2Fhumming-bird/lists"}