Arri RPC
repository·master·Indexed 19 days ago
https://github.com/modiimedia/arriA code-first RPC framework that automates the generation of type-safe clients from server-side definitions using a central App Definition. It supports building servers in Go—including standard HTTP procedures and Server-Sent Events (SSE) streams—and generating clients for languages such as Dart.
What's inside arri
- Arri RPC is a code-first RPC framework designed to provide type-safe clients automatically. Instead of manually writing clients for different platforms, you define your server code, and Arri generates the corresponding client implementations. This ensures that your client and server stay in sync without manual intervention.
Overview of Arri Schema Interface
masterThe Arri Schema Interface is an experimental universal validator interface designed for the Arri ecosystem. Inspired bystandard-schema, its goal is to provide a standardized way for different validation libraries to implement an interface that is compatible with@arrirpc/server. This allows developers to use their preferred validation tools within an Arri RPC server implementation.Implement a custom Event type in Go
masterIn Arri RPC, every procedure receives an
Eventtype. This type is passed to every procedure and allows access to request-specific context. To use a custom event, you must implement thearri.Eventinterface, which requiresRequest() *http.RequestandWriter() http.ResponseWritermethods.When initializing your app with
arri.NewApp, you provide a factory function that creates your custom event using the incominghttp.ResponseWriterand*http.Request.type MyCustomEvent struct { r *http.Request w http.ResponseWriter } func (e MyCustomEvent) Request() *http.Request { return e.r } func (e MyCustomEvent) Writer() http.ResponseWriter { return e.w } // In main: app := arri.NewApp( http.DefaultServeMux, arri.AppOptions[MyCustomEvent]{}, func(w http.ResponseWriter, r *http.Request) (*MyCustomEvent, arri.RpcError) { return &MyCustomEvent{r: r, w: w}, nil }, )Define messages using Go structs
masterIn Arri RPC, all parameters and responses are defined as Go structs. Arri uses the
reflectlibrary to validate incoming requests against these structs and automatically converts them into Arri Type Definitions (ATD). This allows client generators to create type-safe clients automatically without manual work.Example struct definition:
type User struct { Id string Name string IsAdmin bool }How procedures map to endpoints
masterArri uses a file-based routing system where procedure endpoints are derived from their file paths and names.
Routing Rules:
- Endpoints are converted to kebab-case (e.g.,
getStatus.rpc.tsbecomes/get-status). - Directory structures are preserved in the URL (e.g.,
src/procedures/users/getUser.rpc.tsbecomes/users/get-user). - All paths are relative to the
rpcRoutePrefixoption.
HTTP Methods:
- By default, all procedures use the
postmethod. - You can override the method using the
methodoption indefineRpc. - Supported methods:
post,get,delete,patch,put.
Special Note on GET requests: When using the
getmethod, RPC parameters are mapped as query parameters and coerced usinga.coercefromarri-validate. While scalar types are supported, arrays and nested objects are not supported forgetmethods.// procedures/users/getUser.rpc.ts export default defineRpc({ method: 'get', // rest of config });- Endpoints are converted to kebab-case (e.g.,
Use Compiled Validators for high performance
masterFor maximum performance, use
a.compile(schema)to create a highly optimized validator. The compiled validator implements the standard-schema interface.Important Constraints:
- Environment: Compiled validators use
new Function()and will not work in a browser environment. They are intended for backend servers. - Performance: Compilation has overhead, so compile each schema only once.
- Code Generation: To access the generated function bodies (e.g., for debugging or custom logic), pass
trueas the second argument toa.compile(schema, true). This is disabled by default to save memory.
Compiled Methods:
validate(input)parse(input)parseUnsafe(input)coerce(input)coerceUnsafe(input)serialize(input)serializeUnsafe(input)
const User = a.object({ id: a.string(), email: a.nullable(a.string()), created: a.timestamp(), }); const $$User = a.compile(User); $$User.validate(someInput); $$User.parse(someJson); $$User.coerce(someObject); $$User.serialize({ id: '1', email: null, created: new Date() }); // Accessing generated code (requires second param true) const $$UserWithCode = a.compile(User, true); console.log($$UserWithCode.compiledCode.validate);- Environment: Compiled validators use
Understand the Arri Definition File
masterThe Arri server automatically generates a
__definition.jsonfile which serves as the API schema for all procedures and models. This schema is used by Arri to generate type-safe clients in multiple languages. It uses a superset of JSON Type Definition for models rather than standard JSON Schema to ensure better cross-language code generation.By default, you can view this schema at the
/__definitionendpoint while the server is running. Note that this endpoint is relative to your configuredrpcRoutePrefix.{ "procedures": { "sayHello": { "transport": "http", "path": "/say-hello", "method": "post", "params": "SayHelloParams", "response": "SayHelloResponse" } }, "definitions": { "SayHelloParams": { "properties": { "name": { "type": "string" } } } } }How to create tolerant Arri clients
masterTo prevent crashes when the server's data model changes, Arri clients should be tolerant. Instead of failing validation, clients should assign fallback values for missing or unrecognized fields.
Fallback Value Mapping:
type fallback value any nullable type nullany optional type undefinedstring ""boolean falsetimestamp [Current Date and Time] floats 0.0integers 0enums The first specified enum value arrays []records {}objects An instance of the object with all fields set to their fallback value discriminated union An instance of the first specified sub type with all fields set to their fallback value (skip recursive types) When to return an error instead of falling back:
- Unable to connect to the server.
- Server returned an error.
- Server did not provide the correct
content-typeheader. - Server response is not correctly formatted JSON.
Use arrays and slices in messages
masterBoth Go arrays and slices are supported in Arri messages and will be converted to the appropriate ATD elements format.
[]stringUse Metadata for cross-language code generation
masterArri schemas support a metadata object passed as the second argument to schema definitions. This metadata is used by client generators to produce high-quality code in other languages.
Supported Metadata Fields:
id: Used as the type name in generated clients.description: Added as a documentation comment above the generated type.isDeprecated: Marks generated code with the target language's deprecation annotation.
const BookSchema = a.object( { title: a.string(), author: a.string(), publishDate: a.timestamp(), }, { id: 'Book', description: 'This is a book', }, );Understand Arri Generated Kotlin Models
masterAll generated models are Kotlin
data classes. They include built-in support for serialization and URL parameter conversion.Key Features:
- Methods:
toJson(): StringtoUrlQueryParams(): String
- Factory Methods:
new()fromJson(input: String)fromJsonElement(input: JsonElement, instancePath: String)
- Enums: All enums include a
serialValueproperty. - Discriminator Schemas: Converted to Kotlin
sealed classes.
- Methods:
Use maps with string keys
masterArri supports maps where the keys are
string. Attempting to use non-string keys for RPC inputs or outputs will cause a panic when the server starts. Map values can be any supported Arri type.map[string]bool