Websockets guide
In this guide we'll be creating a simple realtime application using Nitric websockets.
This guide relies on the Websocket feature of Nitric which is currently in preview. See the docs for this feature to see how to enable it.
Prerequisites
To complete this guide you'll need the following:
- Node.js installed locally
- Nitric CLI installed
- (optional) Your choice of an AWS, GCP or Azure account
Getting Started
Let's start by setting up a Nitric project:
nitric new websocket-example ts-starter
Install dependencies:
cd websocket-example
yarn install
You can go ahead and open this new project in your editor of choice. You should see a project structure similar to:
├── services
│ ├── hello.ts
├── node_modules
│ ├── ...
├── .gitignore
├── nitric.yaml
├── package.json
├── tsconfig.json
├── README.md
└── yarn.lock
In this structure you'll notice the services
folder. By default, this is where Nitric expects the entrypoint code for your application. However, that's just a convention, we can change that to anything else that suits our needs.
Let's update our hello.ts
file with some websocket code to get started.
import { websocket } from '@nitric/sdk'
const socket = websocket('example-websocket')
socket.on('connect', async (ctx) => {
console.log(`connecting: ${ctx.req.connectionId}`)
})
socket.on('disconnect', async (ctx) => {
console.log(`disconnecting: ${ctx.req.connectionId}`)
})
socket.on('message', async (ctx) => {
const message = ctx.req.text()
console.log(`got message from ${ctx.req.connectionId}: ${message}`)
})
At this point, we're ready to start testing locally. Let's start the local Nitric environment.
nitric start
Your websocket will now be running with Nitric acting as a proxy, in this case it's available on port 4001
.
In this guide we'll test this using Insomnia, however feel free to use any websocket capable testing client you like.
When you send messages you will start seeing your server log messages.
Sending messages from server to clients
Websockets only make sense when communication is bi-directional, now we've confirmed that the client can talk to our server lets get our server talking to the client.
To do this we'll need to add some connection management.
You can update the hello.ts
file like so:
import { websocket, kv } from '@nitric/sdk'
// Initialize KV store for connections and a WebSocket
const kvStore = kv('connections').for('getting', 'setting', 'deleting')
const socket = websocket('example-websocket')
// Helper function to get current connections
async function getCurrentConnections() {
try {
const serializedList = await kvStore.get('connections')
return serializedList && serializedList['ids']
? JSON.parse(serializedList['ids'])
: []
} catch (error) {
console.error('Error getting current connections:', error)
return []
}
}
// Helper function to update connections list
async function updateConnections(connections) {
try {
const updatedSerializedList = JSON.stringify(connections)
await kvStore.set('connections', { ids: updatedSerializedList })
} catch (error) {
console.error('Error updating connections:', error)
}
}
// Handle new connections
socket.on('connect', async (ctx) => {
const connections = await getCurrentConnections()
connections.push(ctx.req.connectionId)
await updateConnections(connections)
})
// Handle messages
socket.on('message', async (ctx) => {
const message = ctx.req.text()
const connections = await getCurrentConnections()
// Send the message to each connection
connections.forEach(async (connectionId) => {
try {
if (connectionId !== ctx.req.connectionId) {
await socket.send(connectionId, message)
}
} catch (error) {
console.error(`Error sending message to ${connectionId}:`, error)
}
})
})
// Handle disconnections
socket.on('disconnect', async (ctx) => {
const connections = await getCurrentConnections()
const index = connections.indexOf(ctx.req.connectionId)
if (index > -1) {
connections.splice(index, 1)
await updateConnections(connections)
}
})
If you connect multiple clients using your preferred client and send messages each client should receive messages from other clients:
Deploy to the cloud
To perform the deployment we'll create a stack
, stacks give Nitric the configuration needed for a specific cloud instance of this project, such as the provider and region.
The new stack command can help you create the stack by following prompts.
nitric stack new
This command will create a file named nitric.dev.yaml
, with contents like this:
provider: nitric/aws@1.0.0
region: us-east-1
With the stack file in place we can run the deployment:
nitric up
Go ahead and test your app in the cloud, you can start by connecting wscat
to the websocket endpoint printed in the output for up
.
You'll need to add
$default
to the URL provided to hit the deployed stage. This will be simplified in future versions of the AWS provider.
When you're done with the cloud deployment you can tear it down using the
nitric down
command.
What next?
Now that you have the basics down, try exploring other Nitric resources available to enhance your app.