Overview
Automorph is a Scala RPC client and server library for calling and serving remote APIs in a few lines of code.
Goals
- Focus on flexibility, type-safety and performance
- Ensure there is no boilerplate code
- Require minimal dependencies
- Allow use of dynamic message payload
- Support manipulation of transport layer metadata
- Facilitate customization based on sensible defaults
- Enable easy integration with existing technology
Features
Client
- Transparently generates optimized RPC client bindings at compile time from public API trait methods.
- Calls remote APIs using a supported transport protocol by selecting a client transport layer.
- Allows changing the local to remote RPC function names mapping.
- Allows changing the RPC errors to exceptions mapping.
Server
- Transparently generates optimized RPC server bindings at compile time from public API instance methods.
- Serves remote APIs using a standalone server by selecting a server transport layer.
- Embeds remote API into an existing server via a suitable endpoint transport.
- Automatically generates RPC API discovery functions providing OpenRPC 1.3+ and OpenAPI 3.1+ schemas.
- Allows changing the remote to local RPC function names mapping.
- Allows changing the exceptions to RPC errors mapping.
General
- Enables flexible builds with specific artifacts for selected integrations only.
- Supports use of JSON-RPC or Web-RPC as an RPC protocol.
- Supports all effect systems to call or implement remote APIs.
- Serializes arbitrary data types via the selected message codec.
- Defines an easily composable set of default plugins and configuration values.
- Provides optional remote API extensions to access or modify transport protocol metadata.
- Provides RPC protocol message model to create and consume dynamic message payload.
Usage
Platform requirements
- Scala 3.3+ or 2.13+
- Java Runtime Environment 11+
- SLF4J logger implementation (optional)
Main interface
// Define a remote API
trait Api:
def hello(n: Int): Future[String]
// Create server implementation of the remote API
val service = new Api:
def hello(n: Int): Future[String] =
Future(s"Hello world $n")
// Expose a server API implementation to be called remotely
val apiServer = server.bind(service)
// Create a type-safe local proxy for the remote API from an API trait
val remoteApi = client.bind[Api]
// Call the remote API function via the local proxy
remoteApi.hello(1)
// Call the remote API function dynamically without using the API trait
client.call[String]("hello")("n" -> 1)
Note: Imports, server setup and client setup are omitted here and can be found in the full example.
API
The following classes represent primary entry points to Automorph functionality:
- RPC client - Used to perform type-safe remote API calls or send one-way messages.
- RPC server - Used to serve remote API requests and invoke bound API methods to process them.
- RPC endpoint - Used to handle remote API requests as part of an existing server and invoke bound API methods to process them.
Various combinations of RPC protocol, effect system, message codec and transport layer can be utilized by supplying the desired plugin instances to the factory methods of the primary classes listed above.
There are also additional factory methods for creating primary class instances with default plugins.
SPI
The following traits define interfaces for implementing various Automorph plugins:
- RPC protocol - Enables use of a specific RPC protocol.
- Effect system - Enables remote APIs to use specific effect handling abstraction.
- Message codec - Enables serialization of RPC messages into specific structured data format.
- Client transport - Enables RPC client to send requests and receive responses using specific transport protocol.
- Server transport - Enables RPC server to receive requests and send responses using specific transport protocol.
- Endpoint transport - Enables RPC endpoint to integrate with and handle requests from an existing server infrastructure.
Limitations
- Remote APIs must not contain overloaded methods
- Remote API methods must not use type parameters
- Remote API methods must not be inline
- Remote APIs must not be used from within the App trait nor from within any other delayed initialization scope
- JSON-RPC protocol implementation does not support batch requests
- Maximum number of arguments the RPC client supports for dynamic remote APIs calls without using an API trait is 9
- RPC protocol plugin constructors for Scala 2 might require explicitly supplied type parameters due to type inference constraints
Known issues
- Mangled signatures of a few nonessential methods in the API documentation caused by a Scaladoc defect
Supported standards
The following technical standards are supported by freely combining appropriate plugins.
RPC protocols
Transport protocols
Message formats
- JSON (Default)
- MessagePack
Effect handling
- Asynchronous (Default)
- Synchronous
- Monadic
API schemas
Upcoming features
Core
- Server to client (reversed direction) RPC requests for HTTP, WebSocket and AMQP transport protocols
Platform
- Native Image support
- ScalaJS support
Transport Layer
Message Codec
- weePickle integration
- Play JSON integration
- JSON4S integration
- Smile message format
- CBOR message format
- Ion message format
Author
- Martin Ockajak
Special thanks
- Luigi Antognini
Inspired by
License
Licensed under Apache License, Version 2.0.