Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: rework service and router api for TS server #70

Merged
merged 5 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
- run: pnpm nx affected -t pub -- get # install dart dependencies for affected projects
- run: pnpm run build
- run: pnpm nx affected -t lint
- run: pnpm nx affected -t typecheck
unit-tests:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ Arri is an RPC framework designed for effortless end-to-end type safety across p

This is a work in progress. Things will break.

## Schema Builder
## Official Server Implementations

[@arrirpc/schema](tooling/schema/README.md) is used to define types that can be generated in any language. It also doubles as a parsing and serialization library that can be used on a NodeJS backend.
- [Typescript](/languages/ts/ts-server/README.md)

## Server Implementations
When I have time I would like to add more languages to this list. Currently I have the following lanaguages on my shortlist for potential server implementations:

- [Typescript](languages/ts/ts-server/README.md) - Official ts server implementation. It uses [@arrirpc/schema](tooling/schma/README.md) to define language agnostic types and safely parse/serialize inputs and outputs.
- go
- rust
- dart

## Client Generators

Expand All @@ -32,7 +34,8 @@ Below are the language client generators that are planned to have first party su

## Other Tooling

- [arri CLI](/tooling/cli/README.md) - CLI tool for run code generators and managing dependencies
- [Arri CLI](/tooling/cli/README.md) - CLI tool for run code generators and managing dependencies
- [@arrirpc/schema](tooling/schema/README.md) - Arri type builder used to define types that can be generated in multiple languages. It also doubles as a parsing and serialization library that can be used on a NodeJS backend.
- [@arrirpc/typebox-adapter](tooling/schema-typebox-adapter/README.md) - convert Typebox Schemas to Arri Type Definitions
- [@arrirpc/eslint-plugin](tooling/eslint-plugin/README.md) - Useful eslint rules when making Arri Type Definitions

Expand Down
43 changes: 27 additions & 16 deletions languages/ts/ts-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,17 @@ For those that want to opt out of the file-based routing system you can manually
```ts
// using the app instance
const app = new ArriApp()
app.rpc('sayHello', {...})
app.rpc('sayHello',
defineRpc({...})
);

// using a sub-router
// defining a service
const app = new ArriApp();
const router = new ArriRouter();
router.rpc('sayHello', {...})
app.use(router)
const usersService = defineService("users", {
getUser: defineRpc({..}),
createUser: defineRpc({..}),
});
app.use(usersService);
```

#### Creating Event Stream Procedures
Expand All @@ -205,7 +209,6 @@ Event stream procedures make use of [Server Sent Events](https://developer.mozil
Arri Event streams sent the following event types:

- `message` - A standard message with the response data serialized as JSON
- `error` - An error message with an `ArriRequestError` sent as JSON
- `done` - A message to tell clients that there will be no more events
- `ping` - A message periodically sent by the server to keep the connection alive.

Expand All @@ -215,11 +218,6 @@ id: string | undefined;
event: "message";
data: Response; // whatever you have specified as the response serialized to json

/// error event ///
id: string | undefined;
event: "error";
data: ArriRequestError; // serialized to json

/// done event ///
event: "done";
data: "this stream has ended";
Expand Down Expand Up @@ -268,11 +266,14 @@ export default defineEventStreamRpc({
#### EventStreamConnection methods

```ts
stream.push(data: Data, eventId?: string)
stream.pushError(error: ArriRequestError, eventId?: string)
// send the stream to the client. Must be called before pushing any messages
stream.send()
stream.end()
stream.on(e: 'request:close' | 'close', callback: () => any)
// push a new message to the client
stream.push(data: Data, eventId?: string)
// close the stream and tell the client that there will be no more messages
stream.close()
// register a callback that will fire after the stream has been close by the server or the connection has been dropped
stream.onClosed(cb: () => any)
```

### Creating Websocket Procedures (Experimental)
Expand Down Expand Up @@ -363,6 +364,16 @@ router.route({
}
})
app.use(router)

// sup-routers can also specify a route prefix
const router = new ArriRouter("/v1")
router.route({
method: "get",
path: "/hello-world", // this will become /v1/hello-world
handler(event) {
return "hello world"
}
});
```

### Adding Middleware
Expand Down Expand Up @@ -440,7 +451,7 @@ export default defineConfig({

For info on what generators are available see [here](/README.md#client-generators)

For info on how to create your own generator see []
For info on how to create your own generator see [@arrirpc/codegen-utils](/tooling/codegen-utils/README.md)

## Key Concepts

Expand Down
1 change: 1 addition & 0 deletions languages/ts/ts-server/src/_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export * from "./middleware";
export * from "./route";
export * from "./router";
export * from "./rpc";
export * from "./service";
export * from "./websocketRpc";
export * from "h3";
53 changes: 29 additions & 24 deletions languages/ts/ts-server/src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { type AppDefinition } from "@arrirpc/codegen-utils";
import { a } from "@arrirpc/schema";

import { ArriApp } from "./app";
import { defineEventStreamRpc } from "./eventStreamRpc";
import { defineRpc } from "./rpc";

it("creates valid app definition", () => {
const app = new ArriApp();
Expand All @@ -17,30 +19,33 @@ it("creates valid app definition", () => {
},
{ id: "SayHelloResponse" },
);
app.rpc({
name: "sayHello",
params: SayHelloParams,
response: SayHelloResponse,
handler({ params }) {
return {
message: `Hello ${params.name}`,
};
},
});
app.rpc({
name: "sayHelloStream",
params: SayHelloParams,
response: SayHelloResponse,
isEventStream: true,
handler({ params, stream }) {
const timeout = setInterval(async () => {
await stream.push({ message: `Hello ${params.name}` });
}, 100);
stream.onClosed(() => {
clearInterval(timeout);
});
},
});
app.rpc(
"sayHello",
defineRpc({
params: SayHelloParams,
response: SayHelloResponse,
handler({ params }) {
return {
message: `Hello ${params.name}`,
};
},
}),
);
app.rpc(
"sayHelloStream",
defineEventStreamRpc({
params: SayHelloParams,
response: SayHelloResponse,
handler({ params, stream }) {
const timeout = setInterval(async () => {
await stream.push({ message: `Hello ${params.name}` });
}, 100);
stream.onClosed(() => {
clearInterval(timeout);
});
},
}),
);

const def = app.getAppDefinition();
const expectedResult: AppDefinition = {
Expand Down
Loading
Loading