Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit ceaf0b8

Browse files
committed
add guides.
1 parent e60167b commit ceaf0b8

File tree

2 files changed

+552
-0
lines changed

2 files changed

+552
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
export const description =
2+
'Use the Nitric framework to easily build and deploy Go WebSocket applications for AWS, Azure or GCP'
3+
4+
export const title_meta =
5+
'Building your first WebSocket Application with Go and Nitric'
6+
7+
# Building your first WebSocket Application with Nitric
8+
9+
## What we'll be doing
10+
11+
1. Use Nitric to create a WebSocket endpoint
12+
2. Manage WebSocket connections using a Key-Value store
13+
3. Handle WebSocket events:
14+
- Register connections on connect
15+
- Remove connections on disconnect
16+
- Broadcast messages to all connected clients
17+
4. Run locally for testing
18+
5. Deploy to a cloud of your choice
19+
20+
## Prerequisites
21+
22+
- [Go](https://go.dev/dl/)
23+
- The [Nitric CLI](https://nitric.io/docs/installation)
24+
- An [AWS](https://aws.amazon.com), [GCP](https://cloud.google.com), or [Azure](https://azure.microsoft.com) account (_your choice_)
25+
26+
## Getting started
27+
28+
We'll start by creating a new project for our WebSocket application.
29+
30+
```bash
31+
nitric new my-websocket-app go-starter
32+
```
33+
34+
Next, open the project in your editor of choice.
35+
36+
```bash
37+
cd my-websocket-app
38+
```
39+
40+
Make sure all dependencies are resolved:
41+
42+
```bash
43+
go mod tidy
44+
```
45+
46+
The scaffolded project should have the following structure:
47+
48+
```text
49+
+--services/
50+
| +-- hello/
51+
| +-- main.go
52+
| ...
53+
+--nitric.yaml
54+
+--go.mod
55+
+--go.sum
56+
+--golang.dockerfile
57+
+--.gitignore
58+
+--README.md
59+
```
60+
61+
You can test the project to verify everything is working as expected:
62+
63+
```bash
64+
nitric start
65+
```
66+
67+
If everything is working as expected, you can now delete all files/folders in the `services/` folder. We'll create new services in this guide.
68+
69+
## Building the WebSocket Application
70+
71+
Let's begin by setting up the WebSocket application. First, create a new folder called `websockets` within the services directory. Inside this folder, add a file named `main.go`, and include the following code:
72+
73+
```go
74+
package main
75+
76+
import (
77+
"context"
78+
"fmt"
79+
80+
"github.com/nitrictech/go-sdk/nitric"
81+
"github.com/nitrictech/go-sdk/nitric/keyvalue"
82+
"github.com/nitrictech/go-sdk/nitric/websockets"
83+
)
84+
85+
func main() {
86+
// Create a WebSocket endpoint named "public".
87+
ws := nitric.NewWebsocket("public")
88+
89+
// Initialize a KV store named "connections" with Get, Set, and Delete permissions.
90+
connections := nitric.NewKv("connections").Allow(keyvalue.KvStoreGet, keyvalue.KvStoreSet, keyvalue.KvStoreDelete)
91+
92+
// Handle new WebSocket connections by storing the connection ID in the KV store.
93+
ws.On(websockets.EventType_Connect, func(ctx *websockets.Ctx) {
94+
err := connections.Set(context.TODO(), ctx.Request.ConnectionID(), map[string]interface{}{
95+
"connectionId": ctx.Request.ConnectionID(),
96+
})
97+
if err != nil {
98+
return
99+
}
100+
})
101+
102+
// Add event handlers here
103+
104+
nitric.Run()
105+
}
106+
```
107+
108+
Here we're creating:
109+
110+
- A WebSocket endpoint named `public`
111+
- A Key-Value store named `connections` to track WebSocket connections
112+
113+
From here, let's add some features to that function that allow us to manage connections and broadcast messages.
114+
115+
<Note>
116+
You could separate some or all of these event handlers into their own services
117+
if you prefer. For simplicity, we'll group them together in this guide.
118+
</Note>
119+
120+
### Register connections on connect
121+
122+
```go
123+
ws.On(websockets.EventType_Connect, func(ctx *websockets.Ctx) {
124+
err := connections.Set(context.TODO(), ctx.Request.ConnectionID(), map[string]interface{}{
125+
"connectionId": ctx.Request.ConnectionID(),
126+
})
127+
if err != nil {
128+
return
129+
}
130+
})
131+
```
132+
133+
### Remove connections on disconnect
134+
135+
```go
136+
ws.On(websockets.EventType_Disconnect, func(ctx *websockets.Ctx) {
137+
err := connections.Delete(context.TODO(), ctx.Request.ConnectionID())
138+
if err != nil {
139+
return
140+
}
141+
})
142+
```
143+
144+
### Broadcast messages to all connected clients
145+
146+
```go
147+
ws.On(websockets.EventType_Message, func(ctx *websockets.Ctx) {
148+
connectionStream, err := connections.Keys(context.TODO())
149+
if err != nil {
150+
return
151+
}
152+
senderId := ctx.Request.ConnectionID()
153+
154+
for {
155+
connectionId, err := connectionStream.Recv()
156+
if err != nil {
157+
break
158+
}
159+
160+
if connectionId == senderId {
161+
continue
162+
}
163+
164+
message := fmt.Sprintf("%s: %s", senderId, ctx.Request.Message())
165+
err = ws.Send(context.TODO(), connectionId, []byte(message))
166+
if err != nil {
167+
return
168+
}
169+
}
170+
})
171+
```
172+
173+
### Bringing it all together
174+
175+
<details>
176+
<summary>Your code should look like this:</summary>
177+
178+
```go
179+
package main
180+
181+
import (
182+
"context"
183+
"fmt"
184+
185+
"github.com/nitrictech/go-sdk/nitric"
186+
"github.com/nitrictech/go-sdk/nitric/keyvalue"
187+
"github.com/nitrictech/go-sdk/nitric/websockets"
188+
)
189+
190+
func main() {
191+
// Create a WebSocket endpoint named "public".
192+
ws := nitric.NewWebsocket("public")
193+
194+
// Initialize a KV store named "connections" with Get, Set, and Delete permissions.
195+
connections := nitric.NewKv("connections").Allow(keyvalue.KvStoreGet, keyvalue.KvStoreSet, keyvalue.KvStoreDelete)
196+
197+
// Handle new WebSocket connections by storing the connection ID in the KV store.
198+
ws.On(websockets.EventType_Connect, func(ctx *websockets.Ctx) {
199+
err := connections.Set(context.TODO(), ctx.Request.ConnectionID(), map[string]interface{}{
200+
"connectionId": ctx.Request.ConnectionID(),
201+
})
202+
if err != nil {
203+
return
204+
}
205+
})
206+
207+
ws.On(websockets.EventType_Connect, func(ctx *websockets.Ctx) {
208+
err := connections.Set(context.TODO(), ctx.Request.ConnectionID(), map[string]interface{}{
209+
"connectionId": ctx.Request.ConnectionID(),
210+
})
211+
if err != nil {
212+
return
213+
}
214+
})
215+
216+
ws.On(websockets.EventType_Disconnect, func(ctx *websockets.Ctx) {
217+
err := connections.Delete(context.TODO(), ctx.Request.ConnectionID())
218+
if err != nil {
219+
return
220+
}
221+
})
222+
223+
ws.On(websockets.EventType_Message, func(ctx *websockets.Ctx) {
224+
connectionStream, err := connections.Keys(context.TODO())
225+
if err != nil {
226+
return
227+
}
228+
229+
senderId := ctx.Request.ConnectionID()
230+
231+
for {
232+
connectionId, err := connectionStream.Recv()
233+
if err != nil {
234+
break
235+
}
236+
237+
if connectionId == senderId {
238+
continue
239+
}
240+
241+
message := fmt.Sprintf("%s: %s", senderId, ctx.Request.Message())
242+
err = ws.Send(context.TODO(), connectionId, []byte(message))
243+
if err != nil {
244+
return
245+
}
246+
}
247+
})
248+
249+
nitric.Run()
250+
}
251+
```
252+
253+
</details>
254+
255+
Do a quick `go mod tidy` to make sure all new dependencies are resolved.
256+
257+
## Ok, let's run this thing!
258+
259+
Now that you have your WebSocket application defined with handlers for each event, it's time to test it locally.
260+
261+
```bash
262+
nitric start
263+
```
264+
265+
Once it starts, the application will be ready to accept WebSocket connections. You can use a WebSocket client like Postman or any other WebSocket tool to test the application.
266+
267+
We will keep it running for our tests. If you want to update your services, just save them, and they'll be reloaded automatically.
268+
269+
## Deploy to the cloud
270+
271+
At this point, you can deploy what you've built to any of the supported cloud providers. To do this, start by setting up your credentials and any configuration for the cloud you prefer:
272+
273+
- [AWS](/reference/providers/aws)
274+
- [Azure](/reference/providers/azure)
275+
- [GCP](/reference/providers/gcp)
276+
277+
Next, we'll need to create a `stack`. A stack represents a deployed instance of an application, which is a key value store of resources defined in your project. You might want separate stacks for each environment, such as stacks for `dev`, `test`, and `prod`. For now, let's start by creating a `dev` stack.
278+
279+
The `stack new` command below will create a stack named `dev` that uses the `aws` provider.
280+
281+
```bash
282+
nitric stack new dev aws
283+
```
284+
285+
Continue by checking your stack file `nitric.dev.yaml` and adding in your preferred region. Let's use `us-east-1`.
286+
287+
### AWS
288+
289+
Note: You are responsible for staying within the limits of the free tier or any costs associated with deployment.
290+
291+
We called our stack `dev`. Let's try deploying it with the `up` command:
292+
293+
```bash
294+
nitric up
295+
```
296+
297+
When the deployment is complete, go to the relevant cloud console and you'll be able to see and interact with your WebSocket application.
298+
299+
To tear down your application from the cloud, use the `down` command:
300+
301+
```bash
302+
nitric down
303+
```
304+
305+
## Summary
306+
307+
In this guide, we've created a serverless WebSocket application using Go and Nitric. We've demonstrated how to set up WebSocket connections, track clients using a Key-Value store, and broadcast messages to all connected clients. This application can be easily deployed to the cloud, allowing you to build scalable, real-time communication systems.
308+
309+
For more information and advanced usage, refer to the [Nitric documentation](https://nitric.io/docs).

0 commit comments

Comments
 (0)