osw-studio / docs /SERVER_FEATURES.md
otst's picture
add scheduled functions (cron jobs) with admin UI and scheduler
734c970

Server Features

This guide covers OSW Studio's advanced server features, available in Server Mode only.

Overview

Server Mode unlocks powerful backend capabilities for your published sites, including serverless API endpoints, database management, and secure secrets storage.

Key Features:

  • Edge Functions - REST API endpoints with JavaScript runtime
  • Database - Per-site SQLite with SQL editor and schema browser
  • Server Functions - Reusable helper code for edge functions
  • Scheduled Functions - Run edge functions on cron schedules
  • Secrets - Encrypted storage for API keys and tokens
  • Logs - Execution history and debugging
  • AI Integration - AI awareness of server features via /.server/ folder

Prerequisites

  • OSW Studio running in Server Mode
  • A published site with databaseEnabled: true
  • Admin access to the site

Accessing Server Settings

  1. Open the Admin Dashboard (/admin)
  2. Navigate to Sites and select your site
  3. Click the Server Settings button (server icon) next to Site Settings
    • The server icon only appears for published sites with database enabled
    • You can also access it via the "..." dropdown menu → "Server Settings"

The Server Settings modal contains seven tabs:

  • Schema - Browse tables and columns
  • SQL - Execute raw SQL queries
  • Functions - Create and manage edge functions (HTTP endpoints)
  • Helpers - Create and manage server functions (reusable code)
  • Secrets - Store encrypted API keys and tokens
  • Schedules - Create and manage scheduled functions (cron jobs)
  • Logs - View function execution history

Edge Functions

Creating a Function

  1. Go to Server Settings → Functions

  2. Click New Function

  3. Configure:

    • Name: Lowercase letters, numbers, and hyphens (e.g., get-users)
    • HTTP Method: GET, POST, PUT, DELETE, or ANY
    • Description: Optional description
    • Timeout: 1-30 seconds (default: 5s)
    • Code: JavaScript function body
  4. Click Create Function

Function URL

Each function is accessible at:

https://your-server.com/api/sites/{siteId}/functions/{function-name}

For example:

https://oswstudio.com/api/sites/abc123/functions/get-products

Calling Edge Functions from Published Sites

Published sites automatically route edge function calls! Your frontend JavaScript can call functions using simple paths:

// In your published site's JavaScript
const response = await fetch('/submit-contact', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});
const result = await response.json();

This works because OSW Studio injects a lightweight interceptor script (~1.5KB) into published HTML files that:

  • Detects requests that look like edge function calls (paths without file extensions)
  • Routes them to /api/sites/{siteId}/functions/{path}
  • Works with fetch(), XMLHttpRequest, and form submissions

Form submissions are also intercepted:

<form action="/submit-contact" method="POST">
  <input name="email" type="email" required>
  <button type="submit">Subscribe</button>
</form>

The form data is automatically converted to JSON and sent to your edge function.

Custom event handling:

// Listen for edge function responses
document.addEventListener('edge-function-response', (e) => {
  console.log('Result:', e.detail.result);
});

document.addEventListener('edge-function-error', (e) => {
  console.error('Error:', e.detail.error);
});

Available APIs

Your function code has access to these global objects:

request Object

request.method   // HTTP method (GET, POST, etc.)
request.body     // Parsed JSON body (POST/PUT/PATCH)
request.query    // Query string parameters
request.headers  // Request headers
request.path     // URL path after function name
request.params   // Path parameters (if any)

db Object (Database)

// Execute SELECT queries
const users = db.query('SELECT * FROM users WHERE active = ?', [true]);
const user = db.all('SELECT * FROM users LIMIT 10'); // alias for query

// Execute INSERT/UPDATE/DELETE
const result = db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
// result = { changes: 1 }

Response Object

// Return JSON
Response.json({ users: [...] });
Response.json({ error: 'Not found' }, 404);

// Return plain text
Response.text('Hello World');
Response.text('Created', 201);

// Return error
Response.error('Something went wrong', 500);
Response.error('Unauthorized', 401);

fetch Function

// Make external HTTP requests
const response = await fetch('https://api.example.com/data');
const data = await response.json();
Response.json(data);

// With options
const res = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: { name: 'John', email: 'john@example.com' }
});

Security Limits:

  • Max 10 requests per function execution
  • 10 second timeout per request
  • 5MB max response body
  • Only http:// and https:// protocols allowed
  • Private IPs blocked in production (localhost, 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 169.254.x.x)
  • Development mode allows local requests for testing

atob / btoa Functions

// Base64 encode
const encoded = btoa('Hello World');  // "SGVsbG8gV29ybGQ="

// Base64 decode
const decoded = atob('SGVsbG8gV29ybGQ=');  // "Hello World"

server Object (Helper Functions)

// Call server functions (helpers) defined in the Helpers tab
const auth = server.validateAuth(request.headers['x-api-key']);
const formatted = server.formatPrice(29.99, 'USD');
const user = server.getUserById(123);

See Server Functions (Helpers) for more details.

secrets Object (Encrypted Secrets)

// Get secret value by name
const apiKey = secrets.get('STRIPE_API_KEY');
if (!apiKey) {
  Response.error('Stripe not configured', 500);
  return;
}

// Check if secret exists
if (secrets.has('SENDGRID_KEY')) {
  // Use SendGrid
}

// List all available secret names
const allSecrets = secrets.list(); // ['STRIPE_API_KEY', 'SENDGRID_KEY', ...]

See Secrets for more details.

Example Functions

List Items (GET)

// GET /api/sites/{siteId}/functions/list-items
const items = db.query('SELECT * FROM items ORDER BY created_at DESC LIMIT 20');
Response.json({ items });

Create Item (POST)

// POST /api/sites/{siteId}/functions/create-item
if (!request.body.name) {
  Response.error('Name is required', 400);
  return;
}

const result = db.run(
  'INSERT INTO items (name, description) VALUES (?, ?)',
  [request.body.name, request.body.description || '']
);

Response.json({
  id: result.lastInsertRowid,
  message: 'Item created'
}, 201);

Get Item by ID (GET with path)

// GET /api/sites/{siteId}/functions/get-item/123
const id = request.path.split('/')[1];
if (!id) {
  Response.error('ID required', 400);
  return;
}

const item = db.query('SELECT * FROM items WHERE id = ?', [id]);
if (item.length === 0) {
  Response.error('Item not found', 404);
  return;
}

Response.json(item[0]);

External API Proxy

// GET /api/sites/{siteId}/functions/weather?city=London
const city = request.query.city || 'New York';
const apiKey = secrets.get('WEATHER_API_KEY');
if (!apiKey) {
  Response.error('Weather API not configured', 500);
  return;
}

const res = await fetch(
  `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}`
);
const data = await res.json();

Response.json({
  city: data.location.name,
  temp: data.current.temp_c,
  condition: data.current.condition.text
});

Security Considerations

Sandboxed Execution

  • Functions run in a QuickJS WebAssembly sandbox - a completely separate JavaScript engine
  • True isolation via WASM boundary: no shared memory or access to Node.js internals
  • Memory limits enforced by WASM (64MB default)
  • Execution time limits with interrupt handler (configurable 1-30 seconds)
  • Allowed globals: JSON, Date, Math, Array, Object, String, Number, Boolean, RegExp, Error, Map, Set, Promise, Symbol, console
  • Network: fetch (with security limits - see above)
  • Utility functions: parseInt, parseFloat, isNaN, isFinite, encodeURIComponent, decodeURIComponent, encodeURI, decodeURI
  • Base64: atob (decode), btoa (encode)
  • No access to: require, process, __dirname, Buffer, file system
  • setTimeout/setInterval disabled (prevents runaway execution)

Database Protection

System tables are protected and cannot be accessed:

  • site_info
  • files
  • file_tree_nodes
  • pageviews
  • interactions
  • sessions
  • edge_functions
  • function_logs
  • server_functions
  • secrets

Only user-created tables are accessible via the db API.

Query Limits

  • Maximum 100 database queries per function execution
  • Timeout enforced (1-30 seconds, configurable)
  • SQL keywords validated to prevent dangerous operations

Managing Functions

Enable/Disable

Click the dropdown menu on a function card and select Enable or Disable. Disabled functions return 404.

Edit

Click Edit in the dropdown to modify the function code, method, or timeout.

Delete

Click Delete in the dropdown. This cannot be undone.

Copy URL

Click Copy URL below the function card to copy the public endpoint URL.


Server Functions (Helpers)

Server functions are reusable JavaScript helpers that can be called from your edge functions via the server object. They enable code reuse across multiple edge functions.

Creating a Server Function

  1. Go to Server Settings → Helpers
  2. Click New Helper
  3. Configure:
    • Name: Valid JavaScript identifier (camelCase or snake_case, e.g., validateAuth, format_price)
    • Description: Optional description
    • Code: JavaScript function body
  4. Click Create Function

How Server Functions Work

Server functions receive arguments via an args array and have access to db, fetch, and console. They return a value that is passed back to the calling edge function.

// Server function "validateAuth"
const [apiKey] = args;
if (!apiKey) {
  return { valid: false, error: 'No API key provided' };
}

const users = db.query('SELECT * FROM users WHERE api_key = ?', [apiKey]);
if (users.length === 0) {
  return { valid: false, error: 'Invalid API key' };
}

return { valid: true, user: users[0] };

Calling from Edge Functions

Server functions are available on the server object. Pass arguments as regular function parameters:

// Edge function code
const auth = server.validateAuth(request.headers['x-api-key']);
if (!auth.valid) {
  Response.error(auth.error, 401);
  return;
}

// User is authenticated
const products = db.query(
  'SELECT * FROM products WHERE user_id = ?',
  [auth.user.id]
);
Response.json({ products });

Available APIs in Server Functions

API Description
args Array of arguments passed from edge function
db.query() Execute SELECT query
db.run() Execute INSERT/UPDATE/DELETE
db.all() Alias for query
fetch() Make external HTTP requests
console.log() Log messages (visible in function logs)

Example Server Functions

Validate API Key

// Name: validateAuth
const [apiKey] = args;
if (!apiKey) return { valid: false };

const users = db.query('SELECT id, name, role FROM users WHERE api_key = ?', [apiKey]);
return users.length > 0 ? { valid: true, user: users[0] } : { valid: false };

Format Price

// Name: formatPrice
const [amount, currency = 'USD'] = args;
const symbols = { USD: '$', EUR: '€', GBP: '£', JPY: '¥' };
const symbol = symbols[currency] || currency + ' ';
return symbol + amount.toFixed(2);

Get User by ID

// Name: getUserById
const [id] = args;
if (!id) return null;

const users = db.query('SELECT * FROM users WHERE id = ?', [id]);
return users.length > 0 ? users[0] : null;

Check Permission

// Name: hasPermission
const [userId, permission] = args;
if (!userId || !permission) return false;

const perms = db.query(
  'SELECT 1 FROM user_permissions WHERE user_id = ? AND permission = ?',
  [userId, permission]
);
return perms.length > 0;

Security Notes

  • Server functions run in the same QuickJS WASM context as the parent edge function
  • They share the total execution timeout (not additive)
  • Recursive calls are possible but limited by timeout
  • The server_functions table is protected and cannot be queried
  • Only enabled server functions are available to edge functions

Managing Server Functions

  • Enable/Disable: Toggle from the dropdown menu. Disabled functions are not available to edge functions.
  • Edit: Click Edit to modify the code or description
  • Delete: Click Delete to remove (cannot be undone)

Secrets

Secrets provide secure, encrypted storage for sensitive values like API keys, tokens, and passwords. Edge functions can access secrets via the secrets object without exposing the actual values in your code.

Prerequisites

Before using secrets, you must set the SECRETS_ENCRYPTION_KEY environment variable:

# Generate a secure 256-bit key
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

# Add to your environment
export SECRETS_ENCRYPTION_KEY="your-generated-key-here"

Creating a Secret

  1. Go to Server Settings → Secrets
  2. Click New Secret
  3. Configure:
    • Name: SCREAMING_SNAKE_CASE (e.g., STRIPE_API_KEY, SENDGRID_TOKEN)
    • Value: The secret value (will be encrypted)
    • Description: Optional description
  4. Click Create Secret

Using Secrets in Edge Functions

The secrets object is available in all edge functions:

// Get a secret value
const apiKey = secrets.get('STRIPE_API_KEY');

// Check if a secret exists
if (secrets.has('STRIPE_API_KEY')) {
  // Use the secret
}

// List all available secret names (not values)
const names = secrets.list(); // ['STRIPE_API_KEY', 'SENDGRID_TOKEN', ...]

Example: Stripe API Integration

// POST /api/sites/{siteId}/functions/create-charge
const stripeKey = secrets.get('STRIPE_API_KEY');
if (!stripeKey) {
  Response.error('Stripe not configured', 500);
  return;
}

const { amount, currency, source } = request.body;
if (!amount || !source) {
  Response.error('Amount and source are required', 400);
  return;
}

const res = await fetch('https://api.stripe.com/v1/charges', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${stripeKey}`,
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: new URLSearchParams({
    amount: String(amount),
    currency: currency || 'usd',
    source,
  }),
});

const charge = await res.json();
Response.json({ charge });

Secrets API Reference

Method Description
secrets.get(name) Get secret value, or null if not found
secrets.has(name) Check if secret exists (returns boolean)
secrets.list() Get array of all secret names (not values)

Security Notes

  • Encryption: Secrets are encrypted using AES-256-GCM with unique IVs per secret
  • Never logged: Secret values are never written to logs or exposed in API responses
  • Admin-only: Only authenticated admins can create, view (metadata only), or delete secrets
  • Protected table: The secrets table cannot be queried directly from edge functions
  • Key management: The master encryption key must be stored securely as an environment variable

Managing Secrets

  • Edit: Click Edit in the dropdown to update the value or description (name cannot be changed)
  • Delete: Click Delete to permanently remove (cannot be undone)
  • No value display: Secret values are never displayed after creation for security

Scheduled Functions (Cron Jobs)

Scheduled functions run edge functions automatically on a cron schedule. Use them for periodic tasks like database cleanup, report generation, cache warming, or external API syncing.

Creating a Scheduled Function

  1. Go to Server Settings → Schedules
  2. Click New Schedule
  3. Configure:
    • Name: Lowercase letters, numbers, and hyphens (e.g., daily-cleanup)
    • Edge Function: Select which edge function to invoke
    • Cron Expression: Standard 5-field cron syntax (e.g., 0 8 * * *)
    • Timezone: IANA timezone (default: UTC)
    • Description: Optional description
    • Config: Optional JSON object passed as the request body
  4. Click Create Schedule

How It Works

When a scheduled function fires:

  1. The cron scheduler triggers at the specified time
  2. The linked edge function is invoked with the config object as request.body
  3. The execution result (success/error) and duration are recorded
  4. The next run time is calculated from the cron expression

The edge function runs in the same QuickJS sandbox as HTTP-triggered invocations, with full access to db, fetch, secrets, server, and console.

Cron Expression Reference

Cron expressions use 5 fields: minute hour day-of-month month day-of-week

Minimum interval: 5 minutes. Expressions that resolve to intervals shorter than 5 minutes will be rejected.

Expression Description
*/5 * * * * Every 5 minutes
0 * * * * Every hour (at minute 0)
0 8 * * * Daily at 8:00 AM
0 0 * * * Daily at midnight
30 9 * * 1-5 Weekdays at 9:30 AM
0 0 * * 1 Every Monday at midnight
0 0 1 * * First of every month at midnight
0 0 1 1 * January 1st at midnight (yearly)

Field ranges:

  • Minute: 0-59
  • Hour: 0-23
  • Day of month: 1-31
  • Month: 1-12
  • Day of week: 0-7 (0 and 7 = Sunday)

Example Scheduled Functions

Daily Database Cleanup

Clean up old records every day at 3:00 AM UTC:

  • Edge Function (cleanup):
const daysToKeep = request.body.daysToKeep || 30;
const cutoff = new Date(Date.now() - daysToKeep * 86400000).toISOString();

const result = db.run('DELETE FROM logs WHERE created_at < ?', [cutoff]);
Response.json({ deleted: result.changes, cutoff });
  • Schedule config:
    • Cron: 0 3 * * *
    • Config: { "daysToKeep": 30 }

Hourly Stats Aggregation

Aggregate analytics data every hour:

  • Edge Function (aggregate-stats):
const hourAgo = new Date(Date.now() - 3600000).toISOString();
const stats = db.query('SELECT COUNT(*) as views FROM pageviews WHERE timestamp > ?', [hourAgo]);

db.run('INSERT INTO hourly_stats (hour, views) VALUES (?, ?)',
  [new Date().toISOString().slice(0, 13), stats[0].views]);

Response.json({ aggregated: true, views: stats[0].views });
  • Schedule config:
    • Cron: 0 * * * *
    • Config: {}

Weekly Report Email

Send a weekly summary every Monday at 9:00 AM:

  • Edge Function (send-weekly-report):
const apiKey = secrets.get('SENDGRID_KEY');
if (!apiKey) { Response.error('Email not configured', 500); return; }

const stats = db.query('SELECT COUNT(*) as total FROM orders WHERE created_at > datetime("now", "-7 days")');

const res = await fetch('https://api.sendgrid.com/v3/mail/send', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    personalizations: [{ to: [{ email: request.body.recipient }] }],
    from: { email: 'reports@example.com' },
    subject: 'Weekly Report',
    content: [{ type: 'text/plain', value: 'Orders this week: ' + stats[0].total }]
  })
});

Response.json({ sent: res.ok });
  • Schedule config:
    • Cron: 0 9 * * 1
    • Timezone: America/New_York
    • Config: { "recipient": "admin@example.com" }

Managing Scheduled Functions

  • Enable/Disable: Toggle from the dropdown menu. Disabled schedules won't fire.
  • Edit: Click Edit to modify the cron expression, linked function, timezone, or config.
  • Delete: Click Delete to remove (cannot be undone).
  • Status tracking: Each schedule card shows the next run time, last run status (success/error), and last run time.

SQL Editor

The SQL Editor allows direct SQL query execution against your site's database.

Executing Queries

  1. Go to Server Settings → SQL
  2. Type your SQL query in the editor
  3. Click Execute or press Ctrl/Cmd + Enter
  4. View results in the table below

Query History

The editor maintains a history of your last 20 queries (stored in browser localStorage).

Click History to view and re-run previous queries.

Supported Operations

-- SELECT queries
SELECT * FROM products WHERE price > 100;
SELECT COUNT(*) FROM orders;

-- INSERT
INSERT INTO products (name, price) VALUES ('Widget', 29.99);

-- UPDATE
UPDATE products SET price = 24.99 WHERE id = 1;

-- DELETE
DELETE FROM products WHERE discontinued = 1;

-- CREATE TABLE
CREATE TABLE products (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  price REAL DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- ALTER TABLE
ALTER TABLE products ADD COLUMN stock INTEGER DEFAULT 0;

-- DROP TABLE (use with caution!)
DROP TABLE old_products;

Query Safety

  • System tables are accessible (read-only for some operations)
  • All queries are executed with full permissions - use caution!
  • No automatic transaction management - consider wrapping related operations

Schema Viewer

The Schema Viewer displays your database structure.

Viewing Tables

  1. Go to Server Settings → Schema
  2. Click on a table name to expand and view columns
  3. Each column shows: name, type, nullable, default value, primary key status

System Tables

Toggle Show System Tables to view OSW Studio's internal tables:

  • site_info - Site metadata
  • files - File contents
  • file_tree_nodes - File tree structure
  • pageviews / interactions / sessions - Analytics data
  • edge_functions - Function definitions
  • function_logs - Execution logs

System tables are marked with a "(system)" label.


Execution Logs

View function execution history in the Logs tab.

Log Information

Each log entry shows:

  • Status: Success (2xx), redirect (3xx), or error (4xx/5xx)
  • Function: Function name
  • Method: HTTP method used
  • Path: Request path
  • Duration: Execution time in milliseconds
  • Time: Timestamp

Managing Logs

  • Click Refresh to load latest logs
  • Click Clear to delete all logs (cannot be undone)
  • Logs are limited to the most recent 200 entries

Best Practices

Function Design

  1. Keep functions focused - One function per operation
  2. Validate input - Check request.body and request.query before use
  3. Handle errors - Return appropriate HTTP status codes
  4. Use meaningful names - create-order not func1

Database Usage

  1. Use parameterized queries - Prevents SQL injection

    // Good
    db.query('SELECT * FROM users WHERE id = ?', [userId]);
    
    // Bad - SQL injection risk!
    db.query(`SELECT * FROM users WHERE id = ${userId}`);
    
  2. Create indexes for frequently queried columns:

    CREATE INDEX idx_orders_user_id ON orders(user_id);
    
  3. Limit result sets to avoid memory issues:

    db.query('SELECT * FROM products LIMIT 100');
    

Performance

  1. Set appropriate timeouts - Don't use 30s if 5s is sufficient
  2. Minimize external requests - Each fetch() adds latency
  3. Cache when possible - Store API responses in your database

Troubleshooting

Function Returns 404

  • Check that the function is enabled
  • Verify the function name in the URL is correct
  • Ensure the site is published and has database enabled

Function Returns 500

  • Check the Logs tab for error details
  • Verify your SQL queries are valid
  • Ensure external APIs are responding

Query Execution Failed

  • Check SQL syntax
  • Verify table and column names
  • Look for constraint violations (unique, foreign key)

Cannot Access Table

  • System tables are protected in edge functions
  • Use the SQL Editor for system table access

API Reference

Public Endpoints

Method URL Description
* /api/sites/{siteId}/functions/{name} Invoke edge function
* /api/sites/{siteId}/functions/{name}/* Invoke with path params

Admin Endpoints (Requires Authentication)

Method URL Description
GET /api/admin/sites/{siteId}/functions List edge functions
POST /api/admin/sites/{siteId}/functions Create edge function
GET /api/admin/sites/{siteId}/functions/{id} Get edge function
PUT /api/admin/sites/{siteId}/functions/{id} Update edge function
DELETE /api/admin/sites/{siteId}/functions/{id} Delete edge function
GET /api/admin/sites/{siteId}/server-functions List server functions
POST /api/admin/sites/{siteId}/server-functions Create server function
GET /api/admin/sites/{siteId}/server-functions/{id} Get server function
PUT /api/admin/sites/{siteId}/server-functions/{id} Update server function
DELETE /api/admin/sites/{siteId}/server-functions/{id} Delete server function
GET /api/admin/sites/{siteId}/scheduled-functions List scheduled functions
POST /api/admin/sites/{siteId}/scheduled-functions Create scheduled function
GET /api/admin/sites/{siteId}/scheduled-functions/{id} Get scheduled function
PUT /api/admin/sites/{siteId}/scheduled-functions/{id} Update scheduled function
DELETE /api/admin/sites/{siteId}/scheduled-functions/{id} Delete scheduled function
GET /api/admin/sites/{siteId}/secrets List secrets (metadata only)
POST /api/admin/sites/{siteId}/secrets Create secret
GET /api/admin/sites/{siteId}/secrets/{id} Get secret (metadata only)
PUT /api/admin/sites/{siteId}/secrets/{id} Update secret
DELETE /api/admin/sites/{siteId}/secrets/{id} Delete secret
GET /api/admin/sites/{siteId}/database/schema Get schema
POST /api/admin/sites/{siteId}/database/query Execute SQL
GET /api/admin/sites/{siteId}/database/logs Get logs
DELETE /api/admin/sites/{siteId}/database/logs Clear logs

AI Integration

OSW Studio's AI assistant can understand and work with your server features when you select a site in the workspace.

How It Works

  1. Select a Site - Use the site selector dropdown in the workspace header
  2. Server Context Loaded - OSW Studio fetches the site's server features
  3. AI Awareness - The AI receives information about available:
    • Edge functions (endpoints, methods)
    • Database schema (tables, columns)
    • Server functions (helpers)
    • Scheduled functions (cron schedules)
    • Secrets (names only, not values)

The /.server/ Folder

When a site is selected, a hidden /.server/ folder appears in the file explorer containing:

Folder Contents
edge-functions/*.json Edge function endpoints
server-functions/*.json Helper functions
scheduled-functions/*.json Cron schedules
secrets/*.json Secret names (not values)
db/schema.sql Database schema

These files are read-only and transient - they reflect the current site's state but are not saved with the project.

Using AI with Server Features

Example prompts:

What tables are in the database?
Create an edge function to list all products
I need an endpoint that validates API keys using the STRIPE_KEY secret
Create a scheduled function to clean up old records every night
Help me design a schema for a blog with posts and comments

The AI can:

  • Read and explain your current schema
  • Suggest edge function implementations
  • Reference available secrets by name
  • Help design database structures
  • Debug function issues

Viewing Hidden Files

To see the /.server/ folder:

  1. Right-click in the File Explorer
  2. Select Show Hidden Files
  3. Look for the folder with the orange server icon

See also: Server Mode → Server Context Integration