{"title":"Web Template","description":"The verified files, database, and runtime contract generated by Amber CLI 2.0.6","section":"guides","version":"v2","path":"guides/web-template","canonical_url":"https://amberframework.org/docs/v2/guides/web-template","markdown_url":"https://amberframework.org/docs/v2/guides/web-template.md","inherited":false,"content_markdown":"# Amber V2 Web Template\n\nAmber CLI `2.0.6` generates a complete server-rendered web application with\nECR, Grant ORM, Micrate migrations, and SQLite. The first database-backed\nfeature needs no database server, Node.js process, or front-end package manager.\nThe same template includes the released build-time asset manifest, so its\nhomepage exercises the frontend contract documented for production apps.\n\n**Run from: the parent directory where `my_app/` should be created.**\n\n```bash\namber new my_app --type web\ncd my_app\n```\n\nWeb, ECR, and SQLite are the defaults, so `amber new my_app` is equivalent. Use\n`-d pg` or `-d mysql` when the application should start with a server database.\n\n## Where the examples go\n\nCommands on this page run from the generated application root unless a closer\nlabel says otherwise. Every code or tree example names its source file,\ngenerated output, or reference role. Replace `my_app` with the actual generated\ntarget name when a command includes it.\n\n## Generated project\n\n**Generated output: the top-level structure under `my_app/`.**\n\n```text\nmy_app/\n├── .amber.yml\n├── .gitignore\n├── README.md\n├── shard.yml\n├── config/\n│   ├── application.cr\n│   ├── assets.cr\n│   ├── database.cr\n│   ├── routes.cr\n│   └── environments/\n│       ├── development.yml\n│       ├── production.yml\n│       └── test.yml\n├── db/\n│   ├── migrations/\n│   └── seeds.cr\n├── app/assets/                                  # authored; source control\n│   ├── stylesheets/app.css\n│   ├── javascript/app.js\n│   ├── images/amber-crystal.svg\n│   ├── images/favicon.svg\n│   ├── fonts/.gitkeep\n│   └── files/.gitkeep\n├── public/\n│   ├── assets/                                  # generated; gitignored\n│   │   ├── manifest.json\n│   │   └── ...fingerprinted files...\n│   └── robots.txt\n├── spec/\n│   ├── spec_helper.cr\n│   ├── controllers/home_controller_spec.cr\n│   └── channels, jobs, mailers, models, requests, schemas/\n└── src/\n    ├── my_app.cr\n    ├── controllers/\n    │   ├── application_controller.cr\n    │   └── home_controller.cr\n    ├── views/\n    │   ├── home/index.ecr\n    │   └── layouts/application.ecr\n    └── channels, jobs, mailers, models, schemas, sockets/\n```\n\nThe empty extension directories give generators stable destinations. The\ngenerated `README.md` names those destinations again while a developer is\nworking inside the project.\n\n## Exact dependency contract\n\n**File: `shard.yml` — generated dependency manifest.**\n\n```yaml\ncrystal: \">= 1.20.0, < 2.0\"\n\ndependencies:\n  amber:\n    github: amberframework/amber\n    version: 2.0.0-beta.5\n  grant:\n    github: crimson-knight/grant\n    commit: 2665a978b43ac608c68cde9243821f8f8f053372\n  asset_pipeline:\n    github: amberframework/asset_pipeline\n    version: 0.37.0\n  sqlite3:\n    github: crystal-lang/crystal-sqlite3\n    version: ~> 0.23.0\n```\n\nThe exact Grant commit is intentional while its V2 release is finalized. Amber\nand Grant changes are not pulled from moving branches during application\ngeneration.\n\nThe CLI embeds its web scaffold in the executable. `amber new` does not fetch a\ntemplate manifest, so updating the CLI changes future projects but never\nsilently rewrites an existing application.\n\n## Database connection\n\n**File: `config/database.cr` — generated SQLite registration.**\n\n```crystal\nrequire \"amber\"\nrequire \"grant\"\nrequire \"grant/adapter/sqlite\"\n\nGrant::Connections << Grant::Adapter::Sqlite.new(\n  name: \"primary\",\n  url: ENV[\"DATABASE_URL\"]? || Amber.settings.database_url\n)\n```\n\nEvery generated Grant model declares `connection primary`. `DATABASE_URL`\noverrides the environment YAML, which makes production configuration explicit\nwithout putting credentials in source control.\n\n**File: `config/environments/development.yml` — generated development values.**\n\n```yaml\nname: my_app\n\nserver:\n  host: 127.0.0.1\n  port: 3000\n  secret_key_base: \"generated-development-secret\"\n\ndatabase:\n  url: \"sqlite3:./db/my_app_development.db\"\n\nsession:\n  key: \"my_app.session\"\n  store: \"signed_cookie\"\n  adapter: \"memory\"\n  expires: 0\n\nlogging:\n  severity: \"debug\"\n  colorize: true\n```\n\n`config/environments/test.yml` uses `db/my_app_test.db`. Production leaves the\nURL empty so deployment must provide `DATABASE_URL`.\n\n## Generate the first persisted resource\n\n**Run from: the application root beside `shard.yml`.**\n\n```bash\namber generate scaffold Pet name:string:required species:string:required adopted:bool\namber database migrate\nAMBER_ENV=test amber database migrate\ncrystal spec\n```\n\nThe scaffold command writes:\n\n| Concern | Exact destination |\n|---|---|\n| Grant model | `src/models/pet.cr` |\n| Request schema | `src/schemas/pet_schema.cr` |\n| HTML CRUD controller | `src/controllers/pet_controller.cr` |\n| Index, show, new, edit, and shared form ECR | `src/views/pet/` |\n| Reversible Micrate SQL | `db/migrations/*_create_pets.sql` |\n| Model and request specs | `spec/models/pet_spec.cr`, `spec/controllers/pet_controller_spec.cr` |\n| Resource routes | `config/routes.cr` |\n\nThe generated migration contains `-- +micrate Up` and `-- +micrate Down`\nsections. `amber database migrate` applies the development database; setting\n`AMBER_ENV=test` applies the separate test database.\n\nAmber CLI `2.0.6` generates an HTML schema that declares\n`application/x-www-form-urlencoded`. `PetController` binds it with\n`schema :create, PetSchema` and `schema :update, PetSchema`, then reads the\nrequest-local typed values through `validated_as(PetSchema)`. Invalid input is\nstopped before the action and re-renders the ECR form with field errors. The\ncontroller does not manually construct and validate a second schema object.\nThis is the released CLI `2.0.6` and framework `2.0.0-beta.5` path.\n\n**Run from: the application root — useful database maintenance commands.**\n\n```bash\namber database status\namber database rollback\namber database redo\namber database seed\n```\n\n## Released frontend and asset contract\n\nThe starter uses warm paper colors, faceted geometry, editorial type hierarchy,\nand compact status labels. It is authored entirely in the generated ECR and\nlocal CSS.\n\n**File: `src/views/layouts/application.ecr` — generated front-end entry point.**\n\n```ecr\n<%= favicon_tag(\"images/favicon.svg\") %>\n<%= stylesheet_link_tag(\"stylesheets/app.css\") %>\n<%= javascript_importmap_tag(\n  {\"app\" => \"javascript/app.js\"},\n  preload: [\"javascript/app.js\"]\n) %>\n<script type=\"module\">import \"app\";</script>\n```\n\nThe visible page is `src/views/home/index.ecr`, the component layer is\n`app/assets/stylesheets/app.css`, and browser behavior begins in\n`app/assets/javascript/app.js`. See [Import maps](../assets/import-maps/) for\nadding local ESM modules without a Node.js runtime or bundler.\n\n`amber new` compiles the first manifest before it returns. The generated\nboundary is:\n\n**Generated files and authored source — ownership reference:**\n\n```text\nmy_app/\n├── app/assets/                                  # authored; source control\n│   ├── stylesheets/app.css\n│   ├── javascript/app.js\n│   ├── images/amber-crystal.svg\n│   ├── images/favicon.svg\n│   ├── fonts/.keep\n│   └── files/.keep\n├── config/assets.cr                             # runtime manifest resolver\n├── public/\n│   ├── assets/                                  # generated; gitignored\n│   │   ├── manifest.json\n│   │   └── ...fingerprinted files...\n│   └── robots.txt                               # authored\n└── src/views/layouts/application.ecr\n```\n\n**Run from: the generated application root.**\n\n```bash\namber assets build\namber assets check\n```\n\n`amber watch` rebuilds the manifest before application compilation and watches\n`app/assets/**/*` alongside Crystal and ECR source. Older applications can use\nthe explicit `scripts/build_assets.cr` wrapper from the [Asset Pipeline\nguide](../assets/) while adopting the same contract.\n\n**File: `config/assets.cr` — generated runtime resolver configuration.**\n\n```crystal\nAmber::Assets.configure(\n  manifest_path: \"public/assets/manifest.json\"\n)\n```\n\n**File: `src/views/layouts/application.ecr` — generated helper usage.**\n\n```ecr\n<%= favicon_tag(\"images/favicon.svg\") %>\n<%= stylesheet_link_tag(\"stylesheets/app.css\") %>\n<%= javascript_importmap_tag(\n  {\"app\" => \"javascript/app.js\"},\n  preload: [\"javascript/app.js\"]\n) %>\n<script type=\"module\">import \"app\";</script>\n```\n\n**File: `src/views/home/index.ecr` — generated brand image usage.**\n\n```ecr\n<%= image_tag(\"images/amber-crystal.svg\", class: \"starter-crystal\", alt: \"\") %>\n```\n\nThe generated stylesheet references\n`../images/amber-crystal.svg`, proving that CSS URLs are rewritten as well as\nECR helpers. Images, fonts, favicons, and arbitrary binaries receive the same\ncontent-addressed manifest treatment as CSS and JavaScript. User uploads do not;\nthey remain persistent runtime data outside the authored tree.\n\n## Routes and request pipelines\n\n**File: `config/routes.cr` — generated pipeline and route ownership.**\n\n- `web` includes errors, logging, sessions, flash, and CSRF.\n- `static` serves files under `public/`.\n- `api` is available for explicitly registered API routes.\n- `/` initially renders `HomeController#index`.\n- `resources \"/pets\", PetController` is added by the Pet scaffold.\n\nController generation alone does not edit routes. Scaffold generation does,\nbecause its controller and views implement the complete resource contract.\n\n## Compile and browser contract\n\n**Run from: the generated application root.**\n\n```bash\nshards install\namber assets check\ncrystal spec\ncrystal build src/my_app.cr -o bin/my_app\namber watch\n```\n\nOpen <http://127.0.0.1:3000/> and\n<http://127.0.0.1:3000/pets/new>. The release test also submits that generated\nform, reads the stored Pet, edits it through `_method=PATCH`, and verifies the\nupdated record.\n\nThe release gate runs `amber assets build` and `amber assets check`, starts the\ncompiled application with its release directory read-only, and requests the\nfingerprinted CSS, JavaScript, image, and font URLs rendered from\n`public/assets/manifest.json`. It also verifies SRI, MIME types, immutable cache\nheaders, gzip negotiation, and persisted Pet create/update behavior.\n\nThe supported web-template gate covers macOS, x86-64 Linux, and ARM64 Linux.\nWindows x86-64 must compile the same generated database-backed application in\nCI, but it does not yet have a CLI release archive. See\n[Beta support](../../beta-support/) for the platform boundary and\n[Build a Pet Tracker](../pet-tracker/) for the complete first application."}