Skip to content

Commit

Permalink
docs: realtime example
Browse files Browse the repository at this point in the history
  • Loading branch information
jayair committed Sep 17, 2024
1 parent 793d7bf commit 6a0f6b5
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 61 deletions.
15 changes: 10 additions & 5 deletions examples/aws-realtime-nextjs/authorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import { realtime } from "sst/aws/realtime";
export const handler = realtime.authorizer(async (token) => {
const prefix = `${Resource.App.name}/${Resource.App.stage}`;

// Validate token
const isValid = token === "PLACEHOLDER_TOKEN";

return {
publish: [`${prefix}/*`],
subscribe: [`${prefix}/*`],
};
return isValid
? {
publish: [`${prefix}/*`],
subscribe: [`${prefix}/*`],
}
: {
publish: [],
subscribe: [],
};
});
32 changes: 12 additions & 20 deletions examples/aws-realtime-nextjs/components/chat.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
"use client";

import mqtt from "mqtt";
import { useState, useEffect } from "react";
import { iot, mqtt } from "aws-iot-device-sdk-v2";
import styles from "./chat.module.css";

function createConnection(endpoint: string, authorizer: string) {
const client = new mqtt.MqttClient();
const id = window.crypto.randomUUID();

return client.new_connection(
iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets()
.with_clean_session(true)
.with_client_id(`client_${id}`)
.with_endpoint(endpoint)
.with_custom_authorizer("", authorizer, "", "PLACEHOLDER_TOKEN")
.build()
);
return mqtt.connect(`wss://${endpoint}/mqtt?x-amz-customauthorizer-name=${authorizer}`, {
protocolVersion: 5,
manualConnect: true,
username: "", // Must be empty for the authorizer
password: "PLACEHOLDER_TOKEN", // Passed as the token to the authorizer
clientId: `client_${window.crypto.randomUUID()}`,
});
}

export default function Chat(
{ topic, endpoint, authorizer }: { topic: string, endpoint: string, authorizer: string }
) {
const [messages, setMessages] = useState<string[]>([]);
const [connection, setConnection] = useState<mqtt.MqttClientConnection | null>(null);
const [connection, setConnection] = useState<mqtt.MqttClient | null>(null);

useEffect(() => {
const connection = createConnection(endpoint, authorizer);

connection.on("connect", async () => {
try {
await connection.subscribe(topic, mqtt.QoS.AtLeastOnce);
await connection.subscribeAsync(topic, { qos: 1 });
setConnection(connection);
} catch (e) { }
});
Expand All @@ -42,7 +38,7 @@ export default function Chat(
connection.connect();

return () => {
connection.disconnect();
connection.end();
setConnection(null);
};
}, [topic, endpoint, authorizer]);
Expand All @@ -63,11 +59,7 @@ export default function Chat(

const input = (e.target as HTMLFormElement).message;

connection!.publish(
topic,
input.value,
mqtt.QoS.AtLeastOnce
);
connection!.publish(topic, input.value, { qos: 1 });
input.value = "";
}}
>
Expand Down
2 changes: 1 addition & 1 deletion examples/aws-realtime-nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "next start"
},
"dependencies": {
"aws-iot-device-sdk-v2": "^1.19.5",
"mqtt": "^5.10.1",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",
Expand Down
15 changes: 10 additions & 5 deletions examples/aws-realtime-nextjs/sst-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
import "sst"
export {}
declare module "sst" {
export interface Resource {
MyRealtime: {
authorizer: string
endpoint: string
type: "sst.aws.Realtime"
"MyRealtime": {
"authorizer": string
"endpoint": string
"type": "sst.aws.Realtime"
}
"MyWeb": {
"type": "sst.aws.Nextjs"
"url": string
}
}
}
export {}
63 changes: 33 additions & 30 deletions www/src/content/docs/docs/start/aws/realtime.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,27 @@ import { realtime } from "sst/aws/realtime";
export const handler = realtime.authorizer(async (token) => {
const prefix = `${Resource.App.name}/${Resource.App.stage}`;

// Validate token

return {
publish: [`${prefix}/*`],
subscribe: [`${prefix}/*`],
};
const isValid = token === "PLACEHOLDER_TOKEN";

return isValid
? {
publish: [`${prefix}/*`],
subscribe: [`${prefix}/*`],
}
: {
publish: [],
subscribe: [],
};
});
```

Here we are saying that a user has access to publish and subscribe to any topic namespaced user the app and stage name. In production, we would validate the given token against our database or auth provider.
Here we are saying that a user with a valid token has access to publish and subscribe to any topic namespaced user the app and stage name.

:::tip
Namespace your topics with the app and stage name to keep them unique.
:::

In production, we would validate the given token against our database or auth provider.

---

Expand All @@ -100,15 +111,15 @@ Now let's create a chat interface in our app. Create a new component in `compone
```tsx title="components/chat.tsx" {29-33}
"use client";

import mqtt from "mqtt";
import { useState, useEffect } from "react";
import { iot, mqtt } from "aws-iot-device-sdk-v2";
import styles from "./chat.module.css";

export default function Chat(
{ topic, endpoint, authorizer }: { topic: string, endpoint: string, authorizer: string }
) {
const [messages, setMessages] = useState<string[]>([]);
const [connection, setConnection] = useState<mqtt.MqttClientConnection | null>(null);
const [connection, setConnection] = useState<mqtt.MqttClient | null>(null);

return (
<div className={styles.chat}>
Expand All @@ -126,11 +137,7 @@ export default function Chat(

const input = (e.target as HTMLFormElement).message;

connection!.publish(
topic,
input.value,
mqtt.QoS.AtLeastOnce
);
connection!.publish(topic, input.value, { qos: 1 });
input.value = "";
}}
>
Expand Down Expand Up @@ -207,10 +214,10 @@ Add some styles.
}
```

Install the npm packages.
Install the npm package.

```bash
npm install aws-iot-device-sdk-v2
npm install mqtt
```

---
Expand Down Expand Up @@ -257,7 +264,7 @@ export default function Home() {
We are directly accessing our Realtime component with `Resource.MyRealtime.*`.
:::

Here we are going to publish and subscribe to a _topic_ called `sst-chat` and it's namespaced under the name of the app and the stage our app is deployed to.
Here we are going to publish and subscribe to a _topic_ called `sst-chat`, namespaced under the name of the app and the stage our app is deployed to.

---

Expand All @@ -267,17 +274,13 @@ When our chat component loads, it'll create a new connection to our realtime ser

```ts title="components/chat.tsx"
function createConnection(endpoint: string, authorizer: string) {
const client = new mqtt.MqttClient();
const id = window.crypto.randomUUID();

return client.new_connection(
iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets()
.with_clean_session(true)
.with_client_id(`client_${id}`)
.with_endpoint(endpoint)
.with_custom_authorizer("", authorizer, "", "PLACEHOLDER_TOKEN")
.build()
);
return mqtt.connect(`wss://${endpoint}/mqtt?x-amz-customauthorizer-name=${authorizer}`, {
protocolVersion: 5,
manualConnect: true,
username: "", // Must be empty for the authorizer
password: "PLACEHOLDER_TOKEN", // Passed as the token to the authorizer
clientId: `client_${window.crypto.randomUUID()}`,
});
}
```

Expand All @@ -291,7 +294,7 @@ useEffect(() => {

connection.on("connect", async () => {
try {
await connection.subscribe(topic, mqtt.QoS.AtLeastOnce);
await connection.subscribeAsync(topic, { qos: 1 });
setConnection(connection);
} catch (e) { }
});
Expand All @@ -304,7 +307,7 @@ useEffect(() => {
connection.connect();

return () => {
connection.disconnect();
connection.end();
setConnection(null);
};
}, [topic, endpoint, authorizer]);
Expand Down

0 comments on commit 6a0f6b5

Please sign in to comment.