github.com/clubpay/ronykit/kit@v0.14.4-0.20240515065620-d0dace45cbc7/README.MD (about)

     1  [//]: # (<a href="https://github.com/clubpay/ronykit/actions/workflows/build-and-test.yml?query=branch%3Amain">)
     2  
     3  [//]: # (  <img alt="Build Status" src="https://img.shields.io/github/workflow/status/clubpay/ronykit/build-and-test/main?style=for-the-badge">)
     4  
     5  [//]: # (</a>)
     6  <a href="https://goreportcard.com/report/github.com/clubpay/ronykit">
     7    <img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/clubpay/ronykit?style=for-the-badge">
     8  </a>
     9  <a href="https://github.com/clubpay/ronykit/releases">
    10    <img alt="GitHub release (latest by date including pre-releases)" src="https://img.shields.io/github/v/release/clubpay/ronykit?include_prereleases&style=for-the-badge">
    11  </a>
    12  <a href="https://codecov.io/gh/clubpay/ronykit/branch/main/">
    13      <img alt="Codecov Status" src="https://img.shields.io/codecov/c/github/clubpay/ronykit?style=for-the-badge">
    14  </a>
    15  
    16  # Kit
    17  
    18  RonyKit is a library written in Go (Golang), provides the abstraction layer for creating an API server. By defining separate
    19  components for each task, you are almost free to develop your API server using RPC (Proto/JSON/...), REST, GraphQL or even with your
    20  in-house defined protocol. However, some components are implemented in the 'std' which make it easy to use RonyKit out of the box.
    21  In RonyKIT, I wanted to make it more flexible but still very performant. I will maintain two bundles one based on
    22  [fasthttp](https://github.com/valyala/fasthttp) and the other provides only rpc over websocket based on two very fast
    23  libraries [gobwas](https://github.com/gobwas/ws) and [gnet](https://github.com/panjf2000/gnet), however you are not limited to use those,
    24  and
    25  can build your own Bundles. To have a better understanding of how it is going to work please check examples folder.
    26  in [simple-rest-server](../example/ex-02-rest) it shows a very simple hello world app.
    27  in [mixed-jsonrpc-rest](../example/ex-01-rpc) it shows how you can write your services once but use them with multiple formats like
    28  REST or RPC.
    29  
    30  - [Installation](#installation)
    31  - [Quick start](#quick-start)
    32  - [Kit Components](#kit-components)
    33  	- [1. Handler](#1-handler)
    34  	- [2. Contract](#2-contract)
    35  	- [3. Service](#3-service)
    36  	- [4. Gateway](#4-gateway)
    37  	- [5. Cluster](#5-cluster)
    38  	- [6. EdgeServer](#6-edgeserver)
    39  - [KIT Storage layers](#kit-storage-layers)
    40  
    41  
    42  ## Installation
    43  
    44  To install RonyKIT package, you need to install Go and set your Go workspace first.
    45  
    46  First need to install[Go](https://golang.org/) (**version 1.17+ is required**), then you can use the below Go command to install
    47  RonyKIT.
    48  
    49  ```sh
    50  $ go get -u github.com/clubpay/ronykit/kit/...
    51  ```
    52  
    53  ## Quick start
    54  
    55  You can find some sample codes in the [Examples](../example) folder.
    56  
    57  ## Define Services and Contracts
    58  
    59  In RonyKIT(Kit) you better to define the description of your services and their contracts using `desc` package,
    60  and it provides you many helper methods. Although you can create your concrete implementations of kit.Service
    61  and kit.Contract, but we strongly recommend you to use `desc` package.
    62  
    63  When you develop a handler with Kit package you need to think about the input and output of your API, and define
    64  these DTOs by defining appropriate structs.
    65  
    66  For example, if you want to implement an `Echo` handler you can write your handler like this:
    67  
    68  ```go
    69  package main
    70  
    71  import "github.com/clubpay/ronykit/kit"
    72  
    73  type EchoRequest struct {
    74      ID string `json:"Id"`
    75      Timestamp int64 `json:"timestamp"`
    76  }
    77  
    78  type EchoResponse struct {
    79      ID string `json:"Id"`
    80  }
    81  
    82  func echoHandler(ctx *kit.Context) {
    83      req := ctx.In().GetMsg().(*EchoRequest)
    84  
    85      ctx.In().
    86          Reply().
    87          SetMsg(&EchoResponse{ID: req.ID}).
    88          Send()
    89  }
    90  ```
    91  
    92  Then you can write the service descriptor to define how your handler is going to be accessed from the Gateway of
    93  your server. For example if you have an HTTP Gateway and you need your handler to be accessible using REST API,
    94  you can define your service descriptor like this:
    95  
    96  ```go
    97  package main
    98  
    99  import (
   100      "github.com/clubpay/ronykit/kit"
   101      "github.com/clubpay/ronykit/kit/desc"
   102      "github.com/clubpay/ronykit/std/gateways/fasthttp"
   103  )
   104  
   105  
   106  func MyServiceDesc() *desc.Service {
   107  	return desc.NewService("MyServiceName").
   108  		SetEncoding(kit.JSON).
   109  		AddContract(
   110  			desc.NewContract().
   111  				SetInput(&EchoRequest{}).	// This tells that we expect the incoming request resolves to EchoRequest struct
   112  				SetOutput(&EchoResponse{}). // This tells that we expect the outgoing response resolves to EchoResponse struct
   113  				AddSelector(fasthttp.GET("/echo/:Id")). // This tells that we want to use echoHandler for GET requests with /echo/:Id path
   114  				AddSelector(fasthttp.POST("/echo")). // This tells that we want to use echoHandler for POST requests with /echo path
   115  				SetHandler(echoHandler), // This tells that we want to use echoHandler as the handler of this contract
   116  		)
   117  }
   118  ```
   119  
   120  In the ServiceDescriptor we defined two REST endpoints which our Handler would be served.
   121  RonyKIT's EdgeServer tries its best to fill the input struct (i.e., EchoRequest) from the parameters: urlParam, queryParam or request's
   122  body.
   123  If you have a more complex case, you can also add your own custom decoder when you add your selector to the contract.
   124  
   125  The last step is to set up our EdgeServer and bind our desired Gateway bundle and the `MyServiceDesc`.
   126  The following code shows how we can do that:
   127  
   128  ```go
   129  package main
   130  
   131  import (
   132  	"github.com/clubpay/ronykit/kit"
   133  	"github.com/clubpay/ronykit/kit/desc"
   134  	"github.com/clubpay/ronykit/std/gateways/fasthttp"
   135  )
   136  
   137  type EchoRequest struct {
   138  	ID string `json:"Id"`
   139  	Timestamp int64 `json:"timestamp"`
   140  }
   141  
   142  type EchoResponse struct {
   143  	ID string `json:"Id"`
   144  }
   145  
   146  func echoHandler(ctx *kit.Context) {
   147  	req := ctx.In().GetMsg().(*EchoRequest)
   148  
   149  	ctx.In().
   150  		Reply().
   151  		SetMsg(&EchoResponse{ID: req.ID}).
   152  		Send()
   153  }
   154  
   155  
   156  var MyServiceDesc desc.ServiceDescFunc = func() *desc.Service {
   157  	return desc.NewService("MyServiceName").
   158  		SetEncoding(kit.JSON).
   159  		AddContract(
   160  			desc.NewContract().
   161  				SetInput(&EchoRequest{}).
   162  				SetOutput(&EchoResponse{}).
   163  				AddSelector(fasthttp.GET("/echo/:Id")).
   164  				AddSelector(fasthttp.POST("/echo")).
   165  				SetHandler(echoHandler),
   166  		)
   167  }
   168  
   169  func main() {
   170  	defer kit.NewServer(
   171  		kit.WithGateway(
   172  			fasthttp.MustNew(
   173  				fasthttp.Listen(":80"),
   174  			),
   175  		),
   176  		kit.WithServiceDesc(MyServiceDesc()),
   177  	).
   178  		Start(context.TODO()).
   179  		PrintRoutes(os.Stdout).
   180  		Shutdown(context.TODO(), os.Kill, os.Interrupt)
   181  }
   182  ```
   183  
   184  ---
   185  
   186  # KIT Components
   187  
   188  ## 1. Handler
   189  
   190  Handler is the function/method that accepts `kit.Context` as argument, and it will be called based on the
   191  selector that is defined in the `desc.Service`.
   192  
   193  ## 2. Contract
   194  
   195  Contract defines a business use-case that is very similar to what a function does, just it will be triggered
   196  based on the selectors that we define the ServiceDescriptor.
   197  
   198  ## 3. Service
   199  
   200  One or more Contracts that are doing some operations in a similar domain could be grouped in one Service. Each
   201  EdgeServer can have one or more service attached.
   202  
   203  ## 4. Gateway
   204  
   205  Gateways handle the endpoints of the service. For example, if you need to provide REST apis you can use one of the
   206  standard gateway bundles : fasthttp or silverhttp
   207  For advanced cases you can develop your own Gateway, but it is not necessary for most cases.
   208  
   209  ## 5. Cluster
   210  
   211  Cluster defines the relation between different instances of the EdgeServer. This bundle is optional but if attached
   212  to EdgeServer then in multi instance cases it can help and provides some facilities to share data between different
   213  instances of your EdgeServer.
   214  
   215  ## 6. EdgeServer
   216  
   217  EdgeServer is the main component of the RonyKIT which glues different components together to make it a working
   218  server.
   219  
   220  ---
   221  
   222  ---
   223  
   224  ---
   225  
   226  # KIT Storage layers
   227  
   228  When we develop API server usually we need to store data with different lifecycles. Sometimes we need to store data
   229  that lives between different handlers, in some cases, we need to keep the data between different requests of a connection.
   230  (i.e. for websocket connection we want to keep some data for next request from this connection).
   231  Basically in RonyKIT we provide 4 layers of storage:
   232  
   233  | Layer      | Lifecycle                                                                                                                                                                        |
   234  |------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
   235  | Context    | in the request, but available in all the handlers                                                                                                                                |
   236  | Connection | in the connection, for REST apis usually the lifecycle of Connection storage and Context are same, but for websocket data will be available until websocket connection is active |
   237  | Local      | this is shared between different contracts, services and is in the local memory of the server                                                                                    |
   238  | Cluster    | this is shared between different instances of the EdgeServer. This is enabled ONLY if a Cluster bundle is attached                                                               |
   239  
   240  ---
   241  
   242