Expose a module's protocol over HTTP, so another process can call it.
The counterpart to …rpc.client: that turns protocol calls into requests,
this turns requests back into protocol calls against the local
implementation. Together they are what lets a module run as its own service
without its callers changing (BOU-90).
FC/IS: shell. The envelope contract is pure and lives in
wagoe.platform.core.rpc.
Expose a module's protocol over HTTP, so another process can call it. The counterpart to `…rpc.client`: that turns protocol calls into requests, this turns requests back into protocol calls against the local implementation. Together they are what lets a module run as its own service without its callers changing (BOU-90). FC/IS: shell. The envelope contract is pure and lives in `wagoe.platform.core.rpc`.
Where rpc-app serves the endpoint. Matches client/default-opts.
Where `rpc-app` serves the endpoint. Matches `client/default-opts`.
(handle-envelope protocol
implementation
{:keys [operation args correlation-id] :as envelope})Invoke one envelope against implementation. Returns a response envelope.
Errors are returned, never thrown: the client reads status and body, and an exception escaping here would surface as a 500 with no operation named — the caller would know something failed but not what it had asked for.
That includes a body that is not an envelope. This endpoint is reachable by
anything that can post to it, so :operation may be missing or may not be a
name at all; reading it unguarded would throw before the error map that
promises to describe the failure could be built.
Invoke one envelope against `implementation`. Returns a response envelope. Errors are returned, never thrown: the client reads status and body, and an exception escaping here would surface as a 500 with no operation named — the caller would know something failed but not what it had asked for. That includes a body that is not an envelope. This endpoint is reachable by anything that can post to it, so `:operation` may be missing or may not be a name at all; reading it unguarded would throw before the error map that promises to describe the failure could be built.
(rpc-app protocol implementation opts)(rpc-app protocol implementation opts path)A standalone Ring app serving protocol — for the service's own listener.
Deliberately not a route map for a module's :api or :web slot. Both are
rewritten by the router: :api paths gain the version prefix and :web
paths gain /web, so a client on the default :path gets a 404 either way.
:web is worse than wrong — a POST there is CSRF-validated when CSRF is
enabled, so the call would be rejected 403 by a check meant for browser
forms, which a service-to-service caller has no token for.
Underneath that: this endpoint invokes port methods, and the public listener is not where it belongs. A sliced-out service should serve it on its own listener, reachable only from inside the deployment — which is what the service launch mode (BOU-91) will start. Until then, mounting is the caller's decision to make explicitly, rather than one this namespace makes look routine.
Format middleware is included, so this handler is complete: it decodes the transit body the client sends and encodes the response.
Args:
protocol - the protocol map
implementation - a value satisfying it, in this process
opts - {:service-key "…"}, or {:auth :none}
path - defaults to default-path
Example: (jetty/run-jetty (rpc-app ports/IPaymentProvider provider {:service-key key}) {:port 3001 :join? false})
A standalone Ring app serving `protocol` — for the service's own listener.
Deliberately not a route map for a module's `:api` or `:web` slot. Both are
rewritten by the router: `:api` paths gain the version prefix and `:web`
paths gain `/web`, so a client on the default `:path` gets a 404 either way.
`:web` is worse than wrong — a POST there is CSRF-validated when CSRF is
enabled, so the call would be rejected 403 by a check meant for browser
forms, which a service-to-service caller has no token for.
Underneath that: this endpoint invokes port methods, and the public listener
is not where it belongs. A sliced-out service should serve it on its own
listener, reachable only from inside the deployment — which is what the
service launch mode (BOU-91) will start. Until then, mounting is the
caller's decision to make explicitly, rather than one this namespace makes
look routine.
Format middleware is included, so this handler is complete: it decodes the
transit body the client sends and encodes the response.
Args:
protocol - the protocol map
implementation - a value satisfying it, in this process
opts - {:service-key "…"}, or {:auth :none}
path - defaults to `default-path`
Example:
(jetty/run-jetty (rpc-app ports/IPaymentProvider provider
{:service-key key})
{:port 3001 :join? false})(rpc-handler protocol implementation {:keys [service-key auth] :as _opts})A Ring handler serving protocol backed by implementation.
Use rpc-app unless you have a reason not to — it is this handler with the
format middleware it needs, ready for its own listener. Do not merge this
into a module's :api or :web routes: the router rewrites both paths, and
a POST under /web is CSRF-validated.
Expects the app's format middleware to have decoded the body into
:body-params, and leaves encoding the response to it as well — the client
asks for application/transit+json, so setting a content type here would
override the negotiation and mislabel the body. transit rather than JSON
because JSON flattens every keyword to a string, and a protocol returning a
keyword status would answer with a string across the hop and nowhere else.
The context an inbound request carries (correlation-id, tenant, auth) is read from the headers and merged onto the envelope, so a downstream call made while handling this one keeps the same correlation-id rather than starting a fresh trace.
Args: protocol - the protocol map, e.g. wagoe.payments.ports/IPaymentProvider implementation - a value satisfying it, in this process opts - {:service-key "…"}, or {:auth :none} to opt out
Callers must present the service key in the x-rpc-service-key header. A
missing or wrong one is 401 before anything reaches the implementation —
:sigs bounds which operations exist, not who may invoke them. The key is
validated at construction, so a service configured without one fails to
start instead of coming up unprotected.
Example: (rpc-app payments-ports/IPaymentProvider provider {:service-key key})
A Ring handler serving `protocol` backed by `implementation`.
Use `rpc-app` unless you have a reason not to — it is this handler with the
format middleware it needs, ready for its own listener. Do not merge this
into a module's `:api` or `:web` routes: the router rewrites both paths, and
a POST under `/web` is CSRF-validated.
Expects the app's format middleware to have decoded the body into
`:body-params`, and leaves encoding the response to it as well — the client
asks for `application/transit+json`, so setting a content type here would
override the negotiation and mislabel the body. transit rather than JSON
because JSON flattens every keyword to a string, and a protocol returning a
keyword status would answer with a string across the hop and nowhere else.
The context an inbound request carries (correlation-id, tenant, auth) is
read from the headers and merged onto the envelope, so a downstream call
made while handling this one keeps the same correlation-id rather than
starting a fresh trace.
Args:
protocol - the protocol map, e.g. wagoe.payments.ports/IPaymentProvider
implementation - a value satisfying it, in this process
opts - {:service-key "…"}, or {:auth :none} to opt out
Callers must present the service key in the `x-rpc-service-key` header. A
missing or wrong one is 401 before anything reaches the implementation —
`:sigs` bounds which operations exist, not who may invoke them. The key is
validated at construction, so a service configured without one fails to
start instead of coming up unprotected.
Example:
(rpc-app payments-ports/IPaymentProvider provider {:service-key key})cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |