Rtag is a framework for building realtime applications with a focus on development experience.
Rtag comes out of the box with the following features so that developers don't have to think about them:
- Networking (state synchronization and RPC, efficient binary serialization)
- Authentication
- Automatic persistence
- Declarative API format with client generation
- Development server with hot reloading and built in debug UI
The foundation of an rtag application is the rtag.yml
file, which defines various aspects of the application's behavior. One of the primary components the developer includes in this file is a fully typed API, which lists the server methods as well as the client state tree.
From this specification, rtag automatically generates the following:
- server side method stubs that set up the entire server code structure and just need to be filled in with the application's business logic
- clients for frontends to communicate with the rtag server in a typesafe manner
- a web-based debug application that allows for testing backend logic right away without writing any frontend code
- node v16.12.0+
Install rtag from the npm registry:
npm install -g rtag
First, create a directory for your application and create a rtag.yml
file inside the directory with the following contents:
# rtag.yml
types:
Username: string
Message:
text: string
sentAt: int
sentBy: Username
sentTo: Username?
RoomState:
name: string
createdBy: Username
messages: Message[]
methods:
createRoom:
name: string
sendPublicMessage:
text: string
sendPrivateMessage:
to: Username
text: string
auth:
anonymous:
separator: "-"
userState: RoomState
initialize: createRoom
error: string
Next, run rtag init
to initialize your project. Then run rtag dev
to start the debug server. Visit http://localhost:3000 where you see the following Prototype UI view:
We then fill in the methods in server/impl.ts
with our desired implementation:
import { Methods, Context } from "./.rtag/methods";
import { UserData, Response } from "./.rtag/base";
import { RoomState, ICreateRoomRequest, ISendPublicMessageRequest, ISendPrivateMessageRequest } from "./.rtag/types";
export class Impl implements Methods<RoomState> {
createRoom(user: UserData, ctx: Context, request: ICreateRoomRequest): RoomState {
return { name: request.name, createdBy: user.name, messages: [] };
}
sendPublicMessage(state: RoomState, user: UserData, ctx: Context, request: ISendPublicMessageRequest): Response {
state.messages.push({ text: request.text, sentAt: ctx.time(), sentBy: user.name });
return Response.ok();
}
sendPrivateMessage(state: RoomState, user: UserData, ctx: Context, request: ISendPrivateMessageRequest): Response {
state.messages.push({ text: request.text, sentAt: ctx.time(), sentBy: user.name, sentTo: request.to });
return Response.ok();
}
getUserState(state: RoomState, user: UserData): RoomState {
return {
name: state.name,
createdBy: state.createdBy,
messages: state.messages.filter(
(msg) => msg.sentBy === user.name || msg.sentTo === user.name || msg.sentTo === undefined
),
};
}
}
Note that currently, the only backend language supported is typescript. More language support is planned for the future.
Finally, we can see our working application in action:
Here are some example apps built with rtag:
For a high level overview of rtag concepts and goals, see concepts. For more details on how to implement an rtag application, check out the reference docs.
If you have any questions/suggestions or want to report a bug, please feel free to file an issue or start a discussion on Github!