Using Superflare
Broadcasting
If you're building a modern web application, you'll probably want some form of realtime communication powered by WebSockets.
Superflare makes it super easy to broadcast realtime Events to your users through specific Channels, backed by Durable Objects.
Broadcasting Events
To broadcast realtime events to users, add a broadcastTo
method to your Event class, and return a Channel name:
import { Event } from "superflare";
export class PostUpdated extends Event {
constructor(public post: Post) {
super();
}
broadcastTo() {
return `posts.${this.postId}`;
}
}
This will broadcast the event to all users subscribed to the posts.{postId}
channel:
export async function action({ params }) {
const post = await Post.find(params.postId);
PostUpdated.dispatch(Post);
}
In the browser, you can use a WebSocket to connect to the channel name you defined and listen for events:
<script type="module">
const channelName = "posts.1";
const socket = new WebSocket("wss://your-app.com/channels/" + channelName);
socket.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
console.log(data);
// => { event: "PostUpdated", post: { ... } }
});
</script>
Prerequisites
If you create your app with npx superflare new
, you will be guided through the process of getting requirements in place to broadcast events out of the box.
However, if you'd like to get started with broadcasting events manually, you'll need to:
- Export the
Channel
Durable Object from Superflare in yourworker.ts
entrypoint. - Define the Durable Object in your
wrangler.json
file. - Add a
binding
to thebroadcast
settings in yoursuperflare.config.ts
which corresponds with the Durable Object you defined above. - Create a route in your framework to handle incoming WebSocket connections.
Broadcast Settings
To configure broadcasting, add a channels
key to your superflare.config.ts
file.
The only required setting is to define a default Durable Object binding for your channels:
import { defineConfig } from "superflare";
export default defineConfig((ctx) => {
return {
// ...
channels: {
default: {
binding: ctx.env.CHANNEL,
},
},
};
});
You can also define additional channel bindings and authorization patterns to restrict access to specific channels:
import { defineConfig } from "superflare";
import { User } from "~/app/models/User";
import { Post } from "~/app/models/Post";
export default defineConfig((ctx) => {
return {
// ...
channels: {
default: {
binding: ctx.env.CHANNEL,
},
"posts.*": {
binding: ctx.env.POSTS_CHANNEL,
async authorize(user: User, postId: number) {
const post = await Post.find(postId);
return user.id === post.userId;
},
},
},
};
});
Channels
Channels are a way to group users together, and broadcast events to them. You can use channels to broadcast events to a specific user, or to a group of users.
Unique channel names correspond to unique instances of a given Durable Object class.
When you define channel configurations, you can use asterisks *
separated by periods .
to denote dynamic fields within channel names. For example, if you wanted to broadcast events to a specific post, you could define a channel name like posts.*
.
When you broadcast an event to a channel, you can pass any number of arguments to the event's broadcastTo
method. These arguments will be passed to the authorize
method on the channel configuration, if one is defined.
For example, a broadcastTo
function returning a channel name of posts.123
would match the channel name configuration for posts.*
.
If multiple channels match an event being broadcast, the channel with the most specific match will be used.
Public Channels
By default, all channels are public, and any user can subscribe to them. You can restrict access to a channel by defining an authorize
method in your superflare.config.ts
file.
Handling WebSocket Connections
You will need to define a route in your framework to send WebSocket connections from the browser.
Superflare provides a handleWebSockets
method to handle the WebSocket connections as well as handling any authorization for channels you have defined within your superflare.config.ts
file.
You must provide the incoming Request
. If you plan to authorize channels, you must pass the SuperflareAuth
and SuperflareSession
instances as well as your User
model to the helper function.
// app/routes/channels.$.ts
import { handleWebSockets } from "superflare";
export async loader({request, context: { auth, session }}) {
return handleWebSockets(request, { auth, session, userModel: User });
}
Any GET requests to /channels/{channelName}
will be handled by the WebSocket handler within your Durable Object.
Superflare assumes the last part of the Request URL pathname separated by /
will be the name of your channel. If you'd like to customize this behavior, you can pass a custom channelName
to the handleWebSockets
method.
return handleWebSockets(request, {
auth,
session,
userModel: User,
channelName: "my-custom-channel",
});