### Start Playtest Session with Devvit CLI Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Installs your app to a test subreddit and starts a continuous playtest session. Changes are automatically reflected upon saving, and logs are streamed in real-time. Press Ctrl+C to end the session. ```bash npx devvit playtest ``` -------------------------------- ### Complete Example Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/launch_screen_customization A complete example demonstrating the usage of launch screen customization and mode management. ```APIDOC ## Complete Example ### Description This example demonstrates how to integrate launch screen customization and manage view modes within a React application. ### Code ```typescript import React, { useState, useEffect } from 'react'; import { getWebViewMode, requestExpandedMode, exitExpandedMode, addWebViewModeListener, removeWebViewModeListener, } from '@devvit/web/client'; export function GameApp() { const [mode, setMode] = useState(getWebViewMode()); const [gameStarted, setGameStarted] = useState(false); useEffect(() => { const handleModeChange = (newMode: 'inline' | 'expanded') => { setMode(newMode); // Pause game when exiting expanded mode if (newMode === 'inline' && gameStarted) { pauseGame(); } }; addWebViewModeListener(handleModeChange); return () => removeWebViewModeListener(handleModeChange); }, [gameStarted]); const handlePlayClick = async (event: React.MouseEvent) => { try { await requestExpandedMode(event.nativeEvent, 'game'); setGameStarted(true); } catch (error) { console.error('Could not enter expanded mode:', error); // Fallback: start game inline setGameStarted(true); } }; const handleExitClick = async (event: React.MouseEvent) => { try { await exitExpandedMode(event.nativeEvent); } catch (error) { console.error('Could not exit expanded mode:', error); } }; if (mode === 'inline') { return (

Adventure Game

Tap to play in fullscreen

); } return (
); } ``` ``` -------------------------------- ### Install @devvit/start Source: https://developers.reddit.com/docs/guides/tools/vite Run this command to install the necessary Vite plugin for Devvit projects. ```bash npm install @devvit/start ``` -------------------------------- ### Example of using get() with TxClientLike Source: https://developers.reddit.com/docs/api/public-api/type-aliases/TxClientLike Illustrates retrieving the value of a Redis key using the `get` method. Returns `null` if the key does not exist. ```typescript async function getExample(context: Devvit.Context) { await context.redis.set("quantity", "5"); const quantity : string | undefined = await context.redis.get("quantity"); console.log("Quantity: " + quantity); } ``` -------------------------------- ### Install Latest @devvit/web Source: https://developers.reddit.com/docs/guides/migrate/devvit-web-experimental Install the latest version of the @devvit/web package to begin the migration. ```bash npm install @devvit/web@latest ``` -------------------------------- ### Complete Game App Example Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/launch_screen_customization A comprehensive React example demonstrating how to manage inline and expanded views, handle mode changes, and integrate game logic. ```typescript import React, { useState, useEffect } from 'react'; import { getWebViewMode, requestExpandedMode, exitExpandedMode, addWebViewModeListener, removeWebViewModeListener, } from '@devvit/web/client'; export function GameApp() { const [mode, setMode] = useState(getWebViewMode()); const [gameStarted, setGameStarted] = useState(false); useEffect(() => { const handleModeChange = (newMode: 'inline' | 'expanded') => { setMode(newMode); // Pause game when exiting expanded mode if (newMode === 'inline' && gameStarted) { pauseGame(); } }; addWebViewModeListener(handleModeChange); return () => removeWebViewModeListener(handleModeChange); }, [gameStarted]); const handlePlayClick = async (event: React.MouseEvent) => { try { await requestExpandedMode(event.nativeEvent, 'game'); setGameStarted(true); } catch (error) { console.error('Could not enter expanded mode:', error); // Fallback: start game inline setGameStarted(true); } }; const handleExitClick = async (event: React.MouseEvent) => { try { await exitExpandedMode(event.nativeEvent); } catch (error) { console.error('Could not exit expanded mode:', error); } }; if (mode === 'inline') { return (

Adventure Game

Tap to play in fullscreen

); } return (
); } ``` -------------------------------- ### Install Latest @devvit/web Package Source: https://developers.reddit.com/docs/llms-full.txt Command to update the `@devvit/web` package to the latest version, essential for migrating to the final Devvit Web setup. ```bash npm install @devvit/web@latest ``` -------------------------------- ### Redis String Operations Example Source: https://developers.reddit.com/docs/capabilities/server/redis Demonstrates setting a string, getting a range, overwriting part of a string, and checking its length using Redis commands. ```javascript async function stringsExample() { // First, set 'word' to 'tacocat' await redis.set('word', 'tacocat'); // Use getRange() to get the letters in 'word' between index 0 to 3, inclusive console.log('Range from index 0 to 3: ' + (await redis.getRange('word', 0, 3))); // Use setRange() to insert 'blue' at index 0 await redis.setRange('word', 0, 'blue'); console.log('Word after using setRange(): ' + (await redis.get('word'))); // Use strLen() to verify the word length console.log('Word length: ' + (await redis.strLen('word'))); } ``` ```text Range from index 0 to 3: taco Word after using setRange(): bluecat Word length: 7 ``` -------------------------------- ### Install App to Subreddit with Devvit CLI Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Installs a specified app from the Apps directory to a subreddit you moderate. You can install the latest version or a specific version. ```bash npx devvit install [app-name]@[version] ``` ```bash npx devvit install r/mySubreddit ``` ```bash npx devvit install mySubreddit my-app ``` ```bash npx devvit install r/mySubreddit my-app@1.2.3 ``` ```bash npx devvit install r/mySubreddit @1.2.3 ``` -------------------------------- ### Devvit JSON Configuration Example Source: https://developers.reddit.com/docs/llms-full.txt Example `devvit.json` configuration specifying entry points for client and server builds. The Vite plugin uses this file as the source of truth for client entry points. ```json { "post": { "dir": "dist/client", "entrypoints": { "default": { "inline": true, "entry": "splash.html" } } }, "server": { "dir": "dist/server", "entry": "index.ts" } } ``` -------------------------------- ### Install App to Subreddit Source: https://developers.reddit.com/docs/llms-full.txt Installs the latest non-playtest version of your app to a specified subreddit. You can also specify a particular version to install. ```bash devvit install ``` ```bash devvit install [@version] ``` -------------------------------- ### products.json Schema Example Source: https://developers.reddit.com/docs/earn-money/payments/payments_add This example demonstrates the structure of the `products.json` file, including product attributes like SKU, display name, description, price, image, metadata, and accounting type. ```json { "$schema": "https://developers.reddit.com/schema/products.json", "products": [ { "sku": "god_mode", "displayName": "God mode", "description": "God mode gives you superpowers (in theory)", "price": 25, "images": { "icon": "products/extra_life_icon.png" }, "metadata": { "category": "powerup" }, "accountingType": "CONSUMABLE" } ] } ``` -------------------------------- ### Redis String Operations Example Source: https://developers.reddit.com/docs/llms-full.txt Demonstrates setting a string, getting a substring, overwriting part of a string, and checking its length using Redis commands. ```typescript async function stringsExample() { // First, set 'word' to 'tacocat' await redis.set('word', 'tacocat'); // Use getRange() to get the letters in 'word' between index 0 to 3, inclusive console.log('Range from index 0 to 3: ' + (await redis.getRange('word', 0, 3))); // Use setRange() to insert 'blue' at index 0 await redis.setRange('word', 0, 'blue'); console.log('Word after using setRange(): ' + (await redis.get('word'))); // Use strLen() to verify the word length console.log('Word length: ' + (await redis.strLen('word'))); } ``` ```bash Range from index 0 to 3: taco Word after using setRange(): bluecat Word length: 7 ``` -------------------------------- ### Install Express for Server Endpoints Source: https://developers.reddit.com/docs/guides/migrate/inline-web-view Install the Express.js framework to set up server endpoints in your Devvit application. This is a prerequisite for creating a custom server. ```bash npm i express ``` -------------------------------- ### Server-Side HTTP Fetch Example Source: https://developers.reddit.com/docs/capabilities/http-fetch Make GET requests to allow-listed external domains from your server-side code. Ensure the domain is configured in `devvit.json` and the request does not exceed the 30-second timeout. ```typescript const response = await fetch('https://example.com/api/data', { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); const data = await response.json(); console.log('External API response:', data); ``` -------------------------------- ### List Installed Apps on Subreddit with Devvit CLI Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Lists all apps currently installed on a specified subreddit. If no subreddit is provided, it lists all apps installed by the current user. ```bash npx devvit list installs [subreddit] ``` ```bash npx devvit list installs ``` ```bash npx devvit list installs mySubreddit ``` ```bash npx devvit list installs r/mySubreddit ``` -------------------------------- ### Complete Interactive Post Example with Devvit Source: https://developers.reddit.com/docs/llms-full.txt This example demonstrates creating an interactive post with a splash screen, post data for game state, and includes logic for updating post data and submitting a comment as a user. It utilizes `reddit.submitCustomPost`, `reddit.getPostById`, and `updatedPost.setPostData`. ```typescript import { reddit, context } from '@devvit/web/server'; // Create an interactive post with all features const post = await reddit.submitCustomPost({ subredditName: context.subredditName!, title: 'Interactive Game Post', // First Screen Customization (splash screen) splash: { appDisplayName: 'Number Guessing Game', backgroundUri: 'game-bg.png', buttonLabel: 'Start Playing', description: 'Can you guess the secret number?', heading: 'Welcome to the Challenge!' }, // Post Data for game state postData: { secretNumber: Math.floor(Math.random() * 100) + 1, totalGuesses: 0, gameState: 'active', winner: null } }); // Later, update post data when someone wins const updatedPost = await reddit.getPostById(post.id); await updatedPost.setPostData({ ...context.postData, gameState: 'completed', winner: currentUsername, totalGuesses: context.postData.totalGuesses + 1 }); // Create a comment as the user announcing their win await reddit.submitComment({ runAs: 'USER', id: post.id, text: 🎉 ${currentUsername} won the game! }); ``` -------------------------------- ### Migrate Example Data Job Source: https://developers.reddit.com/docs/llms-full.txt Handles the migration of example data, including compression and cursor-based iteration. It includes logic to stop early if execution time approaches the limit and requeues itself if more data needs processing. ```javascript // The proxy detects the write and compresses the string if beneficial. if (value && value.length > 0) { await redisCompressed.hSet(MY_DATA_HASH_KEY, { [field]: value }); } }) ); processedInJob += fieldValues.length; // Cursor logic: 0 means iteration is complete if (nextCursor === 0) { cursor = 0; keepRunning = false; } else { cursor = nextCursor; } // Safety: Check execution time. // If we are close to 30s (Devvit limit), stop early and requeue. if (Date.now() - startTime > 20000) { console.log('[Migration] Time limit approaching, stopping early.'); keepRunning = false; } } const newTotal = processedTotal + processedInJob; // Daisy Chaining: // If the cursor is not 0, we still have more data to scan. // We schedule *this same job* to run again immediately. if (cursor !== 0) { console.log(`[Migration] Requeueing. Next cursor: ${cursor}. Processed so far: ${newTotal}`); await scheduler.runJob({ name: 'migrate-example-data', runAt: new Date(), data: { cursor, chunkSize, processed: newTotal, }, }); res.json({ status: 'requeued', processed: newTotal, cursor }); } else { console.log(`[Migration] COMPLETE. Total items processed: ${newTotal}`); res.json({ status: 'success', processed: newTotal }); } } catch (error) { console.error('[Migration] Critical Job Error', error); res.status(500).json({ status: 'error', message: error.message }); } }, ); ``` -------------------------------- ### Install Devvit Payments Package Source: https://developers.reddit.com/docs/llms-full.txt Add the Devvit payments functionality to an existing app by installing the `@devvit/payments` npm package. ```bash npm install @devvit/payments ``` -------------------------------- ### Get Range Example Source: https://developers.reddit.com/docs/api/public-api/type-aliases/RedisClient Retrieves a substring from a Redis key based on start and end offsets. Ensure the key exists and the offsets are valid. ```typescript async function getRangeExample(context: Devvit.Context) { await context.redis.set("word", "tacocat"); const range : string = await context.redis.getRange("word", 0, 3) console.log("Range from index 0 to 3: " + range); } ``` -------------------------------- ### Client-Side HTTP Fetch Example Source: https://developers.reddit.com/docs/capabilities/http-fetch Make GET requests to your own webview's API endpoints from client-side code. Requests must target endpoints starting with `/api` and cannot access external domains. ```typescript 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); }; // Incorrect: cannot fetch external domains from client-side // const response = await fetch('https://external-api.com/data'); // Incorrect: endpoint must start with /api // const response = await fetch('/user-data'); ``` -------------------------------- ### Complete Devvit.json Configuration Example Source: https://developers.reddit.com/docs/capabilities/devvit-web/devvit_web_configuration This is a comprehensive example of a devvit.json file. It demonstrates how to configure application name, post settings, server entrypoints, permissions (HTTP and Redis), triggers, menu items, scheduler tasks, marketing assets, development subreddit, and build scripts. ```json { "$schema": "https://developers.reddit.com/schema/config-file.v1.json", "name": "my-awesome-app", "post": { "dir": "public", "entrypoints": { "default": { "entry": "index.html", "height": "tall" } } }, "server": { "entry": "src/server/index.js" }, "permissions": { "http": { "enable": true, "domains": ["api.example.com"] }, "redis": true }, "triggers": { "onPostCreate": "/internal/triggers/post-create" }, "menu": { "items": [ { "label": "Approve", "forUserType": "moderator", "location": "post", "endpoint": "/internal/menu/approve" } ] }, "scheduler": { "tasks": { "daily-cleanup": { "endpoint": "/internal/cron/cleanup", "cron": "0 2 * * *" } } }, "marketingAssets": { "icon": "assets/icon.png" }, "dev": { "subreddit": "my-test-sub" }, "scripts": { "dev": "vite build --watch", "build": "vite build" } } ``` -------------------------------- ### Create a basic app with a log action Source: https://developers.reddit.com/docs/llms-full.txt Configure a Devvit app with a menu item that triggers a server-side log when clicked. Ensure the `devvit.json` includes necessary permissions and server configuration. ```json { "$schema": "https://developers.reddit.com/schema/config-file.v1.json", "name": "app-name", "server": { "dir": "dist/server", "entry": "index.cjs" }, "permissions": { "reddit": true }, "menu": { "items": [ { "label": "Create a log!", "location": "subreddit", "endpoint": "/internal/log-action", "forUserType": "moderator" } ] } } ``` -------------------------------- ### Get User ID Source: https://developers.reddit.com/docs/api/redditapi/models/classes/User Retrieves the unique identifier for a user, which starts with 't2_'. ```javascript 't2_1w72' ``` -------------------------------- ### Fetch Products and Initiate Purchase (Client) Source: https://developers.reddit.com/docs/earn-money/payments/payments_add Fetch product metadata from your server and use it to display products. Call `purchase(sku)` to initiate the payment flow. Requires `@devvit/web/client`. ```typescript import { purchase, OrderResultStatus } from "@devvit/web/client"; // Fetch products from your server endpoint const products = await fetch("/api/products").then((r) => r.json()); // Render your UI; when user chooses a product: async function handleBuy(sku: string) { const result = await purchase(sku); if (result.status === OrderResultStatus.STATUS_SUCCESS) { // show success } else { // show error or retry (result.errorMessage may be set) } } ``` -------------------------------- ### Get Mod Queue Items (Posts and Comments) Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Fetches items from the moderator queue that require review. This example shows how to get both posts and comments by default, and then specifically posts. ```javascript const subreddit = await reddit.getSubredditByName("mysubreddit") let listing = await subreddit.getModQueue(); console.log("Posts and Comments: ", await listing.all()) listing = await subreddit.getModQueue({ type: "post"}); console.log("Posts: ", await listing.all()) ``` -------------------------------- ### Get Devvit CLI Version Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Display the version of the locally installed Devvit CLI. ```bash $ npx devvit version ``` -------------------------------- ### Create Basic App Log Source: https://developers.reddit.com/docs/guides/tools/logs This example demonstrates how to create a basic app configuration and a server-side endpoint that logs a message when triggered by a menu action. Use `console.log`, `console.info`, or `console.error` in your server code to generate logs. ```json { "$schema": "https://developers.reddit.com/schema/config-file.v1.json", "name": "app-name", "server": { "dir": "dist/server", "entry": "index.cjs" }, "permissions": { "reddit": true }, "menu": { "items": [ { "label": "Create a log!", "location": "subreddit", "endpoint": "/internal/log-action", "forUserType": "moderator" } ] } } ``` -------------------------------- ### Get Redis Key Value as String Source: https://developers.reddit.com/docs/api/public-api/type-aliases/RedisClient Example of retrieving the string value associated with a Redis key using the get command. Returns undefined if the key does not exist. Raises an exception for non-UTF-8 values. ```typescript async function getExample(context: Devvit.Context) { await context.redis.set("quantity", "5"); const quantity : string | undefined = await context.redis.get("quantity"); console.log("Quantity: " + quantity); } ``` -------------------------------- ### Get Vault by Address Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Fetches a Vault associated with a given blockchain address. The address must start with '0x'. ```javascript const vault = await reddit.getVaultByAddress('0x205ee28744456bDBf180A0Fa7De51e0F116d54Ed'); ``` -------------------------------- ### Preview HTML for Launch Screen Customization Source: https://developers.reddit.com/docs/llms-full.txt An example HTML file for a launch screen. This serves as the initial inline view for the user before they engage with the app. ```html My Game

Adventure Game

Tap to play in fullscreen

``` -------------------------------- ### Get Vault by User ID Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Retrieves a Vault belonging to a specific user ID. The user ID should start with 't2_'. ```javascript const vault = await reddit.getVaultByUserId('t2_1w72'); ``` -------------------------------- ### Create Custom Post with Styles Source: https://developers.reddit.com/docs/capabilities/creating_custom_post This example shows how to create a custom post and apply specific styles, such as background colors, height, and a share image URL. ```APIDOC ## submitCustomPost with Styles ### Description Creates a custom post with specified styling options to control its appearance in the Reddit UI. ### Method `reddit.submitCustomPost(options)` ### Parameters (See `submitCustomPost` for general parameters. The following are specific to `styles`) #### Request Body - styles - **backgroundColor** (string) - Optional - The default background color shown before the iframe content loads. Must be in `#RRGGBBAA` format. Defaults to transparent (`#00000000`). - **backgroundColorDark** (string) - Optional - The dark mode background color shown before the iframe content loads. Must be in `#RRGGBBAA` format. Defaults to transparent (`#00000000`). - **height** (string) - Optional - Post height. Can be `"REGULAR"` (320px) or `"TALL"` (512px). Defaults to `"TALL"`. - **shareImageUrl** (string) - Optional - The preview image URL used when the post is shared externally. Must be hosted on i.redd.it. ### Request Example ```javascript await reddit.submitCustomPost({ "title": "Post with styles title", "styles": { "backgroundColor": "#FFFFFFFF", // white, fully opaque "backgroundColorDark": "#000000FF", // black, fully opaque "height": "TALL", "shareImageUrl": "https://reddi.it/12345.png" } }) ``` ### Response #### Success Response (200) Returns the created post object with applied styles. #### Response Example (Response structure not explicitly defined in source, but would be a post object) ``` -------------------------------- ### Get Subreddit Info by ID - RedditAPIClient Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Fetches a SubredditInfo object using its unique ID. Ensure the ID starts with 't5_'. ```javascript const memes = await reddit.getSubredditInfoById('t5_2qjpg'); ``` -------------------------------- ### Get Comment by ID RedditAPIClient Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Retrieves a specific Comment object using its unique ID. The ID must start with 't1_'. ```javascript const comment = await reddit.getCommentById('t1_1qjpg'); ``` -------------------------------- ### New Launch Screen Entry Points Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/splash_migration Demonstrates how to use `submitCustomPost()` with explicit entry points for the new launch screen system. ```typescript await reddit.submitCustomPost({ title: 'Tennis Match #37' }); // implicitly default entry await reddit.submitCustomPost({ title: 'Tennis Leaderboard', entry: 'leaderboard' }); // not a URI, an entry name. ``` -------------------------------- ### Vite Build Configuration for Entry Points Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/splash_migration Example of configuring Vite's build process to include multiple HTML entry points. ```javascript { ...all your normal vite nonsense... build: { input: ['splash.html', 'game.html'], <-- add your entrypoints ... } } ``` -------------------------------- ### Example README Fetch Domains Section Source: https://developers.reddit.com/docs/capabilities/http-fetch Include a 'Fetch Domains' section in your README to list requested domains and their justifications. This is required for the approval process. ```markdown ## Fetch Domains The following domains are requested for this app: - `api.wikipedia.org` - Used to fetch article summaries for the knowledge base feature - `username.supabase.com` - Required for relational database storage of user preferences (Devvit KV store doesn't support complex queries needed for this feature) ``` -------------------------------- ### Client Vite Configuration Example Source: https://developers.reddit.com/docs/guides/migrate/devvit-web-experimental Configure Vite for client-side builds, specifying the output directory to `dist/client`. ```javascript export default defineConfig({ build: { outDir: '../../dist/client', // No longer webroot }, }); ``` -------------------------------- ### Example README Fetch Domains Section Source: https://developers.reddit.com/docs/capabilities/server/http-fetch-policy Include a 'Fetch Domains' section in your app's README to list requested domains and their justifications for the approval process. ```markdown ## Fetch Domains The following domains are requested for this app: - `api.wikipedia.org` - Used to fetch article summaries for the knowledge base feature - `username.supabase.com` - Required for relational database storage of user preferences (Devvit KV store doesn't support complex queries needed for this feature) ``` -------------------------------- ### Redis Bitfield Operations Source: https://developers.reddit.com/docs/api/public-api/type-aliases/RedisClient Demonstrates the use of the bitfield command for complex bitwise operations on Redis strings. Includes examples for incrementing and getting bits. ```typescript async function bitfieldExample(context: Devvit.Context) { const fooResults : number[] = await context.redis.bitfield('foo', 'incrBy', 'i5', 100, 1, 'get', 'u4', 0); console.log("fooResults: " + fooResults); // [1, 0] const barResults : number[] = await context.redis.bitfield('bar', 'set', 'u2', 0, 3, 'get', 'u2', 0, 'incrBy', 'u2', 0, 1, 'overflow', 'sat', 'get', 'u2', 0, 'set', 'u2', 0, 3, 'incrBy', 'u2', 0, 1); console.log("barResults: " + barResults); // [0, 3, 0, 0, 3, 3] } ``` -------------------------------- ### Configure Devvit.json Entrypoints Source: https://developers.reddit.com/docs/llms-full.txt Define entrypoints for client, blocks, and server applications in `devvit.json`. The client and server entrypoints should point to bundled output files, while the Devvit CLI handles bundling for blocks automatically. ```json { "post": { "client": { "dir": "dist/client", "entry": "dist/client/index.html" } }, "blocks": { "entry": "src/devvit/main.tsx" }, "server": { "entry": "dist/server/index.cjs" }, } ``` -------------------------------- ### Get Unread Modmail Count Source: https://developers.reddit.com/docs/api/redditapi/models/classes/ModMailService Fetches the count of unread modmail conversations, categorized by conversation state. The example logs the 'highlighted' and 'new' counts. ```javascript const response = await reddit.modMail.getUnreadCount(); console.log(response.highlighted); console.log(response.new); ``` -------------------------------- ### Server Vite Configuration Example Source: https://developers.reddit.com/docs/guides/migrate/devvit-web-experimental Configure Vite for server-side builds, ensuring correct output directories and formats as specified in `devvit.json`. ```javascript export default defineConfig({ ssr: { noExternal: true, }, build: { emptyOutDir: false, ssr: 'index.ts', outDir: '../../dist/server', target: 'node22', sourcemap: true, rollupOptions: { external: [...builtinModules], output: { format: 'cjs', entryFileNames: 'index.cjs', inlineDynamicImports: true, }, }, }, }); ``` -------------------------------- ### Get Moderated Subreddits Source: https://developers.reddit.com/docs/api/redditapi/models/classes/ModMailService Retrieves a list of subreddits that the current user moderates and has mail permissions for. The example shows how to log the ID and name of each subreddit. ```javascript const subredditsData = await reddit.modMail.getSubreddits(); for (const subreddit of Object.values(subreddits)) { console.log(subreddit.id); console.log(subreddit.name); } ``` -------------------------------- ### Example of using getRange() with TxClientLike Source: https://developers.reddit.com/docs/api/public-api/type-aliases/TxClientLike Demonstrates extracting a substring from a Redis string value using the `getRange` method. Both start and end offsets are inclusive. ```typescript async function getRangeExample(context: Devvit.Context) { await context.redis.set("word", "tacocat"); const range : string = await context.redis.getRange("word", 0, 3) console.log("Range from index 0 to 3: " + range); } ``` -------------------------------- ### Redis Menu Endpoint Definition Source: https://developers.reddit.com/docs/capabilities/server/redis Defines the menu endpoint that returns the form definition for initiating the migration. This setup is crucial for user interaction to start the migration process. ```typescript import { redis, scheduler, type TaskRequest, type TaskResponse } from '@devvit/web/server'; // Import the compressed client import { redisCompressed } from '@devvit/redis'; import type { MenuItemRequest, UiResponse } from '@devvit/web/shared'; type MigrateExampleFormRequest = { startCursor?: string; chunkSize?: number; }; type MigrateExampleJobData = { cursor?: number | string; chunkSize?: number; processed?: number; }; const MY_DATA_HASH_KEY = 'my:app:large:dataset'; // 1. Menu Endpoint: Returns the form definition app.post( '/internal/menu/ops/migrate-example', async (_req, res) => { res.json({ showForm: { name: 'migrateExampleForm', // Must match key in devvit.json "forms" form: { title: 'Migrate Hash to Compression', acceptLabel: 'Start Migration', fields: [ { name: 'startCursor', label: 'Start Cursor (0 for beginning)', type: 'string', defaultValue: '0', }, { name: 'chunkSize', label: 'Items per batch', type: 'number', defaultValue: 20000, }, ], }, }, }); }, ); ``` -------------------------------- ### Display Devvit CLI Help Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Use this command to view general help information for the Devvit CLI. ```bash npx devvit help ``` -------------------------------- ### Add Support App Product using Devvit CLI Source: https://developers.reddit.com/docs/earn-money/payments/support_this_app Use the Devvit CLI to generate the necessary product configuration for the 'support-app'. This is the first step in setting up the in-app support feature. ```bash devvit products add support-app ``` -------------------------------- ### mGet Example Source: https://developers.reddit.com/docs/api/public-api/type-aliases/RedisClient Use mGet to retrieve multiple values from Redis by specifying an array of keys. This is more efficient than calling GET multiple times. Note that deprecated mget() should not be used. ```typescript async function mGetExample(context: Devvit.Context) { await context.redis.mSet({"name": "Zeek", "occupation": "Developer"}); const result : (string | null)[] = await context.redis.mGet(["name", "occupation"]); result.forEach(x => { console.log(x); }); } ``` -------------------------------- ### Publish an app for public listing Source: https://developers.reddit.com/docs/guides/launch/launch-guide Run this command to publish your app and request it to be listed in the App Directory, making it available for any community to install. This requires a detailed README.md. ```bash npx devvit publish --public ``` -------------------------------- ### Get User by ID - RedditAPIClient Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Retrieves a User object using their unique ID. Ensure the ID starts with 't2_'. The promise may resolve to undefined if the user is not found. ```javascript const user = await reddit.getUserById('t2_1qjpg'); ``` -------------------------------- ### Get User Modmail Conversations Source: https://developers.reddit.com/docs/api/redditapi/models/classes/ModMailService Retrieves recent posts, comments, and modmail conversations for a specific user. Requires the user's conversation ID. The example logs recent comments and posts. ```javascript const data = await reddit.modMail.getUserConversations('abcdef'); console.log(data.recentComments); console.log(data.recentPosts); ``` -------------------------------- ### Complete Game App Example with View Mode Handling Source: https://developers.reddit.com/docs/llms-full.txt This example demonstrates a full React app that handles inline and expanded view modes, including requesting expanded mode, exiting it, and pausing the game when exiting expanded mode. It also includes fallback logic if expanded mode cannot be entered. ```tsx import React, { useState, useEffect } from 'react'; import { getWebViewMode, requestExpandedMode, exitExpandedMode, addWebViewModeListener, removeWebViewModeListener, } from '@devvit/web/client'; export function GameApp() { const [mode, setMode] = useState(getWebViewMode()); const [gameStarted, setGameStarted] = useState(false); useEffect(() => { const handleModeChange = (newMode: 'inline' | 'expanded') => { setMode(newMode); // Pause game when exiting expanded mode if (newMode === 'inline' && gameStarted) { pauseGame(); } }; addWebViewModeListener(handleModeChange); return () => removeWebViewModeListener(handleModeChange); }, [gameStarted]); const handlePlayClick = async (event: React.MouseEvent) => { try { await requestExpandedMode(event.nativeEvent, 'game'); setGameStarted(true); } catch (error) { console.error('Could not enter expanded mode:', error); // Fallback: start game inline setGameStarted(true); } }; const handleExitClick = async (event: React.MouseEvent) => { try { await exitExpandedMode(event.nativeEvent); } catch (error) { console.error('Could not exit expanded mode:', error); } }; if (mode === 'inline') { return (

Adventure Game

Tap to play in fullscreen

); } return (
); } ``` -------------------------------- ### Get Sorted Set Member Score with zScore Source: https://developers.reddit.com/docs/api/public-api/type-aliases/RedisClient Use zScore to retrieve the score associated with a specific member in a Redis Sorted Set. This example adds members to a 'leaderboard' sorted set and then fetches the score for 'caesar'. ```typescript async function zScoreExample(context: Devvit.Context) { await context.redis.zAdd("leaderboard", {member: "louis", score: 37}, {member: "fernando", score: 10}, {member: "caesar", score: 20}, {member: "alexander", score: 25}, ); const score : number = await context.redis.zScore("leaderboard", "caesar"); console.log("Caesar's score: " + score); } ``` -------------------------------- ### Create an Interactive Post with Features Source: https://developers.reddit.com/docs/capabilities/interactive-posts/interactive_posts_overview This example demonstrates creating an interactive post with first screen customization (splash screen), post data for game state, and includes logic for updating post data and submitting a comment as the user. ```typescript import { reddit, context } from '@devvit/web/server'; // Create an interactive post with all features const post = await reddit.submitCustomPost({ subredditName: context.subredditName!, title: 'Interactive Game Post', // First Screen Customization (splash screen) splash: { appDisplayName: 'Number Guessing Game', backgroundUri: 'game-bg.png', buttonLabel: 'Start Playing', description: 'Can you guess the secret number?', heading: 'Welcome to the Challenge!' }, // Post Data for game state postData: { secretNumber: Math.floor(Math.random() * 100) + 1, totalGuesses: 0, gameState: 'active', winner: null } }); // Later, update post data when someone wins const updatedPost = await reddit.getPostById(post.id); await updatedPost.setPostData({ ...context.postData, gameState: 'completed', winner: currentUsername, totalGuesses: context.postData.totalGuesses + 1 }); // Create a comment as the user announcing their win await reddit.submitComment({ runAs: 'USER', id: post.id, text: 🎉 ${currentUsername} won the game! }); ``` -------------------------------- ### Execute Redis Transaction with EXEC Source: https://developers.reddit.com/docs/llms-full.txt Use `WATCH` to monitor keys, `MULTI` to start a transaction, queue commands like `INCRBY` and `SET`, and `EXEC` to commit them. This example demonstrates updating karma and setting a name within a transaction. ```typescript async function transactionsExample1() { await redis.mSet({ quantity: '5', karma: '32' }); const txn = await redis.watch('quantity'); await txn.multi(); // Begin a transaction await txn.incrBy('karma', 10); await txn.set('name', 'Devvit'); await txn.exec(); // Execute the commands in the transaction console.log( 'Keys after completing transaction: ' + (await redis.mGet(['quantity', 'karma', 'name'])) ); } ``` ```bash Keys after completing transaction: 5,42,Devvit ``` -------------------------------- ### README Fetch Domains Section Example Source: https://developers.reddit.com/docs/llms-full.txt When using fetch domains, include a 'Fetch Domains' section in your app's README. List each requested domain and explain its necessity for the approval process. ```markdown ## Fetch Domains The following domains are requested for this app: - `api.wikipedia.org` - Used to fetch article summaries for the knowledge base feature - `username.supabase.com` - Required for relational database storage of user preferences (Devvit KV store doesn't support complex queries needed for this feature) ``` -------------------------------- ### AppInstallDefinition Source: https://developers.reddit.com/docs/api/public-api/type-aliases/AppInstallDefinition Defines the structure for app installation events, including the event itself and a handler for when the event occurs. ```APIDOC ## Type Alias: AppInstallDefinition > **AppInstallDefinition** = `object` ## Properties ### event > **event** : `AppInstall` > The app installation event. ### onEvent > **onEvent** : `TriggerOnEventHandler`<`AppInstall`> > Handler for when an app installation event occurs. ``` -------------------------------- ### Install Devvit CLI as Dev Dependency Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Install or update the Devvit CLI as a development dependency within your project. This is the recommended installation method. ```bash npm install --save-dev devvit@latest ``` -------------------------------- ### Add Products via CLI Source: https://developers.reddit.com/docs/llms-full.txt Use this command to add products defined in your local src/products.json file to your app. This command is also run when you upload or playtest your app. ```bash devvit products add ``` -------------------------------- ### Uninstall Global CLI and Install as Dev Dependency Source: https://developers.reddit.com/docs/llms-full.txt If the CLI was installed globally, uninstall it and then install it as a development dependency within your project. This ensures version consistency. ```bash npm uninstall -g devvit npm install --save-dev devvit@latest ``` -------------------------------- ### Install or Update Devvit App on Subreddit Source: https://developers.reddit.com/docs/guides/faq Install your Devvit app on a subreddit or update an existing installation. This command ensures the subreddit has the desired app version. ```bash devvit install ``` -------------------------------- ### Example devvit.json Configuration Source: https://developers.reddit.com/docs/guides/tools/vite The Devvit Vite plugin uses `devvit.json` to determine client entry points. Ensure at least one entrypoint is defined. ```json { "post": { "dir": "dist/client", "entrypoints": { "default": { "inline": true, "entry": "splash.html" } } }, "server": { "dir": "dist/server", "entry": "index.ts" } } ``` -------------------------------- ### Update Global Devvit CLI Source: https://developers.reddit.com/docs/guides/tools/devvit_cli Update the globally installed Devvit CLI to the latest version. Note: Installing as a dev dependency is recommended over global installation. ```bash npm install -g devvit@latest ``` -------------------------------- ### Basic Server Usage of media.upload() Source: https://developers.reddit.com/docs/capabilities/server/media-uploads A basic example demonstrating how to upload an image using `media.upload()` on the server. ```APIDOC ## Basic server usage ```typescript import { media } from '@devvit/web/server'; const uploaded = await media.upload({ url: 'https://example.com/my-image.png', type: 'image', }); // uploaded.mediaId // uploaded.mediaUrl ``` ``` -------------------------------- ### mount() Method Source: https://developers.reddit.com/docs/api/public-api/type-aliases/UseWebViewResult Initiates a request to open the web view. ```APIDOC ### mount() > **mount**(): `void` Initiate a request for the web view to open #### Returns `void` ``` -------------------------------- ### Update Global CLI Installation Source: https://developers.reddit.com/docs/llms-full.txt Update a globally installed Devvit CLI to the latest version. Note that using the CLI as a dev dependency is generally recommended over global installation. ```bash npm install -g devvit@latest ``` -------------------------------- ### Create Post with Default Entry Point Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/view_modes_entry_points Use `reddit.submitCustomPost` to create a post, automatically using the 'default' entry point if none is specified. ```typescript import { reddit } from '@devvit/web/server'; // Create a post using the default entrypoint async function createDefaultPost(context: any) { return await reddit.submitCustomPost({ subredditName: context.subredditName!, title: 'Adventure Game', entry: 'default', postData: { gameState: 'menu', }, }); } ``` -------------------------------- ### getBestPosts Source: https://developers.reddit.com/docs/api/redditapi/RedditAPIClient/classes/RedditAPIClient Gets a list of best posts from the front page. This method will get the front page for the app account by default. To get the front page for a user, please contact Reddit. ```APIDOC ## getBestPosts() ### Description Get a list of best posts from the front page. This method will get the front page for the app account by default. To get the front page for a user, please contact Reddit. ### Parameters #### options - **options** (ListingFetchOptions) - Required - Options for the request ### Returns - **Listing** - A Listing of Post objects. ### Example ```javascript const posts = await reddit.getBestPosts({ limit: 1000, pageSize: 100 }).all(); ``` ``` -------------------------------- ### get() Source: https://developers.reddit.com/docs/api/public-api/type-aliases/TxClientLike Gets the value of a key from Redis. Returns nil if the key does not exist. ```APIDOC ## get() ### Description Get the value of key. If the key does not exist the special value nil is returned. ### Method `get(key: string): Promise` ### Parameters #### key - `key` (string) - The key to retrieve the value from. ### Returns - `Promise` - A promise that resolves to the value of the key or null if the key does not exist. ### Example ```javascript async function getExample(context) { await context.redis.set("quantity", "5"); const quantity = await context.redis.get("quantity"); console.log("Quantity: " + quantity); } ``` ``` -------------------------------- ### Configure Multiple Entry Points in devvit.json Source: https://developers.reddit.com/docs/capabilities/server/launch_screen_and_entry_points/view_modes_entry_points Define different entry points for your application in `devvit.json`. Each entry point can specify a unique HTML file and configuration, allowing users to launch your app from various contexts. ```json { "post": { "dir": "dist/client", "entrypoints": { "default": { "entry": "src/client/preview.html", "height": "regular", "inline": true }, "game": { "entry": "src/client/game.html" }, "leaderboard": { "entry": "src/client/leaderboard.html" } } } } ```