Skip to content

Commit 5e97fbb

Browse files
authored
hono, vite, and little tweaks (#41)
<!-- If this pull request closes an issue, please mention the issue number below --> Closes # <!-- Issue # here --> ## 💸 TL;DR <!-- What's the three sentence summary of purpose of the PR --> - Adds a compact tab switcher that persists your choice across pages - Adds hono and express docs for all docs - Adds `devvit.json` scripts support - Updates `vite` docs and adds to sidebar - Adds a simple prettier config but it doesn't run prettier on the app - Fix deprecation warning - Fix all broken links - Mute noisy llms.txt log ## 📜 Details [Design Doc](<!-- insert Google Doc link here if applicable -->) [Jira](<!-- insert Jira link if applicable -->) <!-- Add additional details required for the PR: breaking changes, screenshots, external dependency changes --> ## 🧪 Testing Steps / Validation <!-- add details on how this PR has been tested, include reproductions and screenshots where applicable --> ## ✅ Checks <!-- Make sure your pr passes the CI checks and do check the following fields as needed - --> - [ ] CI tests (if present) are passing - [ ] Adheres to code style for repo - [ ] Contributor License Agreement (CLA) completed if not a Reddit employee
1 parent d1b1b3d commit 5e97fbb

75 files changed

Lines changed: 5645 additions & 2003 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.prettierrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"singleQuote": false
3+
}

docs/capabilities/blocks/blocks_payments.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
You can use the payments template to build your app or add payment functionality to an existing app.
44

55
:::note
6-
[Devvit Web](../../capabilities/devvit-web/devvit_web_overview.mdx) is the recommended approach for all interactive experiences. We recommend [migrating your app](../../earn-money/payments/payments_migrate.md) to Devvit Web payments.
6+
[Devvit Web](../../capabilities/devvit-web/devvit_web_overview.mdx) is the recommended approach for all interactive experiences. We recommend [migrating your app](../../earn-money/payments/payments_migrate.mdx) to Devvit Web payments.
77
:::
88

99
To start with a template, select the payments template when you create a new project or run:
@@ -161,28 +161,28 @@ Errors thrown within the payment handler automatically reject the order. To prov
161161
This example shows how to issue an "extra life" to a user when they purchase the "extra_life" product.
162162

163163
```ts
164-
import { type Context } from '@devvit/public-api';
165-
import { addPaymentHandler } from '@devvit/payments';
166-
import { Devvit, useState } from '@devvit/public-api';
164+
import { type Context } from "@devvit/public-api";
165+
import { addPaymentHandler } from "@devvit/payments";
166+
import { Devvit, useState } from "@devvit/public-api";
167167

168168
Devvit.configure({
169169
redis: true,
170170
redditAPI: true,
171171
});
172172

173-
const GOD_MODE_SKU = 'god_mode';
173+
const GOD_MODE_SKU = "god_mode";
174174

175175
addPaymentHandler({
176176
fulfillOrder: async (order, ctx) => {
177177
if (!order.products.some(({ sku }) => sku === GOD_MODE_SKU)) {
178-
throw new Error('Unable to fulfill order: sku not found');
178+
throw new Error("Unable to fulfill order: sku not found");
179179
}
180-
if (order.status !== 'PAID') {
181-
throw new Error('Becoming a god has a cost (in Reddit Gold)');
180+
if (order.status !== "PAID") {
181+
throw new Error("Becoming a god has a cost (in Reddit Gold)");
182182
}
183183

184184
const redisKey = godModeRedisKey(ctx.postId, ctx.userId);
185-
await ctx.redis.set(redisKey, 'true');
185+
await ctx.redis.set(redisKey, "true");
186186
},
187187
});
188188
```
@@ -204,14 +204,14 @@ Your app can acknowledge or reject the order. For example, for goods with limite
204204
Use the `useProducts` hook or `getProducts` function to fetch details about products.
205205

206206
```tsx
207-
import { useProducts } from '@devvit/payments';
207+
import { useProducts } from "@devvit/payments";
208208

209209
export function ProductsList(context: Devvit.Context): JSX.Element {
210210
// Only query for products with the metadata "category" of value "powerup".
211211
// The metadata field can be empty - if it is, useProducts will not filter on metadata.
212212
const { products } = useProducts(context, {
213213
metadata: {
214-
category: 'powerup',
214+
category: "powerup",
215215
},
216216
});
217217

docs/capabilities/blocks/optimize_performance.md

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Use `context.cache` to reduce the amount of requests to optimize performance and
3939

4040
### Leverage scheduled jobs to fetch or update data
4141

42-
Use [scheduler](../server/scheduler.md) to make large data requests in the background and store it in [Redis](../server/redis.mdx) for later use. You can also [fetch data for multiple users](#how-to-cache-data).
42+
Use [scheduler](../server/scheduler.mdx) to make large data requests in the background and store it in [Redis](../server/redis.mdx) for later use. You can also [fetch data for multiple users](#how-to-cache-data).
4343

4444
### Batch API calls to make parallel requests
4545

@@ -62,7 +62,7 @@ In Devvit, the first render happens on the server side. Parallel fetch requests
6262
In the render function of this interactive post, the app fetches data about the post, the user, the weather, and the leaderboard stats.
6363

6464
```tsx
65-
import { Devvit, useState } from '@devvit/public-api';
65+
import { Devvit, useState } from "@devvit/public-api";
6666

6767
render: (context) => {
6868
const [postInfo] = useState(async () => {
@@ -103,7 +103,7 @@ The main difference between these two methods is that `useState` blocks render u
103103
This is the best choice for performance because it allows you to render parts of your application while others may still be loading. Here’s how the same example looks for useAsync:
104104

105105
```tsx
106-
import { Devvit, useAsync } from '@devvit/public-api';
106+
import { Devvit, useAsync } from "@devvit/public-api";
107107

108108
const { data: postInfo, loading: postInfoLoading } = useAsync(async () => {
109109
return await getThreadInfo(context);
@@ -117,17 +117,19 @@ const { data: weather, loading: weatherLoading } = useAsync(async () => {
117117
return await getTheWeather(context);
118118
});
119119

120-
const { data: leaderboardStats, loading: leaderboardStatsLoading } = useAsync(async () => {
121-
return await getLeaderboard(context);
122-
});
120+
const { data: leaderboardStats, loading: leaderboardStatsLoading } = useAsync(
121+
async () => {
122+
return await getLeaderboard(context);
123+
},
124+
);
123125
```
124126

125127
#### useState
126128

127129
This is the same example using useState.
128130

129131
```tsx
130-
import { Devvit, useState } from '@devvit/public-api';
132+
import { Devvit, useState } from "@devvit/public-api";
131133

132134
render: (context) => {
133135
const [appState, setAppState] = useState(async () => {
@@ -162,11 +164,11 @@ If you need to update one of the state props, you’ll need to do `setAppState({
162164
The following example shows how unoptimized code for fetching data from an external resource, like a weather API, looks:
163165

164166
```tsx
165-
import { Devvit, useState } from '@devvit/public-api';
167+
import { Devvit, useState } from "@devvit/public-api";
166168

167169
// naive, non-optimal way of fetching that kind of data
168170
const [externalData] = useState(async () => {
169-
const response = await fetch('https://external.weather.com');
171+
const response = await fetch("https://external.weather.com");
170172

171173
return await response.json();
172174
});
@@ -183,19 +185,19 @@ You can use a cache helper to make one request for data, save the response, and
183185
**Example: fetch weather data every 2 hours with cache helper**
184186

185187
```tsx
186-
import { Devvit, useState } from '@devvit/public-api';
188+
import { Devvit, useState } from "@devvit/public-api";
187189

188190
// optimized, performant way of fetching that kind of data
189191
const [externalData] = useState(async () => {
190192
return context.cache(
191193
async () => {
192-
const response = await fetch('https://external.weather.com');
194+
const response = await fetch("https://external.weather.com");
193195
return await response.json();
194196
},
195197
{
196198
key: `weather_data`,
197199
ttl: 2 * 60 * 60 * 1000, // 2 hours in milliseconds
198-
}
200+
},
199201
);
200202
});
201203
```
@@ -206,35 +208,35 @@ Do not cache sensitive information. Cache helper randomly selects one user to ma
206208

207209
### Solution: schedule a job
208210

209-
Alternatively, you can use [scheduler](../server/scheduler.md) to make the request in background, save the response to [Redis](../server/redis.mdx), and avoid unnecessary requests to the external resource.
211+
Alternatively, you can use [scheduler](../server/scheduler.mdx) to make the request in background, save the response to [Redis](../server/redis.mdx), and avoid unnecessary requests to the external resource.
210212

211213
**Example: fetch weather data every 2 hours with a scheduled job**
212214

213215
```tsx
214-
import { Devvit } from '@devvit/public-api';
216+
import { Devvit } from "@devvit/public-api";
215217

216218
Devvit.addSchedulerJob({
217-
name: 'fetch_weather_data',
219+
name: "fetch_weather_data",
218220
onRun: async (_event, context) => {
219-
const response = await fetch('https://external.weather.com');
221+
const response = await fetch("https://external.weather.com");
220222
const responseData = await response.json();
221-
await context.redis.set('weather_data', JSON.stringify(responseData));
223+
await context.redis.set("weather_data", JSON.stringify(responseData));
222224
},
223225
});
224226

225227
Devvit.addTrigger({
226-
event: 'AppInstall',
228+
event: "AppInstall",
227229
onEvent: async (_event, context) => {
228230
await context.scheduler.runJob({
229-
cron: '0 */2 * * *', // runs at the top of every second hour
230-
name: 'fetch_weather_data',
231+
cron: "0 */2 * * *", // runs at the top of every second hour
232+
name: "fetch_weather_data",
231233
});
232234
},
233235
});
234236

235237
// inside the render method
236238
const [externalData] = useState(async () => {
237-
return context.redis.get('fetch_weather_data');
239+
return context.redis.get("fetch_weather_data");
238240
});
239241

240242
export default Devvit;
@@ -254,9 +256,9 @@ Before using realtime, the leaderboard fetching code looked like this:
254256

255257
```tsx
256258
const getLeaderboard = async () =>
257-
await context.redis.zRange('leaderboard', 0, 5, {
259+
await context.redis.zRange("leaderboard", 0, 5, {
258260
reverse: true,
259-
by: 'rank',
261+
by: "rank",
260262
});
261263

262264
const [leaderboard, setLeaderboard] = useState(async () => {
@@ -274,7 +276,7 @@ leaderboardInterval.start();
274276
And code for updating the leaderboard looked like this:
275277

276278
```tsx
277-
await context.redis.zAdd('leaderboard', { member: username, score: gameScore });
279+
await context.redis.zAdd("leaderboard", { member: username, score: gameScore });
278280
```
279281

280282
### With realtime​
@@ -285,9 +287,12 @@ This is the updated game completion code:
285287

286288
```tsx
287289
// stays as is
288-
await context.redis.zAdd('leaderboard', { member: username, score: gameScore });
290+
await context.redis.zAdd("leaderboard", { member: username, score: gameScore });
289291
// new code
290-
context.realtime.send('leaderboard_updates', { member: username, score: gameScore });
292+
context.realtime.send("leaderboard_updates", {
293+
member: username,
294+
score: gameScore,
295+
});
291296
```
292297

293298
Now replace the interval with the realtime subscription:
@@ -298,7 +303,7 @@ const [leaderboard, setLeaderboard] = useState(async () => {
298303
}); // stays as is
299304

300305
const channel = useChannel({
301-
name: 'leaderboard_updates',
306+
name: "leaderboard_updates",
302307
onMessage: (newLeaderboardEntry) => {
303308
const newLeaderboard = [...leaderboard, newLeaderboardEntry] // append new entry
304309
.sort((a, b) => b.score - a.score) // sort by score
@@ -342,9 +347,12 @@ To do this, you can add:
342347
```tsx
343348
const [subscriberCount] = useState<number>(async () => {
344349
const startSubscribersRequest = Date.now(); // a reference point for the request start
345-
const devvitSubredditInfo = await context.reddit.getSubredditInfoByName('devvit');
350+
const devvitSubredditInfo =
351+
await context.reddit.getSubredditInfoByName("devvit");
346352

347-
console.log(`subscribers request took: ${Date.now() - startSubscribersRequest} milliseconds`);
353+
console.log(
354+
`subscribers request took: ${Date.now() - startSubscribersRequest} milliseconds`,
355+
);
348356

349357
return devvitSubredditInfo.subscribersCount || 0;
350358
});
@@ -359,7 +367,9 @@ const [performanceStartRender] = useState(Date.now()); // a reference point for
359367
Add a console.log before the return statement:
360368

361369
```tsx
362-
console.log(`Getting the data took: ${Date.now() - performanceStartRender} milliseconds`);
370+
console.log(
371+
`Getting the data took: ${Date.now() - performanceStartRender} milliseconds`,
372+
);
363373
```
364374

365375
All of that put together will look like this:

0 commit comments

Comments
 (0)