Skip to content

Commit a47816d

Browse files
authored
docs: WebSockets (#443)
1 parent 4b5ab8d commit a47816d

File tree

5 files changed

+239
-30
lines changed

5 files changed

+239
-30
lines changed

β€ŽREADME.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -74,34 +74,6 @@ The goal of Dart Frog is to help developers effectively build backends in Dart.
7474

7575
Dart Frog provides a simple core with a small API surface area in order to reduce the learning curve and ramp-up time for developers. In addition, Dart Frog is intended to help Flutter/Dart developers maximize their productivity by having a unified tech stack that enables sharing tooling, models, and more!
7676

77-
## Feature Set ✨
78-
79-
βœ… Hot Reload ⚑️
80-
81-
βœ… Dart Dev Tools βš™οΈ
82-
83-
βœ… File System Routing 🚏
84-
85-
βœ… Index Routes πŸ—‚
86-
87-
βœ… Nested Routes πŸͺ†
88-
89-
βœ… Dynamic Routes πŸŒ“
90-
91-
βœ… Middleware πŸ”
92-
93-
βœ… Dependency Injection πŸ’‰
94-
95-
βœ… Production Builds πŸ‘·β€β™‚οΈ
96-
97-
βœ… Docker 🐳
98-
99-
βœ… Static File Support πŸ“
100-
101-
🚧 Generated Dart Client Package πŸ“¦
102-
103-
🚧 Generated API Documentation πŸ“”
104-
10577
[dart_installation_link]: https://dart.dev/get-dart
10678
[ci_badge]: https://github.com/VeryGoodOpenSource/dart_frog/actions/workflows/main.yaml/badge.svg
10779
[ci_link]: https://github.com/VeryGoodOpenSource/dart_frog/actions/workflows/main.yaml

β€Ždocs/docs/advanced/powered_by_header.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 3
2+
sidebar_position: 4
33
title: πŸ”‹ Powered By Header
44
---
55

β€Ždocs/docs/advanced/web_socket.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
---
2+
sidebar_position: 3
3+
title: πŸ”Œ WebSockets
4+
---
5+
6+
# WebSockets πŸ”Œ
7+
8+
Dart Frog recently introduced [`package:dart_frog_web_socket`](https://pub.dev/packages/dart_frog_web_socket) to make working with WebSockets easier.
9+
10+
:::info
11+
We added WebSocket support as a separate package to keep the Dart Frog package lightweight, containing only core functionality. In the future, we may add additional packages in order to extend the Dart Frog ecosystem without bloating the core.
12+
:::
13+
14+
## Installation
15+
16+
To get started, add the `dart_frog_web_socket` package as a dependency to your existing Dart Frog project:
17+
18+
```sh
19+
dart pub add dart_frog_web_socket
20+
```
21+
22+
## Creating a WebSocket Handler
23+
24+
We can use the `webSocketHandler` from `package:dart_frog_web_socket` to manage WebSocket connections.
25+
26+
You can either create a new route handler or integrate with an existing handler. For simplicity, we'll first take a look at adding a new route handler specifically for WebSocket connections.
27+
28+
Start by creating a new route, `routes/ws.dart`:
29+
30+
```dart
31+
import 'package:dart_frog/dart_frog.dart';
32+
33+
Response onRequest(RequestContext context) {
34+
return Response();
35+
}
36+
```
37+
38+
Next, instead of handling the request directly, we can create a WebSocket handler:
39+
40+
```dart
41+
import 'package:dart_frog/dart_frog.dart';
42+
import 'package:dart_frog_web_socket/dart_frog_web_socket.dart';
43+
44+
Future<Response> onRequest(RequestContext context) async {
45+
final handler = webSocketHandler((channel, protocol) {
46+
// TODO: react to new connections.
47+
});
48+
return handler(context);
49+
}
50+
```
51+
52+
:::note
53+
We need to refactor the request handler to be `async` and return a `Future<Response>` when using the `webSocketHandler`.
54+
:::
55+
56+
The `webSocketHandler` will handle upgrading `HTTP` requests to WebSocket connections and provides an `onConnection` callback which exposes the `WebSocketChannel` as well as an optional subprotocol.
57+
58+
## Receiving Messages from the Client
59+
60+
Next, we can subscribe to the stream of messages exposed by the `WebSocketChannel`:
61+
62+
```dart
63+
import 'package:dart_frog/dart_frog.dart';
64+
import 'package:dart_frog_web_socket/dart_frog_web_socket.dart';
65+
66+
Future<Response> onRequest(RequestContext context) async {
67+
final handler = webSocketHandler((channel, protocol) {
68+
channel.stream.listen((message) {
69+
// Handle incoming client messages.
70+
print(message);
71+
});
72+
});
73+
return handler(context);
74+
}
75+
```
76+
77+
For simplicity, we are just printing all messages sent by connected clients.
78+
79+
Before moving on, let's verify that a client is able to establish a connection and send a message to our server. To do this, we'll create a simple client in Dart.
80+
81+
## Sending Messages from the Client
82+
83+
Create a new directory called `example` at the project root and create a `pubspec.yaml`:
84+
85+
```yaml
86+
name: example
87+
publish_to: none
88+
89+
environment:
90+
sdk: '>=2.18.0 <3.0.0'
91+
92+
dependencies:
93+
web_socket_channel: ^2.0.0
94+
```
95+
96+
Next, install the dependencies:
97+
98+
```sh
99+
dart pub get
100+
```
101+
102+
Now, create a `main.dart` with the following contents:
103+
104+
```dart
105+
import 'package:web_socket_channel/web_socket_channel.dart';
106+
107+
void main() {
108+
// Connect to the remote WebSocket endpoint.
109+
final uri = Uri.parse('ws://localhost:8080/ws');
110+
final channel = WebSocketChannel.connect(uri);
111+
112+
// Send a message to the server.
113+
channel.sink.add('hello');
114+
}
115+
```
116+
117+
We're using [`package:web_socket_channel`](https://pub.dev/packages/web_socket_channel) to connect to our Dart Frog `/ws` endpoint. We can then send messages to the server by calling `add` on the `WebSocketChannel` sink.
118+
119+
We can run our Dart Frog dev server now:
120+
121+
```sh
122+
dart_frog dev
123+
```
124+
125+
:::note
126+
Be sure to run `dart_frog dev` from the project root.
127+
:::
128+
129+
Once the server is running in a new terminal, we can run our example client to test the connection:
130+
131+
```sh
132+
dart example/main.dart
133+
```
134+
135+
We should see the message we sent in our server logs:
136+
137+
```
138+
βœ“ Running on http://localhost:8080 (1.3s)
139+
The Dart VM service is listening on http://127.0.0.1:8181/YKEF_nbwOpM=/
140+
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/YKEF_nbwOpM=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2FYKEF_nbwOpM%3D%2Fws
141+
[hotreload] Hot reload is enabled.
142+
hello
143+
```
144+
145+
## Sending Messages from the Server
146+
147+
Now, let's send a message back to the client from the server. We can do that by adding a message to the channel sink on the server just like we previously did on the client.
148+
149+
```dart
150+
import 'package:dart_frog/dart_frog.dart';
151+
import 'package:dart_frog_web_socket/dart_frog_web_socket.dart';
152+
153+
Future<Response> onRequest(RequestContext context) async {
154+
final handler = webSocketHandler((channel, protocol) {
155+
channel.stream.listen((message) {
156+
// Handle incoming client messages.
157+
print(message);
158+
});
159+
160+
// Send a message back to the client.
161+
channel.sink.add('hi');
162+
});
163+
return handler(context);
164+
}
165+
```
166+
167+
## Receiving Messages from the Server
168+
169+
Lastly, we need to update the client code to subscribe to messages from the server.
170+
171+
```dart
172+
import 'package:web_socket_channel/web_socket_channel.dart';
173+
174+
void main() {
175+
// Connect to the remote WebSocket endpoint.
176+
final uri = Uri.parse('ws://localhost:8080/ws');
177+
final channel = WebSocketChannel.connect(uri);
178+
179+
// Send a message to the server.
180+
channel.sink.add('hello');
181+
182+
// Subscribe to messages from the server.
183+
channel.stream.listen((message) {
184+
print(message);
185+
});
186+
}
187+
```
188+
189+
Once we save all the changes and let the server hot reload, we can run the client code again:
190+
191+
```sh
192+
dart example/main.dart
193+
```
194+
195+
And we should see the following output:
196+
197+
**Server**
198+
199+
```sh
200+
[hotreload] Hot reload is enabled.
201+
hello
202+
```
203+
204+
**Client**
205+
206+
```sh
207+
hi
208+
```
209+
210+
## Simplifying Things Further
211+
212+
To make things even simpler, we can refactor the route handler implementation in cases where the route is only managing WebSocket connections:
213+
214+
```dart
215+
import 'package:dart_frog/dart_frog.dart';
216+
import 'package:dart_frog_web_socket/dart_frog_web_socket.dart';
217+
218+
Handler get onRequest {
219+
return webSocketHandler((channel, protocol) {
220+
channel.stream.listen((message) {
221+
// Handle incoming client messages.
222+
print(message);
223+
});
224+
225+
// Send a message back to the client.
226+
channel.sink.add('hi');
227+
});
228+
}
229+
```
230+
231+
## Conclusion
232+
233+
That's it! πŸŽ‰
234+
235+
We've successfully established a WebSocket connection between the Dart Frog server and a client. We've also demonstrated how messages can be streamed between a server and one or more clients.

β€Ždocs/docs/overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ dart_frog build
8787

8888
βœ… Static File Support πŸ“
8989

90+
βœ… WebSocket Support πŸ”Œ
91+
9092
🚧 Generated Dart Client Package πŸ“¦
9193

9294
🚧 Generated API Documentation πŸ“”

β€Ždocs/docs/roadmap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ In the interest of transparency, we want to share high-level details of our road
4545
- [ ] CLI `new` command to generate new `routes` and `middleware`
4646
- [ ] Health Check endpoint for monitoring
4747
- [ ] Logger which can be configured to adhere to standard log formats (https://cloud.google.com/run/docs/logging)
48-
- [ ] Websocket support
48+
- [x] WebSocket support
4949
- [ ] VSCode/IntelliJ support for DartFrog
5050
- [ ] Create a new project
5151
- [ ] New Routes

0 commit comments

Comments
Β (0)