Home

Awesome

clj-grpc

A Clojure library designed to provide hassle-free, ready to go gRPC experience without ton of preparations and Java code...

...or just a bunch of macros functions and macros.

Rationale

Assumptions

TODO

See also (or rather "read before use")

Credits

Usage

[clj-grpc "0.1.0"]
:plugins [...
          [org.clojars.awebneck/lein-protoc "0.5.5"]
          ...
          ]
;; decide which version of protoc and grpc-java to use
:protoc-version "3.10.0"
:protoc-grpc {:version "1.25.0"}
:protoc-source-paths ["src/proto"] ;; where to look for `.proto` files
:proto-target-path "target/generated-sources/protobuf" ;; where should protoc put generated sources
:java-source-paths [... "target/generated-sources/protobuf"] ;; point java compiler to newly generated sources
syntax = "proto3";

option java_package = "example.grpc_api";
option java_outer_classname = "ExampleProto";

package example;

message GetAllExamplesParams {
}

message GetExampleParams {
    string example_id = 1;
}


service Example {
    rpc GetExample (GetExampleParams) returns (Example) {
    }

    rpc GetAllExamples (GetAllExamplesParams) returns (stream Example) {
    }
}

message Example {
    string example_id = 1;
    string name = 2;
    string description = 3;
}
(ns example.grpc-api.core
  ;; require macros from `clj-grpc.server` namespace
  (:require [clj-grpc.server :refer [implement-grpc-service defrpc on-next]]))

;; describe service which will be implemented in this namespace.
;; `:java-package` and `:java-outer-classname` has to be consistent with `proto` file
;;
;; NOTE: it might be necessary to restart Clojure process after this is defined so all the classes will be compiled
(implement-grpc-service Client
  :java-package "data_engine.grpc.client"
  :java-outer-classname "ClientProto" )

;; define RPC method `getExample` returning only one example
(defrpc getExample [_this req res]
  ;;
  ;; do some code here
  ;;
  ;; Finally call `on-next` which builds message of type `Example`, initializes it with provided map
  ;; handling kebab-case to camelCase conversion and sends it through StreamObserver `res`
  (on-next "Example" res
           {:name                  "Name"
            :description           "Description"
            :example-id            (:example-id req)}))

;; Another example of method, this time `server-streaming`.
;; Implemenation has nothing specific about it, just call `on-next` multiple times

(defrpc getAllExamples [_this _req res]
  (dotimes [x 10]
    (on-next "Example" res
           {:name                  (str "Name " x)
            :description           (str "Description" x)
            :example-id            (str "example-" x)})))
;; Require `new-grpc-server`
(ns example.systems
  "System definition"
  (:require [com.stuartsierra.component :as component]
            [system.core :refer [defsystem]]
            [clj-grpc.server :refer [new-grpc-server]]
            ...
            )

;; Add `GrpcServer` to system definition. Specify port on which to listen. It will automatically server 
;; all services defined with `IMPLEMENT-GRPC-SERVICE`
(defsystem dev-system
           [...
            :my-grpc-server (new-grpc-server :port 5000)
            ...
            ]

Start you system with system's (start) and you should have your service running!

Note for IntelliJ IDEA + Cursive users

To avoid warnings and unexpected behaviour when using provided macros, configure their IDE resolution as follows:

Behavior won't be perfect, but good enough - for example warnings about unused functions might still occur.

See https://cursive-ide.com/userguide/macros.html#customising-symbol-resolution for more info.

License

Copyright © 2019 Slawomir Gonet slawek@otwiera.cz

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.