Use Cases
This page shows typical use cases where endpoints can provide value.
Microservices
Describe the HTTP APIs between the services, and let endpoints implement the clients and servers for these APIs:
/**
* Application of a command.
*
* Returns the produced event, or `None` in case of failure (aggregate
* not found or invalid command).
*/
val command: Endpoint[Command, Option[StoredEvent]] =
endpoint(
post(path / "command", jsonRequest[Command]),
ok(jsonResponse[Option[StoredEvent]])
)
Invoking a service from another is as simple as a method call:
val eventuallyMaybeEvent: Future[Option[StoredEvent]] =
commandsClient.command(CreateMeter(createData.label))
endpoints takes care of correctly constructing the HTTP request and decoding the HTTP response according to the endpoint description.
Maintenance effort is reduced: you only maintain the description of the HTTP API, not its client and server implementations.
Web Applications
Thanks to Scala.js it is possible to write the client-side part of a web application in Scala. Then, endpoints simplifies client-server communication by turning method calls into remote invocations.
Example of endpoint definition:
/** Registers a new meter */
val createMeter: Endpoint[CreateMeter, Meter] =
endpoint(
post(metersPath, jsonRequest[CreateMeter]),
ok(jsonResponse[Meter])
)
Corresponding invocation from the client-side:
val eventuallyCreatedMeter: Future[Meter] =
PublicEndpoints.createMeter(CreateMeter(name))
Documenting a Web Service
Thanks to the separation between the description of an HTTP API and its implementation, endpoints can also generate an OpenAPI document for a given HTTP API description.
For instance, given the following endpoints descriptions:
import endpoints.{algebra, generic}
trait CounterEndpoints
extends algebra.Endpoints
with algebra.JsonEntitiesFromSchemas
with generic.JsonSchemas {
// HTTP endpoint for querying the current value of the counter. Uses the HTTP
// verb ''GET'' and the path ''/counter''. Returns the current value of the counter
// in a JSON object. (see below for the `counterJson` definition)
val currentValue = endpoint(get(path / "counter"), counterJsonResponse)
// HTTP endpoint for updating the value of the counter. Uses the HTTP verb ''POST''
// and the path ''/counter''. The request entity contains an `Operation` object encoded
// in JSON. The endpoint returns the current value of the counter in a JSON object.
val update = endpoint(
post(
path / "counter",
jsonRequest[Operation],
docs = Some("The operation to apply to the counter")
),
counterJsonResponse
)
// Since both the `currentValue` and `update` endpoints return the same
// information, we define it once and just reuse it. Here, we say
// that they return an HTTP response whose entity contains a JSON document
// with the counter value
lazy val counterJsonResponse =
ok(jsonResponse[Counter], docs = Some("The counter current value"))
// We generically derive a data type schema. This schema
// describes that the case class `Counter` has one field
// of type `Int` named “value”
implicit lazy val jsonSchemaCounter: JsonSchema[Counter] = genericJsonSchema
// Again, we generically derive a schema for the `Operation`
// data type. This schema describes that `Operation` can be
// either `Set` or `Add`, and that `Set` has one `Int` field
// name `value`, and `Add` has one `Int` field named `delta`
implicit lazy val jsonSchemaOperation: JsonSchema[Operation] =
genericJsonSchema
}
endpoints can produce the following OpenApi document.