From d664755b56327373bd3ab4efd081f305f41e3cd7 Mon Sep 17 00:00:00 2001 From: Dikran Samarjian Date: Wed, 5 Aug 2026 15:40:08 -0700 Subject: [PATCH 1/2] formatting updates --- docs/capabilities/http-fetch.md | 63 ++++++++----------- .../notifications/pn-best-practices.md | 13 ++-- docs/capabilities/server/http-fetch.mdx | 4 +- .../version-0.12/capabilities/http-fetch.md | 63 ++++++++----------- .../capabilities/server/http-fetch.mdx | 4 +- .../version-0.13/capabilities/http-fetch.md | 63 ++++++++----------- .../notifications/pn-best-practices.md | 13 ++-- .../capabilities/server/http-fetch.mdx | 4 +- 8 files changed, 101 insertions(+), 126 deletions(-) diff --git a/docs/capabilities/http-fetch.md b/docs/capabilities/http-fetch.md index 71971442..8d5cd30f 100644 --- a/docs/capabilities/http-fetch.md +++ b/docs/capabilities/http-fetch.md @@ -6,18 +6,15 @@ Your Devvit app can make network requests to access allow-listed external domain ## Enabling HTTP fetch calls -devvit.json - -``` - +```json title="devvit.json" { - ... - "permissions": { - "http": { - "enable": true, - "domains": ["my-site.com", "another-domain.net"] - } - } + ... + "permissions": { + "http": { + "enable": true, + "domains": ["my-site.com", "another-domain.net"] + } + } } ``` @@ -56,15 +53,12 @@ Devvit Web applications have two different contexts for using fetch: Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): -server/index.ts - -``` - +```ts title="server/index.ts" const response = await fetch('https://example.com/api/data', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, }); const data = await response.json(); @@ -78,22 +72,20 @@ Client-side fetch has different restrictions: - **Domain limitation**: Can only make requests to your own webview domain - **Endpoint requirement**: All requests must target endpoints that start with /api/ - **Authentication**: Handled automatically \- no need to manage auth tokens -- **No external domains**: Cannot make requests to external domains from client-side code - client/index.ts - -``` +- **No external domains**: Cannot make requests to external domains from client-side code +```ts title="client/index.ts" const handleFetchData = async () => { - // ✅ Correct: Fetching your own webview's API endpoint - const response = await fetch("/api/user-data", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = await response.json(); - console.log("API response:", data); + // ✅ Correct: Fetching your own webview's API endpoint + const response = await fetch("/api/user-data", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + console.log("API response:", data); }; // ❌ Incorrect: Cannot fetch external domains from client-side @@ -107,7 +99,7 @@ const handleFetchData = async () => { The following error means HTTP Fetch requests are hitting the internal timeout limits. -``` +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` @@ -191,8 +183,7 @@ If your app uses fetch domains, add this context to your app's [README](../devvi Example Fetch Domains section: -``` - +```md title="README.md" ## Fetch Domains The following domains are requested for this app: diff --git a/docs/capabilities/notifications/pn-best-practices.md b/docs/capabilities/notifications/pn-best-practices.md index 22b59f48..248b05c2 100644 --- a/docs/capabilities/notifications/pn-best-practices.md +++ b/docs/capabilities/notifications/pn-best-practices.md @@ -127,13 +127,13 @@ To learn more about creating deeper engagement loops, check out the best practic In your terminal, navigate to your project directory and run this command to update the push notification to the latest release. -``` +```bash npm install @devvit/notifications ``` ### Step 2: Import the push notification module -``` +```ts import { notifications } from '@devvit/notifications'; ``` @@ -144,7 +144,7 @@ import { notifications } from '@devvit/notifications'; To send a push notification to a group of users, you can use the double curly brackets ( { { } } ) to reference variables in a Mustache template. -``` +```ts await notifications.enqueue({ title: 'Hello {{name}}!', body: 'You have {{score}} new points.', @@ -179,7 +179,7 @@ await notifications.enqueue({ **Note:** Mustache templating is optional. Here's a simplified example without it: -``` +```ts await notifications.enqueue({ title: 'Winner!', body: 'Congrats on your win', @@ -189,6 +189,7 @@ await notifications.enqueue({ link: 't3_xyz987', }, ], +}); ``` **Note**: If the app hasn’t been published, you can only send push notifications to yourself for testing. **Pre-release apps in testing are not subject to the rate-limits below**. @@ -204,14 +205,14 @@ If you need higher limits, let us know. Users will be able to opt in or out of receiving notifications triggered by a button in your UI: -``` +```ts await notifications.optInCurrentUser(); await notifications.optOutCurrentUser(); ``` You will also be able to retrieve a list of users who have opted in (if not managing it manually): -``` +```ts //This will just return the first 1000 users const recipients = await notifications.listOptedInUsers(); diff --git a/docs/capabilities/server/http-fetch.mdx b/docs/capabilities/server/http-fetch.mdx index fbe1b54c..dc30c956 100644 --- a/docs/capabilities/server/http-fetch.mdx +++ b/docs/capabilities/server/http-fetch.mdx @@ -61,7 +61,7 @@ const response = await fetch('https://example.com/api/data', { const data = await response.json(); console.log('External API response:', data); -```` +``` ### Client-side fetch @@ -106,7 +106,7 @@ If you see the following error, it means HTTP Fetch requests are hitting the int - Use a queue or kick off an async request in your back end. You can use [Scheduler](./scheduler.mdx) to monitor the result. - Optimize the overall HTTP request latency if you have a self-hosted server. -```ts +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` diff --git a/versioned_docs/version-0.12/capabilities/http-fetch.md b/versioned_docs/version-0.12/capabilities/http-fetch.md index 7683fd9d..55cab0e3 100644 --- a/versioned_docs/version-0.12/capabilities/http-fetch.md +++ b/versioned_docs/version-0.12/capabilities/http-fetch.md @@ -6,18 +6,15 @@ Your Devvit app can make network requests to access allow-listed external domain ## Enabling HTTP fetch calls -devvit.json - -``` - +```json title="devvit.json" { - ... - "permissions": { - "http": { - "enable": true, - "domains": ["my-site.com", "another-domain.net"] - } - } + ... + "permissions": { + "http": { + "enable": true, + "domains": ["my-site.com", "another-domain.net"] + } + } } ``` @@ -56,15 +53,12 @@ Devvit Web applications have two different contexts for using fetch: Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): -server/index.ts - -``` - +```ts title="server/index.ts" const response = await fetch('https://example.com/api/data', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, }); const data = await response.json(); @@ -78,22 +72,20 @@ Client-side fetch has different restrictions: - **Domain limitation**: Can only make requests to your own webview domain - **Endpoint requirement**: All requests must target endpoints that start with /api/ - **Authentication**: Handled automatically \- no need to manage auth tokens -- **No external domains**: Cannot make requests to external domains from client-side code - client/index.ts - -``` +- **No external domains**: Cannot make requests to external domains from client-side code +```ts title="client/index.ts" const handleFetchData = async () => { - // ✅ Correct: Fetching your own webview's API endpoint - const response = await fetch("/api/user-data", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = await response.json(); - console.log("API response:", data); + // ✅ Correct: Fetching your own webview's API endpoint + const response = await fetch("/api/user-data", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + console.log("API response:", data); }; // ❌ Incorrect: Cannot fetch external domains from client-side @@ -107,7 +99,7 @@ const handleFetchData = async () => { The following error means HTTP Fetch requests are hitting the internal timeout limits. -``` +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` @@ -191,8 +183,7 @@ If your app uses fetch domains, you must add context to your app's README for th Example README section: -``` - +```md title="README.md" ## Fetch Domains The following domains are requested for this app: diff --git a/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx b/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx index fbe1b54c..dc30c956 100644 --- a/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx +++ b/versioned_docs/version-0.12/capabilities/server/http-fetch.mdx @@ -61,7 +61,7 @@ const response = await fetch('https://example.com/api/data', { const data = await response.json(); console.log('External API response:', data); -```` +``` ### Client-side fetch @@ -106,7 +106,7 @@ If you see the following error, it means HTTP Fetch requests are hitting the int - Use a queue or kick off an async request in your back end. You can use [Scheduler](./scheduler.mdx) to monitor the result. - Optimize the overall HTTP request latency if you have a self-hosted server. -```ts +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` diff --git a/versioned_docs/version-0.13/capabilities/http-fetch.md b/versioned_docs/version-0.13/capabilities/http-fetch.md index 71971442..8d5cd30f 100644 --- a/versioned_docs/version-0.13/capabilities/http-fetch.md +++ b/versioned_docs/version-0.13/capabilities/http-fetch.md @@ -6,18 +6,15 @@ Your Devvit app can make network requests to access allow-listed external domain ## Enabling HTTP fetch calls -devvit.json - -``` - +```json title="devvit.json" { - ... - "permissions": { - "http": { - "enable": true, - "domains": ["my-site.com", "another-domain.net"] - } - } + ... + "permissions": { + "http": { + "enable": true, + "domains": ["my-site.com", "another-domain.net"] + } + } } ``` @@ -56,15 +53,12 @@ Devvit Web applications have two different contexts for using fetch: Server-side fetch allows your app to make HTTP requests to allowlisted external domains from your server-side code (e.g., API routes, server actions): -server/index.ts - -``` - +```ts title="server/index.ts" const response = await fetch('https://example.com/api/data', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, }); const data = await response.json(); @@ -78,22 +72,20 @@ Client-side fetch has different restrictions: - **Domain limitation**: Can only make requests to your own webview domain - **Endpoint requirement**: All requests must target endpoints that start with /api/ - **Authentication**: Handled automatically \- no need to manage auth tokens -- **No external domains**: Cannot make requests to external domains from client-side code - client/index.ts - -``` +- **No external domains**: Cannot make requests to external domains from client-side code +```ts title="client/index.ts" const handleFetchData = async () => { - // ✅ Correct: Fetching your own webview's API endpoint - const response = await fetch("/api/user-data", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const data = await response.json(); - console.log("API response:", data); + // ✅ Correct: Fetching your own webview's API endpoint + const response = await fetch("/api/user-data", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + const data = await response.json(); + console.log("API response:", data); }; // ❌ Incorrect: Cannot fetch external domains from client-side @@ -107,7 +99,7 @@ const handleFetchData = async () => { The following error means HTTP Fetch requests are hitting the internal timeout limits. -``` +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` @@ -191,8 +183,7 @@ If your app uses fetch domains, add this context to your app's [README](../devvi Example Fetch Domains section: -``` - +```md title="README.md" ## Fetch Domains The following domains are requested for this app: diff --git a/versioned_docs/version-0.13/capabilities/notifications/pn-best-practices.md b/versioned_docs/version-0.13/capabilities/notifications/pn-best-practices.md index 22b59f48..248b05c2 100644 --- a/versioned_docs/version-0.13/capabilities/notifications/pn-best-practices.md +++ b/versioned_docs/version-0.13/capabilities/notifications/pn-best-practices.md @@ -127,13 +127,13 @@ To learn more about creating deeper engagement loops, check out the best practic In your terminal, navigate to your project directory and run this command to update the push notification to the latest release. -``` +```bash npm install @devvit/notifications ``` ### Step 2: Import the push notification module -``` +```ts import { notifications } from '@devvit/notifications'; ``` @@ -144,7 +144,7 @@ import { notifications } from '@devvit/notifications'; To send a push notification to a group of users, you can use the double curly brackets ( { { } } ) to reference variables in a Mustache template. -``` +```ts await notifications.enqueue({ title: 'Hello {{name}}!', body: 'You have {{score}} new points.', @@ -179,7 +179,7 @@ await notifications.enqueue({ **Note:** Mustache templating is optional. Here's a simplified example without it: -``` +```ts await notifications.enqueue({ title: 'Winner!', body: 'Congrats on your win', @@ -189,6 +189,7 @@ await notifications.enqueue({ link: 't3_xyz987', }, ], +}); ``` **Note**: If the app hasn’t been published, you can only send push notifications to yourself for testing. **Pre-release apps in testing are not subject to the rate-limits below**. @@ -204,14 +205,14 @@ If you need higher limits, let us know. Users will be able to opt in or out of receiving notifications triggered by a button in your UI: -``` +```ts await notifications.optInCurrentUser(); await notifications.optOutCurrentUser(); ``` You will also be able to retrieve a list of users who have opted in (if not managing it manually): -``` +```ts //This will just return the first 1000 users const recipients = await notifications.listOptedInUsers(); diff --git a/versioned_docs/version-0.13/capabilities/server/http-fetch.mdx b/versioned_docs/version-0.13/capabilities/server/http-fetch.mdx index fbe1b54c..dc30c956 100644 --- a/versioned_docs/version-0.13/capabilities/server/http-fetch.mdx +++ b/versioned_docs/version-0.13/capabilities/server/http-fetch.mdx @@ -61,7 +61,7 @@ const response = await fetch('https://example.com/api/data', { const data = await response.json(); console.log('External API response:', data); -```` +``` ### Client-side fetch @@ -106,7 +106,7 @@ If you see the following error, it means HTTP Fetch requests are hitting the int - Use a queue or kick off an async request in your back end. You can use [Scheduler](./scheduler.mdx) to monitor the result. - Optimize the overall HTTP request latency if you have a self-hosted server. -```ts +```text HTTP request to domain: timed out with error: context deadline exceeded. ``` From 417cd8ce60087b4bdf8d00e919965cec0f0fd8f9 Mon Sep 17 00:00:00 2001 From: Dikran Samarjian Date: Wed, 5 Aug 2026 15:58:45 -0700 Subject: [PATCH 2/2] updated hono/express example --- .../capabilities/analytics/devvit-journeys.md | 62 ++++++++++++++++--- .../capabilities/analytics/devvit-journeys.md | 62 ++++++++++++++++--- 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/docs/capabilities/analytics/devvit-journeys.md b/docs/capabilities/analytics/devvit-journeys.md index b383a518..93af6985 100644 --- a/docs/capabilities/analytics/devvit-journeys.md +++ b/docs/capabilities/analytics/devvit-journeys.md @@ -1,3 +1,6 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Devvit Journeys Devvit Journeys adds a telemetry stream to your app that tracks the entire lifecycle of a user session. With journeys, you can: @@ -85,7 +88,7 @@ By instrumenting these moments, you can track session boundaries, understand pla | :---: | ------------------------------------- | ----------------------- | ----------------------------- | | **1** | Game loads | **App.Ready** | Game is fully interactive | | **2** | Player clicks “Start Game” | **Journey.Start** | Begins a new session | -| **3** | Player completes Level 1 (of 5 levels) | **Journey.Progress** | `progress: 0.2` | +| **3** | Player completes Level 1 (of 5 levels) | **Journey.Progress** | `progress: 0.2` | | **4** | Player opens inventory | **Journey.Interaction** | `action: "menu_opened"` | | **5** | Player completes Level 2 | **Journey.Progress** | `progress: 0.4` | | **6** | Player reaches final level | **Journey.Progress** | `progress: 0.9` | @@ -98,7 +101,7 @@ By instrumenting these moments, you can track session boundaries, understand pla | **1** | Game loads | **App.Ready** | Game is fully interactive | | **2** | Player clicks “Start Game” | **Journey.Start** | Begins a new session | | **3** | Player completes early level | **Journey.Progress** | `progress: 0.3` | -| **4** | Player dies | **Journey.End** | `complete: false`, `win: false` | +| **4** | Player dies | **Journey.End** | `complete: false`, `win: false` | ### Scenario 3: early exit / abandonment @@ -149,7 +152,7 @@ Here's how to implement journey tracking in your app. Set Journeys permissions to `true` in `devvit.json`. -``` +```json title="devvit.json" "permissions": { "journeys": true }, @@ -159,7 +162,7 @@ Set Journeys permissions to `true` in `devvit.json`. You can send events solely on the backend and use the front‑end only to establish and pass along the journeyId. To do this, thread the active `journey ID` from your front‑end to your backend routes. -``` +```ts title="client/index.ts" import { telemetry } from '@devvit/analytics/client/reddit'; export async function submitScore(score: number): Promise { @@ -184,7 +187,50 @@ export async function submitScore(score: number): Promise { On the server, read the incoming `journeyId` and use it for correlation in your own route. + + + +```ts title="server/index.ts" +import { telemetry } from '@devvit/analytics/server/reddit'; +import { Hono } from 'hono'; + +const app = new Hono(); + +app.post('/api/score', async (c) => { + const journeyId = c.req.header('x-devvit-journey-id') ?? ''; + const { score } = await c.req.json<{ score: number }>(); + + console.log('score event', { + journeyId, + score, + }); + + await telemetry.endJourney({ + journeyId, + complete: true, + game: { + win: true, + score, + }, + }); + + return c.json({ ok: true }); +}); + +export default app; ``` + + + + +```ts title="server/index.ts" import express from 'express'; import { telemetry } from '@devvit/analytics/server/reddit'; @@ -209,16 +255,18 @@ app.post('/api/score', async (req, res) => { res.json({ ok: true }); }); - ``` + + + ### Client events If you don’t want to manually send server-events, you can use the generic client side events. In this case, the `JourneyId` is handled. In this case, you won’t need to pass a `JourneyId` when calling progress and so forth. You also won’t need `telemetry.getActiveJourneyId()` unless you’re curious about that data. Note: This also requires using the route adapters provided in `@devvit/analytics/server/reddit` -``` +```ts title="client/index.ts" // client import { telemetry } from '@devvit/analytics/client/reddit'; @@ -231,7 +279,7 @@ await telemetry.progress({ ``` -``` +```ts title="server/index.ts" // server import { createTelemetryRouter } from '@devvit/analytics/server/reddit'; app.use(createTelemetryRouter()); diff --git a/versioned_docs/version-0.13/capabilities/analytics/devvit-journeys.md b/versioned_docs/version-0.13/capabilities/analytics/devvit-journeys.md index b383a518..93af6985 100644 --- a/versioned_docs/version-0.13/capabilities/analytics/devvit-journeys.md +++ b/versioned_docs/version-0.13/capabilities/analytics/devvit-journeys.md @@ -1,3 +1,6 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Devvit Journeys Devvit Journeys adds a telemetry stream to your app that tracks the entire lifecycle of a user session. With journeys, you can: @@ -85,7 +88,7 @@ By instrumenting these moments, you can track session boundaries, understand pla | :---: | ------------------------------------- | ----------------------- | ----------------------------- | | **1** | Game loads | **App.Ready** | Game is fully interactive | | **2** | Player clicks “Start Game” | **Journey.Start** | Begins a new session | -| **3** | Player completes Level 1 (of 5 levels) | **Journey.Progress** | `progress: 0.2` | +| **3** | Player completes Level 1 (of 5 levels) | **Journey.Progress** | `progress: 0.2` | | **4** | Player opens inventory | **Journey.Interaction** | `action: "menu_opened"` | | **5** | Player completes Level 2 | **Journey.Progress** | `progress: 0.4` | | **6** | Player reaches final level | **Journey.Progress** | `progress: 0.9` | @@ -98,7 +101,7 @@ By instrumenting these moments, you can track session boundaries, understand pla | **1** | Game loads | **App.Ready** | Game is fully interactive | | **2** | Player clicks “Start Game” | **Journey.Start** | Begins a new session | | **3** | Player completes early level | **Journey.Progress** | `progress: 0.3` | -| **4** | Player dies | **Journey.End** | `complete: false`, `win: false` | +| **4** | Player dies | **Journey.End** | `complete: false`, `win: false` | ### Scenario 3: early exit / abandonment @@ -149,7 +152,7 @@ Here's how to implement journey tracking in your app. Set Journeys permissions to `true` in `devvit.json`. -``` +```json title="devvit.json" "permissions": { "journeys": true }, @@ -159,7 +162,7 @@ Set Journeys permissions to `true` in `devvit.json`. You can send events solely on the backend and use the front‑end only to establish and pass along the journeyId. To do this, thread the active `journey ID` from your front‑end to your backend routes. -``` +```ts title="client/index.ts" import { telemetry } from '@devvit/analytics/client/reddit'; export async function submitScore(score: number): Promise { @@ -184,7 +187,50 @@ export async function submitScore(score: number): Promise { On the server, read the incoming `journeyId` and use it for correlation in your own route. + + + +```ts title="server/index.ts" +import { telemetry } from '@devvit/analytics/server/reddit'; +import { Hono } from 'hono'; + +const app = new Hono(); + +app.post('/api/score', async (c) => { + const journeyId = c.req.header('x-devvit-journey-id') ?? ''; + const { score } = await c.req.json<{ score: number }>(); + + console.log('score event', { + journeyId, + score, + }); + + await telemetry.endJourney({ + journeyId, + complete: true, + game: { + win: true, + score, + }, + }); + + return c.json({ ok: true }); +}); + +export default app; ``` + + + + +```ts title="server/index.ts" import express from 'express'; import { telemetry } from '@devvit/analytics/server/reddit'; @@ -209,16 +255,18 @@ app.post('/api/score', async (req, res) => { res.json({ ok: true }); }); - ``` + + + ### Client events If you don’t want to manually send server-events, you can use the generic client side events. In this case, the `JourneyId` is handled. In this case, you won’t need to pass a `JourneyId` when calling progress and so forth. You also won’t need `telemetry.getActiveJourneyId()` unless you’re curious about that data. Note: This also requires using the route adapters provided in `@devvit/analytics/server/reddit` -``` +```ts title="client/index.ts" // client import { telemetry } from '@devvit/analytics/client/reddit'; @@ -231,7 +279,7 @@ await telemetry.progress({ ``` -``` +```ts title="server/index.ts" // server import { createTelemetryRouter } from '@devvit/analytics/server/reddit'; app.use(createTelemetryRouter());