Skip to content

Configuration

Tova server blocks support a variety of declarative configuration blocks for CORS, rate limiting, sessions, file uploads, TLS, and more. Each block is placed directly inside the server { } block.

CORS

Configure Cross-Origin Resource Sharing to control which origins can access your server:

tova
server {
  cors {
    origins: ["https://example.com", "http://localhost:3000"]
    methods: ["GET", "POST", "PUT", "DELETE"]
    headers: ["Content-Type", "Authorization"]
    credentials: true
  }
}

Use origins: ["*"] to allow all origins (not recommended for production with credentials):

tova
cors {
  origins: ["*"]
  methods: ["GET", "POST"]
}
FieldTypeDescription
origins[String]Allowed origin URLs
methods[String]Allowed HTTP methods
headers[String]Allowed request headers
credentialsBoolWhether to allow cookies and auth headers

Rate Limiting

Protect your server from abuse by limiting the number of requests a client can make within a time window:

tova
server {
  rate_limit {
    max: 100          // maximum requests per window
    window: 60        // window duration in seconds
  }
}

When a client exceeds the limit, the server responds with 429 Too Many Requests.

FieldTypeDescription
maxIntMaximum number of requests allowed per window
windowIntTime window in seconds

Environment Variables

Declare typed environment variables with optional default values. Variables are validated at startup:

tova
server {
  env DATABASE_URL: String = "sqlite:./data.db"
  env PORT: Int = 3000
  env DEBUG: Bool = false
  env API_KEY: String        // required -- no default value
}
FeatureSyntaxBehavior
With defaultenv PORT: Int = 3000Uses the default if the variable is not set
Requiredenv API_KEY: StringServer fails to start if the variable is not set
String typeenv NAME: StringNo conversion needed
Int typeenv PORT: IntParsed as an integer at startup
Bool typeenv DEBUG: BoolParsed as a boolean ("true", "1" are truthy)

Environment variables are available as regular variables throughout the server block:

tova
server {
  env PORT: Int = 3000
  env DATABASE_URL: String

  db { url: DATABASE_URL }

  on_start fn() {
    print("Server running on port {PORT}")
  }
}

Static Files

Serve static files from a directory:

tova
server {
  static "/public" => "./public"
  static "/assets" => "./dist/assets"
}

The first argument is the URL path prefix, and the second is the filesystem directory.

SPA Fallback

For single-page applications, use the fallback keyword to serve a fallback file when no static file matches:

tova
static "/app" => "./dist" fallback "index.html"

This serves ./dist/index.html for any request under /app that does not match an existing file, enabling client-side routing.

Sessions

Enable server-side session management:

tova
server {
  session {
    secret: "your-session-secret"
    max_age: 86400              // session lifetime in seconds (24 hours)
    cookie_name: "__sid"        // name of the session cookie
  }
}
FieldTypeDescription
secretStringSecret used to sign the session cookie
max_ageIntSession lifetime in seconds
cookie_nameStringName of the session cookie (default: "__sid")

File Upload

Configure file upload limits and allowed types:

tova
server {
  upload {
    max_size: 10_000_000        // 10MB maximum file size
    allowed_types: ["image/png", "image/jpeg", "application/pdf"]
  }
}

Requests that exceed the size limit or send a disallowed content type are rejected with a 413 or 415 response.

FieldTypeDescription
max_sizeIntMaximum upload size in bytes
allowed_types[String]List of permitted MIME types

TLS / HTTPS

Enable TLS for encrypted connections:

tova
server {
  tls {
    cert: "./cert.pem"
    key: "./key.pem"
  }
}

When TLS is configured, the server listens on HTTPS. Both cert and key are paths to PEM-encoded files.

FieldTypeDescription
certStringPath to the TLS certificate file
keyStringPath to the TLS private key file

Compression

Enable response compression. Responses are compressed using gzip or deflate based on the client's Accept-Encoding header:

tova
server {
  compression {
    min_size: 1024              // only compress responses larger than 1KB
  }
}

The presence of the compression block enables compression. Responses smaller than min_size are sent uncompressed.

FieldTypeDescription
min_sizeIntMinimum response size in bytes to trigger compression

Caching

Configure default cache headers for responses:

tova
server {
  cache {
    max_age: 3600                        // Cache-Control max-age in seconds
    stale_while_revalidate: 60           // seconds to serve stale content while revalidating
  }
}

This sets the Cache-Control header on responses. Individual routes can override these defaults using the cache_control helper (see Advanced).

FieldTypeDescription
max_ageIntTime in seconds the response is considered fresh
stale_while_revalidateIntTime in seconds to serve stale content while fetching a fresh copy

Max Body Size

Set a global limit on request body size:

tova
server {
  max_body 10_000_000          // 10MB limit
}

Requests with bodies exceeding this limit are rejected with a 413 Payload Too Large response.

Health Checks

Add a health check endpoint with a single line:

tova
server {
  health "/health"
}

This creates a GET /health endpoint that returns { status: "ok" } with a 200 status code. Health checks are commonly used by load balancers, container orchestrators, and monitoring systems.

Complete Configuration Example

Here is a server block demonstrating multiple configuration options together:

tova
server {
  // Environment
  env PORT: Int = 3000
  env DATABASE_URL: String = "sqlite:./data.db"
  env JWT_SECRET: String

  // Database
  db { url: DATABASE_URL }

  // Auth
  auth { type: "jwt", secret: JWT_SECRET }

  // Security
  cors {
    origins: ["https://myapp.com"]
    methods: ["GET", "POST", "PUT", "DELETE"]
    headers: ["Content-Type", "Authorization"]
    credentials: true
  }
  rate_limit { max: 100, window: 60 }
  max_body 5_000_000

  // Performance
  compression { min_size: 1024 }
  cache { max_age: 3600 }

  // Static files
  static "/assets" => "./public/assets"
  static "/" => "./public/dist" fallback "index.html"

  // Sessions
  session { secret: "session-secret", max_age: 86400 }

  // Uploads
  upload { max_size: 10_000_000, allowed_types: ["image/png", "image/jpeg"] }

  // Health
  health "/health"

  // Routes
  route GET "/api/users" with auth => get_users
  route POST "/api/users" with auth, role("admin") => create_user
}

Practical Tips

Set rate limits early. Even a simple rate limit prevents a single client from overwhelming your server. Adjust the max and window values based on your expected traffic.

Use environment variables for secrets. Never hardcode secrets for sessions, JWT, or database connections. Use env declarations so values come from the runtime environment.

Enable compression for API responses. JSON responses compress well. A min_size of 1024 bytes avoids the overhead of compressing very small payloads.

Use SPA fallback for client-side routing. When serving a single-page application, the fallback option ensures that deep links (e.g., /app/settings/profile) serve the application shell instead of returning a 404.

Released under the MIT License.