{"id":16800214,"url":"https://github.com/abiosoft/river","last_synced_at":"2025-03-22T02:30:57.960Z","repository":{"id":39817288,"uuid":"62752001","full_name":"abiosoft/river","owner":"abiosoft","description":"River is a simple and lightweight REST server","archived":false,"fork":false,"pushed_at":"2016-07-28T16:15:11.000Z","size":66,"stargazers_count":37,"open_issues_count":4,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T06:51:36.850Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abiosoft.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":"2016-07-06T20:42:46.000Z","updated_at":"2022-10-15T14:00:17.000Z","dependencies_parsed_at":"2022-08-26T05:51:29.034Z","dependency_job_id":null,"html_url":"https://github.com/abiosoft/river","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abiosoft%2Friver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abiosoft%2Friver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abiosoft%2Friver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abiosoft%2Friver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abiosoft","download_url":"https://codeload.github.com/abiosoft/river/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244897810,"owners_count":20528294,"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":[],"created_at":"2024-10-13T09:31:33.540Z","updated_at":"2025-03-22T02:30:57.668Z","avatar_url":"https://github.com/abiosoft.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"river\n=====\nRiver is a simple and lightweight REST server.\n\n### Getting Started\n```go\nrv := river.New()\n```\n\nUse middlewares\n```go\nrv.Use(river.Logger()) \n```\n\nCreate endpoints\n```go\ne := river.NewEndpoint(). \n    Get(\"/:id\", func(c *river.Context){\n        id := c.Param(\"id\")\n        ... // fetch data with id\n        c.Render(200, data)\n    }).\n    Post(\"/\", func(c *river.Context){\n        ... // process c.Body and store in db\n        c.Render(201, data)\n    })\n    ...\n\ne.Use(MyMiddleware) // endpoint specific middleware\n```\n\nHandle endpoints\n```go\nrv.Handle(\"/user\", e) \n```\n\nRun\n```go\nrv.Run(\":8080\")\n```\n\nCheck [example](https://github.com/abiosoft/river/tree/master/example) code for more.\n\n### Approach\n* An endpoint is a REST endpoint with handlers for supported methods.\n* All endpoints are handled by a River instance.\n* Outputs are rendered via a preset or custom Renderer.\n* Middlewares and Renderers can be global or specific to an endpoint.\n\n### Request Flow\nBasic flow\n```\nRequest -\u003e Middlewares -\u003e Endpoint -\u003e Renderer\n```\n\nFull flow\n```\n                    Request\n                       |\n                       |  \n                     Router\n                    /     \\                  \n                   /       \\\n                  /         \\\n              Found      Not Found / Method Not Allowed\n                 \\          /\n                  \\        /\n                   \\      /\n              Global Middlewares\n                   /      \\\n                  /        \\\n Endpoint Middlewares    Not Found / Method Not Allowed Handler\n        |                       |\n        |                       |\n     Endpoint                Renderer\n        |\n        |\n     Renderer\n\n```\n\n### Endpoint\nCreate\n```go\ne := river.NewEndpoint()\n```\n\nHandle Requests\n```go\ne.Get(\"/\", handler).Post(...).Put(...) // method chaining\ne.Handle(method, ...) // for custom request methods\n```\n\nRiver supports dependency injection. With that, any function can be an endpoint handler.  \n```go\nfunc () {...} // valid\nfunc (c *river.Context) {...} // valid\nfunc (c *river.Context, m MyStruct) {...} // valid\nfunc (w http.ResponseWriter, r *http.Request) {...} // valid\nfunc (w http.ResponseWriter, r *http.Request, m MyStruct) {...} // valid\n```\n\nJSON helper\n```go\nfunc (c *river.Context){\n    var users []User\n    c.DecodeJSONBody(\u0026users)\n    ... // process users\n}\n```\n\n### Middleware\nAny function that takes in the context can be used as a middleware.\n```go\ntype Middleware func(c *river.Context)\n```\n\nRiver comes with `river.Recovery()` for panic recovery.  \n\n```go\nrv.Use(Middleware) // global\ne.Use(Middleware)  // endpoint\n```\n\nMiddleware can choose to terminate request flow by not calling `c.Next()`. e.g. Authentication middleware.\n```go\nfunc (c *river.Context){\n    ... // do something before\n    c.Next()\n    ... // do something after\n}\n```\n\nAny `http.Handler` can also be used as a middleware.\n```go\nrv.UseHandler(handler)\n```\n\n### Service Injection\nRegistering\n```go\nvar m MyStruct\n...\nrv.Register(m) // global\ne.Register(m)  // endpoint\n```\n\nThis will be passed as parameter to any endpoint handler that has `MyStruct`\nas a function parameter.\n```go\nfunc handle(c *river.Context, m MyStruct) { ... }\n```\n\nMiddlewares can also register request scoped service.\n```go\nfunc AuthMiddleware(c *river.Context) {\n    var session *Session\n    ... // retrieve session\n    c.Register(session)\n}\n```\n\n### Renderer\nRenderer takes in data from endpoints and renders the data as response.\n\n`context.Render(...)` renders using the configured Renderer. `JSONRenderer` is one of the available renderers. \n\nExample Renderer, transform response to JSend format before sending as JSON.\n```go\nfunc MyRenderer (c *river.Context, data interface{}) error {\n    resp := river.M{\"status\" : \"success\", \"data\" : data}\n    if _, ok := data.(error); ok {\n        resp[\"status\"] = \"error\"\n        resp[\"message\"] = data\n        delete(resp, \"data\")\n    }\n    return JSONRenderer(c, resp)\n}\n```\n\nSetting a Renderer. When an endpoint Renderer is not set, global Renderer is used.\n```go\nrv.Renderer(MyRenderer) // global\ne.Renderer(MyRenderer)  // endpoint\n```\n\n### Custom server\nRiver is an `http.Handler`. You can do without `Run()`.\n```go\nhttp.ListenAndServe(\":8080\", rv)\n```\n\n### Router\nRiver uses [httprouter](https://github.com/julienschmidt/httprouter) underneath.\n\n### Contributing\n* Create an issue to discuss.\n* Send in a PR.\n\n### Why the name \"River\", a \"REST\" server ? Can you REST on a River ?\nWell, yes. You only need to know how to swim or wear a life jacket. \n\n### License\nApache 2","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabiosoft%2Friver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabiosoft%2Friver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabiosoft%2Friver/lists"}