How to add a PostgreSQL or Redis database to a coding challenge
Cet article n'est pas encore traduit : il s'affiche en anglais.
List PostgreSQL 18 or Valkey 9 under databases in kendor.yaml. They run inside the sandbox, and code connects with DATABASE_URL or REDIS_URL.
To give a challenge a database, list it under databases in kendor.yaml. Kendor runs PostgreSQL 18
and Valkey 9 (Redis compatible) inside the candidate's sandbox on 127.0.0.1, starts them before any
install command, and sets DATABASE_URL and REDIS_URL for every process, so the code connects
without any setup from the candidate.
Declare a database
version: 1
runtime:
type: node
version: "22"
databases:
- type: postgres
version: "18"
init: db/seed.sql
- type: valkey
version: "9"
services:
app:
install:
commands:
- npm ci
dev:
command: node --watch src/server.js
port: 3000
test:
command: node --test| Key | Required | Description |
|---|---|---|
type |
Yes | postgres or valkey. Each at most once. |
version |
Yes | "18" for PostgreSQL, "9" for Valkey, as a quoted string. |
name |
No | PostgreSQL only. The database to create. Default app. Lowercase letters, digits and underscores, starting with a letter or underscore, and not postgres, template0 or template1. |
init |
No | PostgreSQL only. A .sql file, or a folder of them, relative to the workspace root. |
You can also add databases in the Runtime configuration dialog under Databases, with an optional Seed file. The Express + PostgreSQL + Valkey starter for Node.js is a complete working example.
Seed data with init
init runs once, right after the database is created:
- A file runs on its own. A folder runs every
.sqlfile in it in name order, so prefix files with numbers:01_schema.sql,02_data.sql. - Everything runs in one transaction as the app's own user. If any statement fails, nothing is applied.
- If the seed fails while the sandbox is running, the database keeps running and the error shows in its process. Fix the file, then restart the Database process from the Processes view to run it again.
Migrations that your framework runs (prisma migrate deploy, php artisan migrate) can go in an
install command instead: databases are already up when install commands start.
Connection details
Both servers listen on 127.0.0.1 only. PostgreSQL connects as the user kendor with no password,
which owns the database but isn't a superuser.
PostgreSQL
| Variable | Value |
|---|---|
DATABASE_URL |
postgresql://kendor@127.0.0.1:5432/app (with your name in place of app) |
PGHOST, PGPORT, PGUSER, PGDATABASE |
127.0.0.1, 5432, kendor, the database name |
DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD |
The same values, for Laravel. DB_PASSWORD is empty. |
SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD |
jdbc:postgresql://127.0.0.1:5432/app, kendor and empty, for Spring Boot. |
Because these are real environment variables, they take precedence over a Laravel .env file or a
Spring application.properties.
Valkey
| Variable | Value |
|---|---|
REDIS_URL, VALKEY_URL |
redis://127.0.0.1:6379 |
REDIS_HOST, REDIS_PORT |
127.0.0.1, 6379 |
SPRING_DATA_REDIS_HOST, SPRING_DATA_REDIS_PORT |
The same, for Spring Boot. |
Valkey speaks the Redis protocol, so any Redis client works.
If your code reads a different variable name, map it in the service's dev.env.vars with
databases.postgres.url or databases.valkey.url:
version: 1
runtime:
type: python
version: "3.12"
databases:
- type: postgres
version: "18"
name: inventory
services:
app:
install:
commands:
- pip install --user -r requirements.txt
dev:
command: uvicorn main:app --host $HOST --port $PORT --reload
port: 8000
env:
vars:
SQLALCHEMY_DATABASE_URI: databases.postgres.urlCandidates see the same details in the editor: the Processes view has a Databases section
with each connection URL and every variable set for it. psql and valkey-cli work in the terminal
without arguments.
Connect from code
Read the URL from the environment. Add the client library to your project's dependencies (pg and
redis for Node.js, psycopg and redis for Python) so the install command fetches it.
import pg from 'pg';
import { createClient } from 'redis';
export const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
export const cache = createClient({ url: process.env.REDIS_URL });
await cache.connect();
const { rows } = await pool.query('SELECT id, title FROM todos ORDER BY id');
await cache.set('todos', JSON.stringify(rows));import json
import os
import psycopg
import redis
conn = psycopg.connect(os.environ["DATABASE_URL"])
cache = redis.Redis.from_url(os.environ["REDIS_URL"])
with conn.cursor() as cur:
cur.execute("SELECT id, title FROM todos ORDER BY id")
rows = cur.fetchall()
cache.set("todos", json.dumps(rows))What happens to the data
During a session, data persists. Rows are stored in the sandbox's own cache folder, so they survive
a restart of the database process or of the whole sandbox, and the init seed doesn't run a second
time.
The Tests tab runs against that same live database. Write tests that create the rows they need and clean up after themselves, or wrap each test in a transaction, so a candidate's experiments in the terminal can't change the result.
Limits
| Limit | |
|---|---|
| PostgreSQL connections | 20 |
| PostgreSQL temporary files | 1 GB per connection |
| Valkey memory | 64 MB |
| Data on disk | 1 GB; past that the database servers stop, and the Database process says how to start over with an empty database |
The database ports, 5432 and 6379, are never exposed through the preview, even if a server listens on them.