Skip to main content

Setup & Scaffolding

In this section, you'll create the runnable project foundation and generate the database wiring used by all later steps.

Prerequisites

What You’ll Learn

  • How to scaffold the base Shelf project
  • Which dependencies power each layer of the stack
  • How to prepare the project for model/codegen/migration steps

Step Outcome

By the end of this page, you should have:

  • A Shelf-based Dart app in packages/ormed/example/fullstack
  • Dependencies installed (dart pub get)
  • Ormed initialized with generated datasource helpers
  • A bootable AppDatabase wrapper used by server and tests

Do This Now (in order)

dart create -t server-shelf packages/ormed/example/fullstack
cd packages/ormed/example/fullstack
dart pub get
dart run ormed_cli:ormed init --no-interaction

Optional YAML scaffold:

dart run ormed_cli:ormed init --with-config --only=config --no-interaction

Create the project

First, create a new Dart project using the server-shelf template. This gives us a basic Shelf server structure to build upon.

dart create -t server-shelf packages/ormed/example/fullstack

Add dependencies

We use a carefully selected set of packages to handle different aspects of our full-stack application. Here's a breakdown of what each package does:

Core Framework

  • shelf: The standard web server middleware for Dart. It's lightweight and highly extensible.
  • shelf_router: A powerful router for Shelf that makes defining web and API endpoints easy.
  • shelf_multipart: Adds multipart/form-data support to Shelf for handling file uploads.
  • shelf_static: Serves static files (like uploaded images) from the file system.

Database (Ormed)

  • ormed: Our main ORM. It provides a type-safe way to interact with the database using Dart models.
  • ormed_sqlite: The SQLite driver for Ormed. We'll use SQLite for its simplicity and zero-config setup.

UI & Templates

  • liquify: A flexible template engine (Liquid-compatible) for rendering HTML on the server.

Storage & Files

  • file_cloud: A cloud-agnostic storage abstraction.
  • storage_fs: A local file system implementation for file_cloud, perfect for development.

Observability

  • contextual: A structured logging library that allows you to attach context to your logs.
  • contextual_shelf: Middleware to automatically inject request context (like Request IDs) into your logs.

Testing

  • server_testing: A library for testing HTTP servers, supporting both in-memory and live server testing.
  • server_testing_shelf: Shelf-specific bindings for server_testing.
  • property_testing: A property-based testing framework for Dart, used for stress testing and finding edge cases.
  • assertable_json: A fluent API for asserting on JSON structures in tests.

CLI & Utilities

  • artisanal: A toolkit for building Laravel-style CLI commands (Artisan).
name: ormed_fullstack_example
description: Full-stack movie catalog example using Ormed, Shelf, Liquify, and structured logging.
version: 1.0.1
# repository: https://github.com/my_org/my_repo

environment:
sdk: ^3.10.4

# #region fullstack-pubspec-core
dependencies:
artisanal: ^0.2.0
contextual: ^2.1.0
contextual_shelf: ^0.3.0
file_cloud: ^0.1.0
liquify: ^1.4.1
ormed: ^0.1.0
ormed_cli: ^0.1.0
ormed_sqlite: ^0.1.0
path: ^1.9.1
shelf: ^1.4.2
shelf_multipart: ^2.0.1
shelf_router: ^1.1.4
shelf_static: ^1.1.3
storage_fs: ^0.1.0

dev_dependencies:
assertable_json: ^0.2.1
build_runner: ^2.10.4
http: ^1.6.0
lints: ^6.0.0
property_testing: ^0.2.1+1
server_testing: ^0.2.0
server_testing_shelf: ^0.2.1+1
test: ^1.28.0
# #endregion fullstack-pubspec-core

dependency_overrides:
ormed: { path: ../.. }
ormed_cli: { path: ../../../ormed_cli }
ormed_d1: { path: ../../../ormed_d1 }
ormed_mysql: { path: ../../../ormed_mysql }
ormed_postgres: { path: ../../../ormed_postgres }
ormed_sqlite_core: { path: ../../../ormed_sqlite_core }
ormed_sqlite: { path: ../../../ormed_sqlite }

Initialize Ormed

Once the dependencies are added, initialize Ormed. This scaffolds code-first datasource helpers and sets up the project for code generation.

dart run ormed_cli:ormed init --no-interaction

Lean default note:

  • init scaffolds runtime datasource + migrations by default.
  • Seeder and test helper scaffolds are optional (--with-seeders, --with-tests) and can also be created lazily when first needed.

If you want explicit YAML-driven CLI configuration, scaffold it with:

dart run ormed_cli:ormed init --with-config --only=config --no-interaction

ormed.yaml is optional in the default code-first flow.

Use code-first (config.dart + datasource.dart) for runtime bootstrapping and day-to-day app development. Keep ormed.yaml when you want explicit CLI connection definitions or multi-connection YAML workflows.

# #region fullstack-ormed-config
driver:
type: sqlite
options:
database: database/ormed_fullstack_example.sqlite
migrations:
directory: lib/src/database/migrations
registry: lib/src/database/migrations.dart
ledger_table: orm_migrations
schema_dump: database/schema.sql
seeds:
directory: lib/src/database/seeders
registry: lib/src/database/seeders.dart
# #endregion fullstack-ormed-config

DataSource helper

To make database access consistent across our app and tests, we use a generated createDataSource helper.

/// Creates a new DataSource instance using generated helper options.
DataSource createDataSource() {
final env = OrmedEnvironment.fromDirectory(Directory.current);
final registry = bootstrapOrm();
final path = env.string(
'DB_PATH',
fallback: 'database/ormed_fullstack_example.sqlite',
);
return DataSource(
registry.sqliteFileDataSourceOptions(path: path, name: 'default'),
);
}

We then wrap this in an AppDatabase class to manage the connection lifecycle within our application.

class AppDatabase {
AppDatabase() : _ownsDataSource = true;

AppDatabase.fromDataSource(this.dataSource) : _ownsDataSource = false;

late final DataSource dataSource;
final bool _ownsDataSource;

Future<void> init() async {
if (_ownsDataSource) {
dataSource = createDataSource();
}
await dataSource.init();
}

OrmConnection get connection => dataSource.connection;

Future<void> dispose() {
if (_ownsDataSource) {
return dataSource.dispose();
}
return Future.value();
}
}

Verify Before Continuing

Run these commands before moving to the models page:

dart analyze
dart run build_runner build --delete-conflicting-outputs