Reinhardt Basis Tutorial
Build a polling application on the Reinhardt pages template: a Rust/WASM client, typed server functions, generated model info DTOs, shared request DTOs, session-cookie authentication, ownership-checked CRUD, static assets, tests, and the Reinhardt admin.
The reference implementation lives in examples/examples-tutorial-basis. Use its app source and tests as the reference for completed slices. Its Cargo workspace and local infrastructure files are repository-specific; the standalone project created in Part 1 uses the generated SQLite-first settings. The tutorial introduces the same architecture one working slice at a time, so every part ends with something you can run or click.
Who This Tutorial Is For
- Developers new to Reinhardt who want the full-stack pages workflow.
- Django developers who want to map familiar ideas onto Reinhardt's Rust/WASM architecture.
- Rust developers building browser applications that talk to typed server functions.
Prerequisites
- Rust 1.96.0 or newer, plus basic Rust and Cargo knowledge.
cargo makeinstalled.wasm32-unknown-unknowntarget andwasm-packinstalled.- SQLite support through the Reinhardt feature flags used in Part 1.
- A browser capable of running WebAssembly.
What You'll Build
End users can:
- View the latest polls on a WASM-rendered index page.
- Open a poll, vote on a choice, and see the results update.
- Sign up, log in, and log out.
- Create, edit, and delete their own polls and choices.
Administrators can:
- Manage polls and choices at
/admin/. - Serve the compiled WASM bundle and project static files from the same application.
The Pages Template at a Glance
The completed project has this shape:
examples-tutorial-basis/
+-- Cargo.toml
+-- Makefile.toml
+-- build.rs
+-- index.html
+-- settings/
| +-- base.toml
| +-- ci.toml
| +-- local.toml
+-- src/
| +-- lib.rs
| +-- apps.rs
| +-- config.rs
| +-- client.rs
| +-- bin/
| | +-- manage.rs
| +-- config/
| | +-- admin.rs
| | +-- apps.rs
| | +-- settings.rs
| | +-- urls.rs
| | +-- wasm.rs
| +-- apps/
| | +-- polls.rs
| | +-- polls/
| | | +-- client.rs
| | | +-- models.rs
| | | +-- server.rs
| | | +-- server_fn.rs
| | | +-- services.rs
| | | +-- urls.rs
| | | +-- client/
| | | | +-- components.rs
| | | | +-- components/
| | | | +-- polls_index.rs
| | | | +-- polls_detail.rs
| | | | +-- polls_results.rs
| | | | +-- question_new.rs
| | | | +-- question_edit.rs
| | | | +-- question_delete.rs
| | | | +-- choice_new.rs
| | | | +-- choice_edit.rs
| | | | +-- choice_delete.rs
| | | +-- server/
| | | +-- admin.rs
| | | +-- serializers.rs
| | | +-- services/
| | | +-- server.rs
| | | +-- urls/
| | | +-- client_router.rs
| | | +-- server_router.rs
| | +-- users.rs
| | +-- users/
| | +-- client.rs
| | +-- models.rs
| | +-- server.rs
| | +-- server_fn.rs
| | +-- urls.rs
| | +-- client/
| | | +-- components.rs
| | | +-- components/
| | | +-- login_page.rs
| | | +-- logout_page.rs
| | | +-- signup_page.rs
| | +-- urls/
| | +-- client_router.rs
| | +-- server_router.rs
| +-- client/
| +-- components.rs
| +-- components/
| | +-- nav.rs
| +-- lib.rs
+-- migrations/
| +-- polls/
| +-- 0001_initial.rs
| +-- 0002_question_author.rs
+-- static/
| +-- css/
| +-- style.css
+-- tests/
+-- createsuperuser.rs
+-- integration.rs
+-- wasm/
+-- polls_mock_test.rs
+-- users_mock_test.rsThree rules keep this structure predictable:
- Server and client are separate targets.
#[cfg(server)]code runs in the native server binary.#[cfg(client)]code runs in the browser as WASM. Model definitions stay importable so#[model]can generate shared info DTOs, while admin definitions and service implementations stay behind server-only module gates. - Server functions are the bridge. Anything the WASM client needs from the database goes through a
#[server_fn]insrc/apps/<app>/server_fn.rs. The client receives generated*InfoDTOs from#[model]and explicit request DTOs fromsrc/shared/types.rs. - Routing belongs to each app. Each app exposes target-specific route functions from
src/apps/<app>/urls.rs, which gatesurls/server_router.rsfor server-function markers andurls/client_router.rsfor client component routes. Route-backed components use the#[component]macro undersrc/apps/<app>/client/components/. The project-levelsrc/config/urls.rsaggregates the active target's app routers, session middleware, admin routes, and static-file routes.
Tutorial Structure
Part 1: Project Setup and SPA Shell
Generate a pages project, inspect the src/{apps,config,client,bin} layout, wire settings through ProjectSettings, install the WASM tools, and run the empty SPA shell.
Part 2: Your First Feature - the Poll Index
Create the polls app, define Question and Choice, generate the first migration without ownership, expose get_questions, and render the poll list in the browser.
Part 3: Detail Pages and Voting
Add poll detail and results routes, submit votes through form! and #[server_fn], and update the page through reactive client state.
Part 4: Users and Authentication
Create the users app, define the User model, register the project-local AuthUserManager, add login/register/logout/current-user server functions, and show authentication state in the nav.
Part 5: Ownership and Poll CRUD
Add the Question.author foreign key in migration 0002, then build create/edit/delete flows for polls and choices with server-side ownership checks.
Part 6: The Admin and Static Files
Register poll models with the Reinhardt admin, mount /admin/, and learn how dist-wasm/, static/, and staticfiles/ fit together.
Part 7: Testing
Run native integration tests with isolated SQLite fixtures, test management commands, and exercise WASM client flows with MSW-backed server-function mocks.
Recommended Learning Path
Work through the parts in order. Each part assumes the files from the previous one exist and compile. When your project differs from the text, compare the app source and tests with examples/examples-tutorial-basis before inventing a local workaround. Do not copy the reference example's repository-only Cargo workspace or local PostgreSQL/Redis infrastructure into the standalone SQLite project.
REST Tutorial Comparison
Use this tutorial when you want the full-stack pages architecture: WASM client, typed server functions, generated model info DTOs, shared request DTOs, session auth, admin, and static assets.
Use the REST tutorial when you want classic JSON endpoints built with #[get], #[post], serializers, and viewsets. The model and migration APIs are shared between both tracks.