Avro Protocols
Typr Events generates RPC interfaces from Avro protocol files (.avpr).
Protocol Definitionβ
{
"protocol": "UserService",
"namespace": "com.example.service",
"doc": "User management service",
"types": [
{
"type": "record",
"name": "User",
"fields": [
{"name": "id", "type": "string"},
{"name": "email", "type": "string"},
{"name": "name", "type": "string"},
{"name": "createdAt", "type": {"type": "long", "logicalType": "timestamp-millis"}}
]
},
{
"type": "error",
"name": "UserNotFoundError",
"fields": [
{"name": "userId", "type": "string"},
{"name": "message", "type": "string"}
]
},
{
"type": "error",
"name": "ValidationError",
"fields": [
{"name": "field", "type": "string"},
{"name": "message", "type": "string"}
]
}
],
"messages": {
"getUser": {
"doc": "Get a user by ID",
"request": [{"name": "userId", "type": "string"}],
"response": "User",
"errors": ["UserNotFoundError"]
},
"createUser": {
"doc": "Create a new user",
"request": [
{"name": "email", "type": "string"},
{"name": "name", "type": "string"}
],
"response": "User",
"errors": ["ValidationError"]
},
"deleteUser": {
"doc": "Delete a user",
"request": [{"name": "userId", "type": "string"}],
"response": "null",
"errors": ["UserNotFoundError"]
},
"notifyUser": {
"doc": "Send notification (fire-and-forget)",
"request": [
{"name": "userId", "type": "string"},
{"name": "message", "type": "string"}
],
"one-way": true
}
}
}
Generated Interfaceβ
Javaβ
public interface UserService {
GetUserResult getUser(String userId);
CreateUserResult createUser(String email, String name);
DeleteUserResult deleteUser(String userId);
void notifyUser(String userId, String message); // one-way
}
Kotlinβ
interface UserService {
fun getUser(userId: String): GetUserResult
fun createUser(email: String, name: String): CreateUserResult
fun deleteUser(userId: String): DeleteUserResult
fun notifyUser(userId: String, message: String) // one-way
}
Scalaβ
trait UserService:
def getUser(userId: String): GetUserResult
def createUser(email: String, name: String): CreateUserResult
def deleteUser(userId: String): DeleteUserResult
def notifyUser(userId: String, message: String): Unit // one-way
Handler Interfaceβ
For implementing the service:
public interface UserServiceHandler extends UserService {
// Inherits all methods from UserService
}
Implement this interface with your business logic:
public class UserServiceImpl implements UserServiceHandler {
@Override
public GetUserResult getUser(String userId) {
return userRepository.findById(userId)
.map(GetUserResult.Ok::new)
.orElseGet(() -> new GetUserResult.Err(
new UserNotFoundError(userId, "User not found")));
}
}
Message Typesβ
Request-Responseβ
Messages with response and optionally errors:
{
"request": [{"name": "userId", "type": "string"}],
"response": "User",
"errors": ["UserNotFoundError"]
}
Void Responseβ
Use "response": "null" for operations that succeed without returning data:
{
"response": "null",
"errors": ["UserNotFoundError"]
}
One-Way (Fire-and-Forget)β
Use "one-way": true for notifications that don't expect a response:
{
"one-way": true
}
Generates void return type with no result ADT.
Error Typesβ
Avro error types generate exception-like classes:
public record UserNotFoundError(
String userId,
String message
) { }
These are used in the Result ADT (see Result ADT).