Project Structure
A Quantum web app is a folder. quantum start, run inside it, serves it.
my-app/
├── quantum.config.yaml datasources and server settings
├── components/ one .q file per page (and reusable components)
│ ├── index.q /
│ ├── about.q /about
│ └── shop/
│ ├── index.q /shop
│ └── [id].q /shop/41 (id = 41)
├── static/ served as-is under /static/
├── migrations/ V001_create_users.sql, applied by `quantum migrate up`
└── data/ your SQLite files, CSV/JSON for q:dataOnly components/ is required. The rules are ROUTE-1, DB-6 and CFG-1; the page below is served in CI (tests/docs/test_guide_project_structure.py).
Pages and URLs
Each .q file in components/ is served at its path:
| File | URL |
|---|---|
components/index.q | / |
components/about.q | /about |
components/shop/index.q | /shop |
components/shop/[id].q | /shop/<anything> |
A [name] segment matches any value and hands it to the page as the parameter name. Save as components/shop/[id].q:
<q:component name="product" xmlns:q="https://quantum.lang/ns">
<q:param name="id" type="integer" />
<q:query name="product" datasource="db">
SELECT name, price FROM products WHERE id = :id
<q:param name="id" value="{id}" type="integer" />
</q:query>
<h1>{product.name}</h1>
</q:component>With the products of the example database, /shop/2 shows Mouse. A URL with no matching file answers 404.
quantum.config.yaml
server:
port: 8080
host: 127.0.0.1
datasources:
db:
driver: sqlite
database: ./data/app.dbValues can come from environment variables: password: ${DB_PASSWORD}. See Installation and Database Queries.
Migrations
quantum migrate create create_users # writes migrations/V001_create_users.sql and .down.sql
quantum migrate up # applies pending migrations
quantum migrate status
quantum migrate down # rolls back the last oneMigrations are applied to the datasource declared in quantum.config.yaml — the same database the pages query. With more than one datasource, choose it: quantum migrate --datasource db up.
Or: write the schema, let Quantum write the migration
Keep a schema.sql with the tables as they should be, and let quantum migrate plan work out the migration:
quantum migrate plan # what changes; what loses data is marked !!
quantum migrate plan --write add_tags # saves migrations/V00N_add_tags.sql (+ .down.sql), after asking
quantum migrate upPlan: schema.sql vs. the migrations in migrations/
+ add table tags
~ rebuild posts (+ slug; status: CHECK)
+ add index posts_slug- It compares
schema.sqlwith the schema the migrations produce (built in memory), not with your local database — the same answer on every machine. - A new nullable column (or one with a default) is
ADD COLUMN; any other change rebuilds the table and copies the rows over (SQLite cannot alter a column). - Dropping a table or a column, or changing a column's type, loses data: the plan says so and
--writerefuses it without--allow-data-loss. A newNOT NULLcolumn without a default is flagged: existing rows have no value. - The rollback is written next to it. Before writing, the plan is checked: applied to the migrations' schema, it must give exactly
schema.sql's. - SQLite for now.
projects/tarefaskeeps aschema.sql.
Reusable components
A component used inside pages is a .q file too: a page imports it with q:import and uses it as a tag. A file or folder whose name starts with _ (components/_parts/Card.q) is never served as a page (ROUTE-3). The example in Components runs in CI.
Next steps
- Quick Start — a page with a database and a form
- Actions & Forms