{"id":22283894,"url":"https://github.com/jdonaldson/golgi","last_synced_at":"2026-02-06T15:35:42.233Z","repository":{"id":138430749,"uuid":"97177323","full_name":"jdonaldson/golgi","owner":"jdonaldson","description":"A composable routing library for Haxe.","archived":false,"fork":false,"pushed_at":"2019-10-15T00:37:46.000Z","size":174,"stargazers_count":39,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-10T01:37:42.931Z","etag":null,"topics":["dispatch","parse","routing","url"],"latest_commit_sha":null,"homepage":"","language":"Haxe","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jdonaldson.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2017-07-14T00:52:35.000Z","updated_at":"2024-07-18T11:39:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"33db7a8d-784d-4221-9840-76d5e99cf04c","html_url":"https://github.com/jdonaldson/golgi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jdonaldson/golgi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdonaldson%2Fgolgi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdonaldson%2Fgolgi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdonaldson%2Fgolgi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdonaldson%2Fgolgi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jdonaldson","download_url":"https://codeload.github.com/jdonaldson/golgi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jdonaldson%2Fgolgi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29167021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T14:37:12.680Z","status":"ssl_error","status_checked_at":"2026-02-06T14:36:22.973Z","response_time":59,"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":["dispatch","parse","routing","url"],"created_at":"2024-12-03T16:42:51.906Z","updated_at":"2026-02-06T15:35:42.217Z","avatar_url":"https://github.com/jdonaldson.png","language":"Haxe","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![golgi logo](https://vectr.com/omgjjd/aabjEN2Z9.png?width=64\u0026height=64\u0026select=aabjEN2Z9page0) golgi\n[![Build Status](https://travis-ci.org/jdonaldson/golgi.svg?branch=master)](https://travis-ci.org/jdonaldson/golgi)\n\nA composable routing library for Haxe.\n\nGolgi is a macro-based generic routing library for Haxe.  It is intended to be\nused as the basis for more complex and specific routing applications (e.g. Http\nURL routing)\n\nIt follows these design guidelines:\n\n1. Routes should be simple, fast, and composable.\n2. Routing should avoid allocation and unnecessary overhead.\n3. Route handling shouldn't presuppose a specific protocol (e.g. Http).\n4. Routing should avoid boilerplate and excessive code duplication.\n5. Routing should not include the rendering of results (e.g. to Json)\n\n\nDespite these restrictions, Golgi makes very few tradeoffs for feature support.\nGolgi relies heavily on macros, which can optimize routing in many cases, and\nprovides a unique macro-based class/ADT binding that enables fluent route\nmanagement with a minimum of coding.\n\nSee a [recent\npresentation](https://docs.google.com/presentation/d/14_sodhxaSQ_wWDhmqVq_Fogjz-fGUIY6RvvXnSs_uaY/edit?usp=sharing)\nfor more info.\n\n\n# Golgi Speed\nGolgi is *fast*.  The macro-based route generation eliminates common runtime and\nreflection overhead required in other routing libraries.  Raw\nthroughput can reach 1 Million requests per second on some targets.\n\nA brief speed comparison of Golgi vs. haxe.web.Dispatch for equivalent routing\ntasks on a sample of Haxe targets.\n\n![plot](https://i.imgur.com/erHufxP.png)\n\n# Intro\n\nWe'll start with a simplified version of the Golgi API in the golgi.basic\nmodule.  The golgi Api requires a type parameter for a request type, but we can\nignore that for now by passing a dummy type. Here's a small example of a small\nroute class:\n\n```haxe\nimport golgi.Api;\ntypedef Req = Any;\n\nclass TestApi extends Api\u003cReq\u003e  {\n    public function foo() : String {\n        return 'foo';\n    }\n    public function bar() : Int {\n        return 4;\n    }\n}\n```\n\nConventional routing system functions don't have heterogeneous return types.\nThey will require that responses be written inside the handler, or they will\nrequire returning a single type across all routes. Golgi differs radically from\nthis approach by enabling heterogeneous return values across the defined routes.\nThis enables greater flexibility in providing routing behavior, while also\nmaintaining a type safe interface for a routing result.\n\n# Routers and ADT\n\nIdeally, an algebraic data type (ADT) *enum* is used to specify\nheterogeneous return values.  However, this enum must be maintained separately\nfrom the actual routing logic, increasing the chances for bugs, and adding to\nthe maintenance overhead.  Golgi's approach is to build the enum for you, based\non the routing api you specify using a special `@:build` directive:\n\n```haxe\n@:build(golgi.Build.routes(TestApi))\nenum TestApiResult {}\n\n```\n\nThe `@:build` metadata here instructs the Golgi macro method to build the full\nspecification for the `TestApiResult` enum based on the api of `TestApi`.\n\n\nIf we look at the enum constructors from `TestApiResult` we see that they include\n`Foo(res:String)` and `Bar(res:Int)`, both according to the compiler and in the\nruntime.  These enum states describe the public methods of `TestApi`, with a\nsingle parameter providing the return type and value.\n\n```haxe\n var ctors = TestApiResult.getConstructors();\n trace(ctors + \" is the value for ctors\"); // [Foo, Bar]\n\n```\n\n\nHaving a synchronized enum for our test api results is not enough though, we\nstill need to provide the logic for parsing a string into paths and parameters,\nselecting the function to invoke, and capturing the return value in the enum.\nFurthermore, we only want the ADT enum type when we *don't* know which function\nis getting called.  In all other cases, we want to be able to use the plain\n`TestApi` instance directly.\n\nGolgi provides all of this functionality by extending a separate `Golgi` class.\nThis class is fully parameterized by the types we've defined previously.\n\n```haxe\nimport golgi.Golgi;\ntypedef TestMeta = golgi.meta.MetaGolgi\u003cAny,TestApiResult\u003e;\n\nclass TestApiGolgi extends Golgi\u003cReq, TestApi, TestApiResult, TestMeta\u003e{}\n\n```\n\nThe Golgi class we defined is also under the effect of a build macro.  This\nmacro builds a specialized `route` function that:\n\n1. Separates the path arguments into function names and arguments\n2. Applies relevant route metamethods defined in the MetaGolgi.\n3. Applies the arguments on the route function.\n4. Captures the result in the route enum.\n\nThe routing class requires references to the types we've defined previously.  (In\naddition, it requires a `MetaGolgi` parameter that we will describe later.)\n\n\n```haxe\nimport golgi.Golgi;\n\nclass Main {\n    static function main() {\n        var params = {};\n        var req = {};\n\n        var api  = new TestApi();\n        var glg = new TestApiGolgi();\n\n        var res = glg.route([\"foo\"], params, req);\n    }\n}\n```\n\nHere we're running the Golgi router on the path \"foo\", using the Api defined by\n`TestApi` (other arguments will be discussed shortly).    This method manages the\nlookup of the right function on TestApi, and invokes the function there.\n\nNote : Golgi accepts its path argument as an array of simple strings.  Golgi\ndoes not split or decode strings in urls, leaving that to be handled by upstream\nlibraries.\n\n# Fully Typed Path Arguments\n\nThe next step is to do something useful with the API, such as accepting\narguments from the parsed path:\n\n```haxe\nclass TestApi extends Api\u003cAny\u003e  {\n    public function arg(x:Int){\n        return x;\n    }\n}\n```\n\nThe TestApi class now has a ``foo`` function that accepts an integer and returns\nit as its result. We can invoke it with the following call:\n\n```haxe\nclass Main {\n    static function main() {\n        glg.run( [\"foo\",\"1\"], {}, req));\n    }\n}\n```\n\nNote that the argument ``x`` inside the function body is typed as an ``Int``.\nGolgi reads the type information on the``TestApi`` method interface, and then\nmakes the appropriate conversion on the corresponding path segment in the\nruntime.  If the ``x`` argument is missing, a ``NotFound(path:String)`` error is\nthrown.  If the argument can not be converted to an ``Int``, then an\n``InvalidValue`` error is thrown.  `Float`, `Int`, and `Bool` are all\nconverted directly from strings.\n\nWe can add as many typed arguments as we want, but the argument types are\nsomewhat limited.  They can only be value types that are able to be converted\nfrom ``String``, such as ``Float``, ``Int``, and ``Bool``.  *More types are\navailable via abstract typing which is described later on*.\n\n# Route Parameter Support\n\nWe can also pass in query parameters using a special ``params`` argument.\nThis simple example adds the `x` Integer argument to the `y` argument passed as\na param:\n\n```haxe\nclass TestApi implements Api\u003cReq\u003e  {\n   public function param(x : Int, params : {y : Int}){\n      return x  + params.y;\n   }\n}\n```\n\nThe params are passed in using the second argument of the ``Golgi.run`` method:\n\n```haxe\nclass Main {\n    static function main() {\n      var param = glg.route([\"param\", \"1\"], {y : \"2\"}, {});\n    }\n}\n```\n\nThe ``params`` argument name is *reserved*.  That is, you can only use that\nargument name to specify path-derived parameters, and it must be typed as an\nanonymous object.  Also, all param fields must be simple value types, just like\nthe typed path arguments.\n\n\nNote that params are not automatically parsed from the path.  They must be\nprovided separately, or omitted.\n\n# Additional request context\nIt's common to utilize a *request* argument for route handling.\nThis is often necessary for web routing, when certain routing logic involves\nchecking headers, etc.  In Golgi this is called the `request` argument.  It can be\nof any type, so once again `request` is a reserved argument name:\n\n```haxe\ntypedef Req = { header : String };\n\nclass TestApi implements Api\u003cReq\u003e  {\n    public function foo(request : Req){\n\n    }\n}\n```\n\n```haxe\nclass Main {\n    static function main() {\n         var req = glg.route([\"request\"], {}, req);\n    }\n}\n```\n\nHere we're using another structural type for our request.  However, `request`\nand `params` tend to have specialized purposes : The `params` argument *must* be\nan anonymous object type that has simple string fields.  It is typically\nconstructed from the path content itself.  The `request` argument should refer\nto internal application data that is available in the request context.\n\n\n# Sub-Routing\n\nIt's also possible to perform sub-routing in Golgi.  This process involves using\na secondary Golgi Api to process additional path parameters, common in\nhierarchical routing scenarios:\n\n```haxe\nimport golgi.*;\nclass SubTestApi extends Api\u003cReq\u003e {\n   public function foo(x: Int){\n      return x;\n   }\n}\n\n```\n\nWhen we handle the subroute, we can use the special `subroute` argument to route\nthe leftover parts of the path on the relevant instance.\n\n\n\n```haxe\nclass TestApi implements golgi.Api\u003cReq\u003e  {\n   public function subroute(request : Req, subroute : Subroute\u003cReq\u003e) {\n      var sub_api = new SubTestApi();\n      var sub_glg = new SubTestApiGolgi(sub_api);\n      vu res = subroute.route(sub_glg);\n      switch(res){\n         case Foo(x) : return x;\n         default : throw ('Invalid $res');\n      }\n   }\n}\n```\n\nLike the params and request argument, `subroute` is a reserved argument name. It\ncontains a simple method that will accept an appropriately typed Golgi instance,\nand handle the leftover paths as a route there.\n\nRouting to the subroute doesn't require anything special from the main router.\nSimply pass in the path containing the extra parameters required by the\nsubroute.\n\n```haxe\n   var sub = glg.route([\"subroute\",\"foo\",\"1\"], {}, req);\n```\n\n\n# Golgi Type Parameters Explained\n\nWe can see that the type parameters of the Golgi Api ``TestApi\u003cReq\u003e``\nincludes the type for the request (``Req``).\n\nThe Golgi router itself accepts four parameters:\n\n```haxe\nclass TestApiGolgi extends Golgi\u003cReq, TestApi, TestApiResult, TestMeta\u003e{}\n```\nThese parameters tell Golgi the relationships between the four types required\nfor routing :\n\n1. Request parameter\n2. Api parameter (which must have its own matching Request parameter)\n3. Route parameter (which must match the Api parameter function/return values)\n4. MetaGolgi (meta) parameter, which must match the Route parameter as well\n   as the Request parameter.\n\n\n# Path Metadata\n\nSometimes paths must include characters that are not allowed as valid function\nnames.  Golgi handles this with special path metadata which can be applied to a\nroute.  Here's how one would handle an *empty* path:\n\n```haxe\nclass TestApi implements Api\u003cRequest,String\u003e  {\n    @:default\n    public function foo(){\n        return 'foo';\n    }\n}\n```\n\nIn this case, the `foo` route is activated for an empty path.  Here's the\nfull list of path metadata:\n\n1. `@:default` : This route is triggered *only* for an empty path.\n2. `@:alias('additional_path', 'additional_path2')` : The following paths will trigger\n   the given route inclusive of the function name.\n3. `@:route('route_path1', 'route_path2')` : The following list of paths trigger\n   the route exclusive of the function name.\n4. `@:helper`: This function is not treated as a path (useful for public helper\n   functions).\n\nAny additional route paths given in `@:alias` or `@:route` should be given as\nanonymous strings.  Only one type of path metadata is allowed per route, so if\nyou're combining a lot of cases together, use the more general `@:route`\nspecification.\n\n# MetaGolgi\n\nIt's common for certain routes to share common handling patterns.  E.g., some\nroutes require authentication, others are only applicable for certain Http\nmethods. It's painful to have to manage these pattern manually on a per-route\nbasis.  Golgi addresses this with a powerful metadata-driven middleware system.\n\nThe MetaGolgi instance expects a signature of `TReq-\u003e(TReq-\u003eTRet)-\u003eTRet`.  This\nsignature provides the request parameter, and a function that calls the next\nmiddleware method.  Eventually, either a middlware function returns a `TRet`\ntype, or the route function itself is called.  This enables middleware methods\nto intercept specific route traffic, and perform certain modifications\n(modifying headers, or pre-emptively returning a given response).\n\nIn order to use a MetaGolgi, it's necessary to extend a base `MetaGolgi`\ninstance.  This special class will ensure that every public instance method has\nthe required signatures for its methods.\n\n\n```haxe\nclass MetaTestApi extends golgi.meta.MetaGolgi\u003cRequest\u003e {\n   public function bar(req : Request, next : Request-\u003eString) : String {\n      return next(req) + \"!\";\n   }\n}\n```\n\nWhen you have an appropriate class declared, you may use it in your Api\ndeclarations.  Just use it as simple metadata, *with no colon*:\n\n```haxe\nclass TestApi extends Api\u003cRequest,String,MetaTestApi\u003e {\n    @bar\n    public function foo(request : Request){\n        return 'foo';\n    }\n}\n```\n\nThe presence of the `@bar` metadata tells Golgi to apply the corresponding\nmiddleware to this route.  Note that a `request` argument must be accepted by\nthe function for the metagolgi method to work.\n\nAny unknown simple metadata that is not handled by the MetaGolgi instance will\nthrow a compile error, ensuring that your middleware behavior is completely\nunderstood by the compiler.\n\nYou may also apply metadata at a class level, which will apply the metadata to\nall routes defined by the API:\n\n```haxe\n@bar\nclass TestApi extends Api\u003cRequest\u003e {\n    public function foo(x:Int, request : Request, subroute : Subroute\u003cRequest\u003e){\n        subroute.run(new SubTestApi());\n        return 'foo';\n    }\n}\n```\nFinally, the base MetaGolgi instance comes with a pass through middleware called\n`_golgi_pass`.  You can use this metadata to pass runtime information without\ntriggering a middleware function.\n\nUsing MetaGolgi for middleware lets you flexibly define complex shared\nbehaviors, while still adhering to the input and output type parameters defined\nby your API.\n\n# Additional features\n\n## Abstract type route arguments\nIt's possible for routes to accept *abstract* types(!) The abstract type must unify\nwith one of the four basic value types.  This opens up a lot of\npossibilities for automated instantiation and reduction of boilerplate:\n\n\n```haxe\nclass TestApi implements golgi.BasicApi\u003cReq\u003e  {\n    public function foo(x:Bar) : String{\n        trace(x.toString());\n        return 'foo';\n    }\n}\n\nabstract Bar(String){\n    public function new (str: String){\n        this = str + '?';\n    }\n    public function toString() {\n        return this + \"!\";\n    }\n    @:from\n    static public function fromString(str:String){\n        return new Bar(str);\n    }\n}\n```\n\n# Misc\n\n## What the heck is a Golgi?\n\nThe Golgi apparatus is an\n[organelle](https://en.wikipedia.org/wiki/Golgi_apparatus), or specialized\nsubunit within a biological cell.  It's involved with packaging proteins and\nrouting them to destinations within the cell's nucleus.  As important as the\nGolgi apparatus is, it is still just part of a cell.  It doesn't stand on its\nown.\n\nA Golgi API is involved with packaging content and routing it to the appropriate\nAPI.  As critical as this job is, Golgi doesn't stand on its own as a web\nframework.  Instead, it seeks to serve as a flexible basis for numerous other\nrouting tasks.\n\n\nGolgi is based heavily off of\n[haxe.web.Dispatch](http://api.haxe.org/haxe/web/Dispatch.html).  Dispatch is\nwell loved, but it's older and its design was driven in part due to limitations\nin the macro features of the time.  While certain Dispatch patterns will be\nfamiliar, enough of the API and feature set has changed to merit a new name\nrather than a new version.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdonaldson%2Fgolgi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjdonaldson%2Fgolgi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjdonaldson%2Fgolgi/lists"}