import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Configure your app with settings that can be customized per subreddit or globally across all installations. Settings allow moderators to customize app behavior for their subreddit, while secrets enable secure storage of sensitive data like API keys.
Settings come in two scopes:
- Subreddit settings: Configurable by moderators for each installation
- Global settings & Secrets: Set by developers and shared across all installations
:::warning
Local environment variables and .env files are read during playtesting only.
:::
Define settings in your devvit.json file under the settings object. Settings are organized by scope: global for app-wide settings and secrets, subreddit for installation-specific settings.
{
"settings": {
"global": {
"apiKey": {
"type": "string",
"label": "API Key",
"defaultValue": "",
"isSecret": true
},
"environment": {
"type": "select",
"label": "Environment",
"options": [
{
"label": "Production",
"value": "production"
},
{
"label": "Development",
"value": "development"
}
],
"defaultValue": "production"
}
},
"subreddit": {
"welcomeMessage": {
"type": "string",
"label": "Welcome Message",
"validationEndpoint": "/internal/settings/validate-message",
"defaultValue": "Welcome to our community!"
},
"enabledFeatures": {
"type": "multiSelect",
"label": "Enabled Features",
"options": [
{
"label": "Auto-moderation",
"value": "automod"
},
{
"label": "Welcome posts",
"value": "welcome"
},
{
"label": "Statistics tracking",
"value": "stats"
}
],
"defaultValue": ["welcome"]
}
}
}
}:::note
After defining settings in devvit.json, you must build your app (npm run dev) before you can set secrets via the CLI.
:::
The following setting types are supported:
- string: Text input field
- boolean: Toggle switch
- number: Numeric input
- select: Dropdown selection (single choice)
- multiSelect: Multiple choice dropdown
Secrets are global settings marked with isSecret: true. They're encrypted and can only be set by developers via the CLI.
View all defined secrets in your app:
npx devvit settings list
Key Label Is this a secret? Type
────────── ─────────── ───────────────── ──────
apiKey API Key true STRING
environment Environment false SELECTOnly app developers can set secret values:
npx devvit settings set apiKey
? Enter the value you would like to assign to the variable apiKey: <value>
Updating app settings... ✅
Successfully added app settings for apiKey!:::warning
At least one app installation is required before you can store secrets via the CLI. Run npm run dev to start your first installation.
:::
Settings can be retrieved from within your app.
<Tabs variant="pill" groupId="http-server-framework" defaultValue="hono" values={[ { label: 'Hono', value: 'hono' }, { label: 'Express', value: 'express' }, ]}>
import { settings } from '@devvit/web/server';
type ProcessResponse = { success: true };
// Get a single setting
const apiKey = await settings.get('apiKey');
// Get multiple settings
const [welcomeMessage, features] = await Promise.all([
settings.get('welcomeMessage'),
settings.get('enabledFeatures')
]);
// Use in an endpoint
app.post('/api/process', async (c) => {
const apiKey = await settings.get('apiKey');
const environment = await settings.get('environment');
const response = await fetch('https://api.example.com/endpoint', {
headers: {
'Authorization': `Bearer ${apiKey}`,
'X-Environment': environment
}
});
return c.json<ProcessResponse>({ success: true });
});import { settings } from '@devvit/web/server';
type ProcessResponse = { success: true };
// Get a single setting
const apiKey = await settings.get('apiKey');
// Get multiple settings
const [welcomeMessage, features] = await Promise.all([
settings.get('welcomeMessage'),
settings.get('enabledFeatures')
]);
// Use in an endpoint
router.post<string, never, ProcessResponse, never>('/api/process', async (req, res) => {
const apiKey = await settings.get('apiKey');
const environment = await settings.get('environment');
const response = await fetch('https://api.example.com/endpoint', {
headers: {
'Authorization': `Bearer ${apiKey}`,
'X-Environment': environment
}
});
res.json({ success: true });
});Validate user input to ensure it meets your requirements before saving. Define a validation endpoint in your devvit.json and implement it in your server:
{
"settings": {
"subreddit": {
"minimumAge": {
"type": "number",
"label": "Minimum Account Age (days)",
"validationEndpoint": "/internal/settings/validate-age",
"defaultValue": 7
}
}
}
}<Tabs variant="pill" groupId="http-server-framework" defaultValue="hono" values={[ { label: 'Hono', value: 'hono' }, { label: 'Express', value: 'express' }, ]}>
import type { SettingsValidationRequest, SettingsValidationResponse } from '@devvit/web/shared';
app.post('/internal/settings/validate-age', async (c) => {
const { value } = await c.req.json<SettingsValidationRequest<number>>();
if (!value || value < 0) {
return c.json<SettingsValidationResponse>({
success: false,
error: 'Age must be a positive number',
});
}
if (value > 365) {
return c.json<SettingsValidationResponse>({
success: false,
error: 'Maximum age is 365 days',
});
}
return c.json<SettingsValidationResponse>({ success: true });
});import type { SettingsValidationRequest, SettingsValidationResponse } from '@devvit/web/shared';
router.post<string, never, SettingsValidationResponse, SettingsValidationRequest<number>>(
'/internal/settings/validate-age',
async (req, res): Promise<void> => {
const { value } = req.body;
if (!value || value < 0) {
res.json({
success: false,
error: 'Age must be a positive number',
});
return;
}
if (value > 365) {
res.json({
success: false,
error: 'Maximum age is 365 days',
});
return;
}
res.json({ success: true });
}
);Once your app is installed, moderators can configure subreddit settings through the Install Settings page. These settings are scoped to the specific subreddit where the app is installed.
Moderators will see all non-secret settings defined for the subreddit scope and can update them as needed. Changes are saved immediately and available to your app.
Here's a complete example showing both secrets and subreddit settings in action:
{
"settings": {
"global": {
"openaiApiKey": {
"type": "string",
"label": "OpenAI API Key",
"isSecret": true,
"defaultValue": ""
}
},
"subreddit": {
"aiModel": {
"type": "select",
"label": "AI Model",
"options": [
{ "label": "GPT-4", "value": "gpt-4" },
{ "label": "GPT-3.5", "value": "gpt-3.5-turbo" }
],
"defaultValue": "gpt-3.5-turbo"
},
"maxTokens": {
"type": "number",
"label": "Max Response Tokens",
"validationEndpoint": "/internal/settings/validate-tokens",
"defaultValue": 150
}
}
}
}<Tabs variant="pill" groupId="http-server-framework" defaultValue="hono" values={[ { label: 'Hono', value: 'hono' }, { label: 'Express', value: 'express' }, ]}>
import type { JsonObject, JsonValue } from '@devvit/web/shared';
import { settings } from '@devvit/web/server';
type GenerateRequest = { messages: JsonValue };
type GenerateResponse = JsonObject;
app.post('/api/generate', async (c) => {
const [apiKey, model, maxTokens] = await Promise.all([
settings.get('openaiApiKey'),
settings.get('aiModel'),
settings.get('maxTokens')
]);
const { messages } = await c.req.json<GenerateRequest>();
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
max_tokens: maxTokens,
messages,
}),
});
const data = (await response.json()) as GenerateResponse;
return c.json<GenerateResponse>(data);
});import type { JsonObject, JsonValue } from '@devvit/web/shared';
import { settings } from '@devvit/web/server';
type GenerateRequest = { messages: JsonValue };
type GenerateResponse = JsonObject;
router.post<string, never, GenerateResponse, GenerateRequest>('/api/generate', async (req, res) => {
const [apiKey, model, maxTokens] = await Promise.all([
settings.get('openaiApiKey'),
settings.get('aiModel'),
settings.get('maxTokens')
]);
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
max_tokens: maxTokens,
messages: req.body.messages,
}),
});
const data = (await response.json()) as GenerateResponse;
res.json(data);
});- Secrets can only be global
- Secrets can only be set via CLI by app developers
- Setting values are currently not fully surfaced in the CLI
- Maximum of 2KB per setting value
