Part 1: Project Setup
Generate a Reinhardt REST project, inspect the settings layout, and run the development server.
Part 1: Project Setup
Let's create the project that will become the snippets API. In this chapter you will generate a REST project, add the snippets app, look at the settings and management-command entry points, and start the development server.
The reference implementation for this tutorial is examples/examples-tutorial-rest. You do not need to copy that whole crate now; use it as the answer key when you want to compare your files with a finished project.
Install the Project Generator
Install reinhardt-admin-cli once:
cargo install reinhardt-admin-cli --version "0.2.0-rc.6"The installed command is named reinhardt-admin.
Create the Project
Choose a workspace directory and generate a REST project:
reinhardt-admin startproject tutorial --template rest
cd tutorialNow create the app that will own the snippets model and API routes:
reinhardt-admin startapp snippets --template reststartproject creates the project shell. startapp creates src/apps/snippets/, adds the module entry under src/apps.rs, and registers the app label in src/config/apps.rs. That registration matters later: migrations, route discovery, admin metadata, and generated names all use the app label.
Your project should now have this shape:
tutorial/
+-- Cargo.toml
+-- Makefile.toml
+-- settings/
| +-- base.toml
| +-- local.toml
+-- src/
+-- apps.rs
+-- config.rs
+-- lib.rs
+-- apps/
| +-- snippets.rs
| +-- snippets/
| +-- models.rs
| +-- serializers.rs
| +-- urls.rs
| +-- views.rs
+-- config/
| +-- apps.rs
| +-- settings.rs
| +-- urls.rs
+-- bin/
+-- manage.rsThe finished example has a few more files: migrations, Bruno API collections, Docker helpers, and tests. We will add the tutorial-facing pieces as the chapters need them.
Read the Generated Crate
Open Cargo.toml. The reference crate uses the facade reinhardt dependency with the features this API needs:
[dependencies]
reinhardt = { workspace = true, features = [
"minimal",
"core",
"conf",
"database",
"db-postgres",
"db-sqlite",
"commands",
"di",
"server",
"api",
"client-router",
] }
tokio = { version = "1.48.0", features = ["full"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
chrono = { version = "0.4", features = ["serde"] }
syntect = "5.2"The important point is not the exact dependency versions. It is that example projects import Reinhardt APIs through the reinhardt facade crate, not through internal sub-crates.
Inspect Settings
Open settings/base.toml. The reference example keeps only settings that the current runtime consumes:
[core]
debug = false
secret_key = "CHANGE_THIS_IN_PRODUCTION"
allowed_hosts = []
installed_apps = []
middleware = []
root_urlconf = ""
[core.security]
secure_ssl_redirect = false
secure_hsts_include_subdomains = false
secure_hsts_preload = false
session_cookie_secure = false
csrf_cookie_secure = false
append_slash = true
[core.databases.default]
engine = "postgresql"
host = "localhost"
port = 5432
name = "examples_tutorial_rest"
user = "reinhardt"
password = "reinhardt"
[contacts]
admins = []
managers = []This file tells the management commands and server where the project database lives. The tutorial uses PostgreSQL for the running app and SQLite in a few tests. If you use the generated Makefile.toml tasks, the local PostgreSQL container is started for you.
Now open src/config/settings.rs. The reference project composes the core and contacts fragments into one project settings type:
use reinhardt::conf::settings::builder::SettingsBuilder;
use reinhardt::conf::settings::profile::Profile;
use reinhardt::conf::settings::sources::{DefaultSource, LowPriorityEnvSource, TomlFileSource};
use reinhardt::core::serde::json;
use reinhardt::settings;
use std::env;
use std::path::PathBuf;
#[settings(core: CoreSettings | contacts: ContactSettings)]
pub struct ProjectSettings;The rest of that file chooses the profile (local by default, ci in CI), reads settings/base.toml, then overlays settings/<profile>.toml. That gives you a predictable place for local secrets and CI-only database settings.
Check App Registration
Open src/apps.rs. After startapp snippets --template rest, it should expose the app module:
pub mod snippets;Then open src/config/apps.rs. The generated app label should be registered:
use reinhardt::installed_apps;
installed_apps! {
snippets: "snippets",
}
pub fn get_installed_apps() -> Vec<String> {
InstalledApp::all_apps()
}This is why you used startapp instead of creating the directory by hand. Reinhardt can only discover migrations and app-level routes for apps that are registered here.
Check URL Mounting
The project-level router lives in src/config/urls.rs. The reference example mounts the snippets app under /api/:
use reinhardt::prelude::*;
use reinhardt::routes;
#[routes]
pub fn routes() -> UnifiedRouter {
UnifiedRouter::new().mount("/api/", crate::apps::snippets::urls::url_patterns())
}This does not create any endpoint by itself. It says, "take whatever the snippets app registers, and serve it below /api/." In Part 2, src/apps/snippets/urls.rs will provide the app router.
Understand the Manage Binary
The command entry point is src/bin/manage.rs:
use examples_tutorial_rest::config;
use reinhardt::commands::execute_from_command_line_with_settings;
use reinhardt::core::tokio;
use std::process;
#[tokio::main]
async fn main() {
unsafe {
std::env::set_var(
"REINHARDT_SETTINGS_MODULE",
"examples_tutorial_rest.config.settings",
);
}
let _ = &config::urls::routes;
if let Err(e) = execute_from_command_line_with_settings(config::settings::get_settings()).await
{
eprintln!("Error: {}", e);
process::exit(1);
}
}For your generated crate, the module path will use your package name instead of examples_tutorial_rest. This binary is the local equivalent of Django's manage.py: it loads project settings, makes sure the route macro inventory is linked, and then dispatches commands such as migrate, showurls, and runserver.
Run the Empty Server
Start the server:
cargo run --bin manage -- runserverIn the reference example, prefer the make task for normal local development:
cargo make runserverThat task starts disposable PostgreSQL and Redis containers, applies migrations, and then runs the same cargo run --bin manage -- runserver command. The direct command is useful for understanding the management CLI; the make task is easier when the database-backed chapters are in place.
You should see the server listening on 127.0.0.1:8000. Leave it running and open another terminal:
curl http://127.0.0.1:8000/api/snippets/At this point the route may still return 404, because you have not written the snippets app endpoints yet. That is fine. Part 2 adds the first temporary JSON endpoints so the URL starts responding.
What You Built
You now have:
- A REST project generated from the
resttemplate - A
snippetsapp created bystartapp - A settings stack based on
ProjectSettings,settings/base.toml, and profile-specific TOML files - A project router ready to mount app-level routes under
/api/ - A
managebinary that loads settings and runs Reinhardt management commands
When you are comfortable with that shell, continue to Part 2: Your First Endpoints.