Stage 2 — Give it data
Create a managed Postgres database, bind it with one redeploy, and watch Lumen migrate and seed itself — then query the 54 seeded incidents directly.
Lumen is live but empty. This stage gives it a managed Postgres database. You create the database, redeploy once to pick up its connection string, and the app migrates and seeds itself on boot — the first-run banner disappears and there are incidents to read.
Give Lumen a managed Postgres database and bind it.
Create a managed Dibbla Postgres database named "lumen_incidents" — org-wide
scope, not scoped to a single deployment, because a later worker will need it
too. Then redeploy the current app with --update so it picks up the injected
DATABASE_URL_LUMEN_INCIDENTS secret and migrates itself. When it is up, curl
/api/health and /api/incidents and report the total incident count. What happens next
Hand that prompt to your code assistant and it takes over the mechanics. It creates a managed Postgres database in your org, then redeploys the app once so the platform injects the database’s connection string as a secret. There is no URL to copy anywhere — creating the database is what binds it.
On that redeploy the app boots, finds the connection string, runs its own migrations to build the schema, and seeds itself. You do not run any of this by hand; the app does it the first time it sees a database.
When the assistant reports back, the app has changed. Open it and the first-run
banner is gone — the incident list is populated with fifty-four seeded
incidents, and the health check now says the database is "connected" instead
of "absent".
A code assistant is non-deterministic, so the exact commands it runs and the order it reports them may differ from one run to the next. That is fine — the end state is what matters: a database bound, the app migrated and seeded, and real incidents to read.
Under the hood the CLI & code behind this step — optional
1. Create the database
$dibbla db create lumen_incidents Secret: DATABASE_URL_LUMEN_INCIDENTS (auto-created) This is a global secret available to all deployments in your org. It will be injected automatically on every deploy.
Creating a database also creates the secret that carries its connection string. You do not copy a URL anywhere — the platform injects it.
A bare dibbla db create makes an org-wide database whose secret every
deployment in the org receives. Passing --deployment <alias> instead scopes
both the database and its secret to a single app. Either way there is no
separate link or bind command — the scope you pick at creation is the
binding.
This tutorial uses the org-wide form deliberately: Stage 5 adds a nightly
worker, which is a second deployment, and it needs the same database. Scope
it to lumen now and that later worker could not reach it.
Here the name is lumen_incidents, so the secret is
DATABASE_URL_LUMEN_INCIDENTS. This holds whatever the scope — there is no
bare DATABASE_URL. Read the connection string from the _<NAME> variable in
your app’s config.
2. Redeploy to pick it up
The secret is injected at deploy time, so the running app does not see it until
you redeploy. Use --update to ship a new version of the existing app:
$dibbla deploy --update -m "chore: bind lumen_incidents" deploy ok · https://lumen-0ee1f995.dibbla.app · 3.2s
On boot the app finds DATABASE_URL_LUMEN_INCIDENTS, runs its migrations to
create the schema, and seeds it.
3. Check it
The health check now reports the database as connected, and the incidents
endpoint returns seeded rows in an {"items":[...],"total":N} envelope:
$curl https://lumen-0ee1f995.dibbla.app/api/health {"app":"Lumen","database":"connected","status":"ok"} $curl https://lumen-0ee1f995.dibbla.app/api/incidents {"items":[ ... ],"total":54}
Fifty-four incidents were seeded. Open the app and the first-run banner is gone; the incident list is populated.
You can also connect to the database directly. dibbla db connect with -q
prints just the connection string, so you can hand it straight to psql:
$psql "$(dibbla db connect lumen_incidents -q)" The seed data is real enough to ask real questions of. Open incidents by region:
SELECT region, count(*)
FROM incidents
WHERE resolved_at IS NULL
GROUP BY region
ORDER BY 2 DESC;
region | count
------------+-------
ap-south | 5
us-east | 5
eu-north | 4
eu-central | 4
(18 open) Recent high-severity incidents, joined to the service they hit:
SELECT i.title, s.name, i.opened_at
FROM incidents i
JOIN services s ON s.id = i.service_id
WHERE i.severity IN ('high', 'critical')
AND i.opened_at > now() - interval '30 days'
ORDER BY i.opened_at DESC;
-- returns several rows: recent high/critical incidents with their service Mean time to resolution per region:
SELECT region, avg(resolved_at - opened_at) AS mttr
FROM incidents
WHERE resolved_at IS NOT NULL
GROUP BY region;
-- all four regions return a value Next: data it can reason about
Lumen has fifty-four incidents, each with a free-text body nobody has time to read in full. Stage 3 adds an endpoint that summarises one with AI — using your Dibbla token as the model key.
Also at this point
- Managed storage — data has meant managed Postgres so far. Files are the other half of that sentence, and having just connected a database you may already be wondering where uploads would go. Lumen doesn’t need file storage to be the app it is, so this stays a pointer, not a step.