{"id":13804314,"url":"https://github.com/trikko/serverino","last_synced_at":"2026-02-14T18:06:57.062Z","repository":{"id":65557533,"uuid":"486284504","full_name":"trikko/serverino","owner":"trikko","description":"Small and ready-to-go http server. Support for websockets included.","archived":false,"fork":false,"pushed_at":"2026-02-13T21:10:56.000Z","size":4562,"stargazers_count":74,"open_issues_count":1,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-02-14T03:39:26.897Z","etag":null,"topics":["d","dlang","http","http-server"],"latest_commit_sha":null,"homepage":"https://trikko.github.io/serverino/","language":"D","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/trikko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["trikko"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-04-27T17:13:38.000Z","updated_at":"2026-02-13T21:11:00.000Z","dependencies_parsed_at":"2023-10-20T16:47:51.375Z","dependency_job_id":"68ed81bc-fefb-4c52-91d0-3aeb2b223886","html_url":"https://github.com/trikko/serverino","commit_stats":null,"previous_names":[],"tags_count":47,"template":false,"template_full_name":null,"purl":"pkg:github/trikko/serverino","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trikko%2Fserverino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trikko%2Fserverino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trikko%2Fserverino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trikko%2Fserverino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trikko","download_url":"https://codeload.github.com/trikko/serverino/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trikko%2Fserverino/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29452122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T15:52:44.973Z","status":"ssl_error","status_checked_at":"2026-02-14T15:52:11.208Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["d","dlang","http","http-server"],"created_at":"2024-08-04T01:00:45.608Z","updated_at":"2026-02-14T18:06:57.050Z","avatar_url":"https://github.com/trikko.png","language":"D","readme":"serverino\n\u003cimg align=\"left\" alt=\"Logo\" width=\"100\" src=\"https://github.com/trikko/serverino/assets/647157/a6f462fa-8b76-43c3-9855-0671e704aa6c\" height=\"96\"\u003e\n=======\n[![BUILD \u0026 TEST](https://github.com/trikko/serverino/actions/workflows/d.yml/badge.svg)](https://github.com/trikko/serverino/actions/workflows/d.yml) [![LICENSE](https://img.shields.io/badge/LICENSE-MIT-blue)](https://github.com/trikko/serverino/tree/master/LICENSE) [![Donate](https://img.shields.io/badge/paypal-buy_me_a_beer-FFEF00?logo=paypal\u0026logoColor=white)](https://paypal.me/andreafontana/5)\n###### [quickstart](#quickstart) – [minimal example](#a-simple-webserver-in-just-three-lines) – [wiki](https://github.com/trikko/serverino/wiki/) - [more examples](https://github.com/trikko/serverino/tree/master/examples/) - [docs]( #documentation-you-need) – [shielding serverino using proxy](#shielding-the-whole-thing)\n---\n* 🚀 **Quick build \u0026 start**: *build \u0026 run your server in seconds.*\n* 🙌 **Zero dependencies**: *serverino doesn't rely on any external library.*\n* 💪 **High performance**: *capable of managing tens of thousands of connections per second.*\n* 🌐 **Cross-platform**: *every release is tested on Linux, Windows, and MacOS.*\n* 🔄 **Hot-reload**: *restart workers without downtime or dropped connections. [(how?)](#restarting-workers-without-downtime)*\n\n\n## Quickstart\nInstall a [dlang compiler](https://dlang.org/), then:\n```sh\ndub init your_app_name -t serverino\ncd your_app_name\ndub run\n```\n\n## A simple webserver in just three lines\n```d\nimport serverino;\nmixin ServerinoMain;\nvoid simple(Request request, Output output) { output ~= request.dump(); }\n```\n\n## Documentation you need\n* [Serverino docs](https://trikko.github.io/serverino/) - Serverino reference, generated from code\n* [Examples](https://github.com/trikko/serverino/tree/master/examples) - Some ready-to-try examples\n* [Tips](https://github.com/trikko/serverino/wiki/) - Some snippets you want to read\n\n## Defining more than one endpoint\n\u003e [!IMPORTANT]\n\u003e All the functions marked with ```@endpoint``` will be called one by one, in order of ```@priority``` (higher to lower), until one of them modifies the output.\n\n```d\nmodule app;\n\nimport std;\nimport serverino;\n\nmixin ServerinoMain;\n\n// This endpoint handles the root of the server. Try: http://localhost:8080/\n@endpoint @route!\"/\"\nvoid homepage(Request request, Output output)\n{\n\toutput ~= `\u003chtml\u003e\u003cbody\u003e`;\n\toutput ~= `\u003ca href=\"/private/profile\"\u003ePrivate page\u003c/a\u003e\u003cbr\u003e`;\n\toutput ~= `\u003ca href=\"/private/asdasd\"\u003ePrivate (404) page\u003c/a\u003e`;\n\toutput ~= `\u003c/body\u003e\u003c/html\u003e`;\n}\n\n// This endpoint shows a private page: it's protected by the auth endpoint below.\n@endpoint @route!\"/private/profile\"\nvoid user(Request request, Output output)\n{\n\toutput ~= \"Hello user!\";\n}\n\n// This endpoint shows a private page: it's protected by the auth endpoint below.\n@endpoint\nvoid blah(Request request, Output output)\n{\n\t// Same as marking this endpoint with @route!\"/private/dump\"\n\tif (request.path != \"/private/dump\")\n\t\treturn;\n\n\toutput ~= request.dump();\n}\n\n// This endpoint simply checks if the user and password are correct for all the private pages.\n// Since it has a higher priority than the previous endpoints, it will be called first.\n@priority(10)\n@endpoint @route!(r =\u003e r.path.startsWith(\"/private/\"))\nvoid auth(Request request, Output output)\n{\n\tif (request.user != \"user\" || request.password != \"password\")\n\t{\n\t\t// If the user and password are not correct, we return a 401 status code and a www-authenticate header.\n\t\toutput.status = 401;\n\t\toutput.addHeader(\"www-authenticate\",`Basic realm=\"my serverino\"`);\n\t}\n\n\t// If the user and password are correct, we call the next matching endpoint.\n\t// (the next matching endpoint will be called only if the current endpoint doesn't write anything)\n}\n\n// This endpoint has the highest priority between the endpoints and it logs all the requests.\n@priority(12) @endpoint\nvoid requestLog(Request request)\n{\n\t// There's no http output, so the next endpoint will be called.\n\tinfo(\"Request: \", request.path);\n}\n```\n\u003e [!NOTE]\n\u003e Using `Fallthrough.Yes` as a return value for a endpoint allows you to avoid stopping the flow even if the output is touched.\n\n## Managing request-scoped state\n\nGlobal variables marked with `@requestScope` are automatically reset at the beginning and end of each request, preventing data leaks between separate HTTP requests.\n\n```d\nimport serverino;\n\nmixin ServerinoMain;\n\n// Global variable - reset for every request\n@requestScope UserData currentUser;\n\n// Global variable - persists across requests (until the worker is stopped)\nUserCache cache;\n\n@endpoint\nvoid handler(Request request, Output output)\n{\n\tcurrentUser.id = request.get(\"user_id\").to!int;  // Reset every request\n\tcache.store(currentUser.id);  // Persists across requests\n\n\toutput ~= \"User \" ~ currentUser.id.to!text ~ \" loaded\";\n}\n```\n\n**How it works:**\n- Global variables marked with `@requestScope` are automatically destroyed and re-initialized at the beginning and end of each HTTP request\n- This ensures that state from one request doesn't leak into the next request\n- Useful for request-specific global state like the current user, request buffers, or temporary session data\n\n## Websockets\n\n```d\n// Accept a new connection only if the request path is \"/echo\"\n// Every websocket will start a new indipendent process\n@onWebSocketUpgrade bool onUpgrade(Request req) {\n\treturn req.path == \"/echo\";\n}\n\n// Handle the WebSocket connection\n// Just like a normal endpoint, but with a WebSocket parameter\n@endpoint void echo(Request r, WebSocket ws) {\n\n\t// Keep the websocket running\n\twhile (true) {\n\n\t\t// Try to receive a message from the client\n\t\tif (WebSocketMessage msg = ws.receiveMessage())\n\t\t{\n\t\t\t// If we received a message, send it back to the client\n\t\t\tws.send(\"I received your message: `\" ~ msg.asString ~ \"`\");\n\t\t}\n\t}\n\n\t// When the function returns, the WebSocket connection is closed\n}\n```\n\n## Restarting workers without downtime\n\u003e [!CAUTION]\n\u003e Hot-reloading only affects the worker processes, not the main daemon process. If you modify code in the main daemon process, those changes will not be applied until you fully restart the server. Only changes to endpoint handlers and worker-specific code will be picked up by the hot-reload mechanism.\n\nServerino workers can be restarted on demand without causing downtime.\n * On POSIX systems: send `SIGUSR1` signal to the main process with `kill -10 \u003cdaemon_pid\u003e` or `kill -SIGUSR1 \u003cdaemon_pid\u003e`\n * On Windows: delete the canary file in the temp folder named `serverino-pid-\u003csha256 of pid\u003e.canary`\n\nThis allows you to recompile your workers and perform hot-reloading of your application code without any service interruption or dropped connections.\n\n\n## Shielding the whole thing\n\u003e [!CAUTION]\n\u003e  I recommend securing *serverino* behind a full web server. Below, I provide three examples of how to run *serverino* with *caddy*, *nginx* and *apache*.\n\n### Using caddy\nCaddy is probably the fastest and easiest solution. It automatically obtains and renews SSL certificates for your domains.\n\nHere's a minimal working `Caddyfile` (with https!):\n```\nyourhost.example.com {   \n   reverse_proxy localhost:8080 \n}\n```\n\nProbably you want to forward also some headers (in this example we create a local proxy on port 8081):\n```\n:8081 {   \n   reverse_proxy localhost:8080 {\n      header_up Host {host}\n      header_up X-Real-IP {remote_host}\n   }\n}\n```\n\nYou can easily balance the load between multiple serverino instances, Caddy automatically handles round-robin load balancing when you specify multiple backends:\n```\nyourhost.example.com {   \n   reverse_proxy localhost:8080 backend1.example.com:8080 backend2.example.com:8080 {\n      header_up Host {host}\n      header_up X-Real-IP {remote_host}\n   }\n}\n```\n\n\n### Using nginx\nIt's pretty easy. Just add these lines inside your nginx configuration:\n\n```\nserver {\n   listen 80 default_server;\n   listen [::]:80 default_server;\n\n   location /your_path/ {\n      proxy_set_header Host $host;\n      proxy_set_header X-Real-IP $remote_addr;\n\n      proxy_pass http://localhost:8080;\n   }\n   ...\n   ...\n}\n```\n\nIf you want to enable keepalive (between nginx and serverino) you must use an upstream:\n\n```\nupstream your_upstream_name {\n  server localhost:8080;\n  keepalive 64;\n}\n\n\nserver {\n   listen 80 default_server;\n   listen [::]:80 default_server;\n\n   location /your_path/ {\n      proxy_set_header Connection \"\";\n      proxy_http_version 1.1;\n      proxy_set_header Host $host;\n      proxy_set_header X-Real-IP $remote_addr;\n\n      proxy_pass http://your_upstream_name;\n    }\n\n    ...\n    ...\n }\n```\n### Using apache2\nEnable proxy module for apache2:\n```\nsudo a2enmod proxy\nsudo a2enmod proxy_http\n```\n\nAdd a proxy in your virtualhost configuration:\n```\n\u003cVirtualHost *:80\u003e\n   ProxyPass \"/\"  \"http://localhost:8080/\"\n   ...\n\u003c/VirtualHost\u003e\n```\n","funding_links":["https://github.com/sponsors/trikko","https://paypal.me/andreafontana/5"],"categories":["Web Frameworks","Tools per Language"],"sub_categories":["Bare metal / kernel development","D"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrikko%2Fserverino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrikko%2Fserverino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrikko%2Fserverino/lists"}