a-pea-eye
v1.2.9
Published
A strongly-typed RPC framework for client-server applications written in TypeScript.
Downloads
7
Maintainers
Readme
A-Pea-Eye
A strongly-typed RPC framework for client-server applications written in TypeScript.
Motivation
It helps to have correctness guarantees from your compiler when writing your programs. This is applicable when writing client-server applications, but requires some tooling to achieve.
To this end, we can use an RPC client that is aware of the return type of the server response. We will encode the data type and error type into the return type of an RPC method. The client will infer the return type from the RPC method enabling the compiler to know about data and error types. The compiler will enforce appropriate error handling on the client: providing us strongly-typed client-server code.
Our error propagation is inspired by
Rust's Result type, which returns a
tuple of Result<T, E>
from a function. Here T
is your data type and E
is
your error type.
Features
- [x] RPC Client that can propagate errors, data types, stack traces over a network boundary
- [ ] Stack traces and errors are anonymized/hidden in production
- [ ] Runtime type guards on client boundaries (with appropriate exception handling)
Usage
We define our server methods as functions with a return type of Result<T, E>
.
This is equivalent to Ok<T> | Err<E>
.
Note: Our function's return type conforms with
Ok(x/y) and Err('Divided by zero')
.
// methods.ts
import { Ok, Err, Result } from 'a-pea-eye'
// Define methods for your server logic
export const methods = {
divide(
_req: Request,
x: number,
y: number
): Result<number, 'Divided by zero'> {
if (y === 0) {
return Err('Divided by zero')
} else {
return Ok(x / y)
}
},
}
Create a client that is aware of the return types of your methods.
// client.ts
import { createClient } from 'a-pea-eye'
import { methods } from './methods'
// Create RPC client
const client = createClient<typeof methods>(`http://localhost:8080/rpc'`)
// Invoke method on RPC client
const result = await client.divide(10, 0)
// NOTE: TypeScript enforces error checking through type narrowing.
if (result.ok) {
const quotient = val // compiler knows `val` is of type 'number'
} else {
const err = val // compiler knows `val` is of type 'string'
}
Finally, this is what configuring your server looks like.
// server.ts
import { createMiddleware } from 'a-pea-eye'
import express from 'express'
import { methods } from './methods'
// Configure express server
const app = express()
app.use(express.json())
app.post('/rpc', createMiddleware(methods))
// Start server
app.listen(8080)
Alternatives
- https://github.com/shekohex/jsonrpc-ts
- https://github.com/aiden/rpc_ts
- https://github.com/strongloop/loopback-next