Skip to content

Repository files navigation

river

River is a simple and lightweight REST server.

Getting Started

rv:=river.New()

Use middlewares

rv.Use(river.Logger()) 

Create endpoints

e:=river.NewEndpoint(). Get("/:id", func(c*river.Context){
id:=c.Param("id")
...// fetch data with idc.Render(200, data)
}).
Post("/", func(c*river.Context){
...// process c.Body and store in dbc.Render(201, data)
})
...e.Use(MyMiddleware) // endpoint specific middleware

Handle endpoints

rv.Handle("/user", e) 

Run

rv.Run(":8080")

Check example code for more.

Approach

  • An endpoint is a REST endpoint with handlers for supported methods.
  • All endpoints are handled by a River instance.
  • Outputs are rendered via a preset or custom Renderer.
  • Middlewares and Renderers can be global or specific to an endpoint.

Request Flow

Basic flow

Request -> Middlewares -> Endpoint -> Renderer

Full flow

 Request
|
| Router
/ \ / \
/ \
Found Not Found / Method Not Allowed
\ /
\ /
\ /
Global Middlewares
/ \
/ \
Endpoint Middlewares Not Found / Method Not Allowed Handler
| |
| |
Endpoint Renderer
|
|
Renderer

Endpoint

Create

e:=river.NewEndpoint()

Handle Requests

e.Get("/", handler).Post(...).Put(...) // method chaining
e.Handle(method, ...) // for custom request methods

River supports dependency injection. With that, any function can be an endpoint handler.

func () {...} // validfunc (c*river.Context) {...} // validfunc (c*river.Context, mMyStruct) {...} // validfunc (w http.ResponseWriter, r*http.Request) {...} // validfunc (w http.ResponseWriter, r*http.Request, mMyStruct) {...} // valid

JSON helper

func (c*river.Context){
varusers []Userc.DecodeJSONBody(&users)
...// process users
}

Middleware

Any function that takes in the context can be used as a middleware.

typeMiddlewarefunc(c*river.Context)

River comes with river.Recovery() for panic recovery.

rv.Use(Middleware) // globale.Use(Middleware) // endpoint

Middleware can choose to terminate request flow by not calling c.Next(). e.g. Authentication middleware.

func (c*river.Context){
...// do something beforec.Next()
...// do something after
}

Any http.Handler can also be used as a middleware.

rv.UseHandler(handler)

Service Injection

Registering

varmMyStruct...rv.Register(m) // globale.Register(m) // endpoint

This will be passed as parameter to any endpoint handler that has MyStruct as a function parameter.

funchandle(c*river.Context, mMyStruct) { ... }

Middlewares can also register request scoped service.

funcAuthMiddleware(c*river.Context) {
varsession*Session...// retrieve sessionc.Register(session)
}

Renderer

Renderer takes in data from endpoints and renders the data as response.

context.Render(...) renders using the configured Renderer. JSONRenderer is one of the available renderers.

Example Renderer, transform response to JSend format before sending as JSON.

funcMyRenderer (c*river.Context, datainterface{}) error {
resp:= river.M{"status" : "success", "data" : data}
if_, ok:=data.(error); ok {
resp["status"] ="error"resp["message"] =datadelete(resp, "data")
}
returnJSONRenderer(c, resp)
}

Setting a Renderer. When an endpoint Renderer is not set, global Renderer is used.

rv.Renderer(MyRenderer) // globale.Renderer(MyRenderer) // endpoint

Custom server

River is an http.Handler. You can do without Run().

http.ListenAndServe(":8080", rv)

Router

River uses httprouter underneath.

Contributing

  • Create an issue to discuss.
  • Send in a PR.

Why the name "River", a "REST" server ? Can you REST on a River ?

Well, yes. You only need to know how to swim or wear a life jacket.

License

Apache 2

About

River is a simple and lightweight REST server

Resources

Stars

36 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages