Files
Notes/notes-service/vendor/loco-rs/CHANGELOG.md
T
2026-08-01 16:11:49 +03:00

81 KiB

Changelog

1.0.1 - 2026-07-31

A documentation and CLI-ergonomics patch. No behavior changes to the framework runtime.

Fixed

  • generate scaffold/controller no longer error on the old --api flag. The 1.0 adaptive-generator rebuild removed the --api/--html/--htmx (and -k/--kind) flags — scaffold now auto-detects headless vs. clientside from frontend/, and controllers are always JSON API controllers — but the docs (including the Your first app tutorial) still showed --api, so copy-pasting them failed with error: unexpected argument '--api' found. The generators now accept the old flags for compatibility: --api is a no-op (it's the headless default) and --html/--htmx return a clear message pointing at the React SPA frontend that replaced server-rendered views. (#1790)
  • Docs resync. Removed every reference to the removed scaffold/controller kind flags and the deleted ScaffoldKind enum / mappings.json across the tutorials, how-to guides, and CLI/generators reference; corrected the field-type reference to describe loco-gen/src/column.rs (including that array:int is now a 64-bit BigInt array, consistent with the scalar inti64 change).

1.0.0 - 2026-07-25

1.0.0 is the first stable Loco release — a single, intentionally-breaking milestone. Its headline is the move to Sea-ORM 2.0, alongside first-class LLM/agent support, priority queues, a broad dependency modernization, and a deep hardening pass across the queue, storage, config, error, remote-IP, and middleware subsystems. Follow the step-by-step 0.16 → 1.0 upgrade guide.

Breaking Changes

  • Sea-ORM 2.0 + sqlx 0.9. Bump sea-orm/sea-orm-migration to 2.0 (app + migration crate), direct sqlx to 0.9, update the Sea-ORM CLI, and regenerate entities. Raw-Statement calls gain a _raw suffix; runtime SQL strings need AssertSqlSafe. MSRV is 1.94 (sea-orm 2.0.0 declares it). (Adopted from the SeaQL fork and #1698.)
  • Generated primary/foreign keys are now 64-bit (BIGINT / i64). Also the int/unsigned field types generate 64-bit columns. Only affects newly generated code; existing tables are untouched.
  • Priority queues — Redis backend change. The Redis worker moved from Lists to Sorted Sets (ZSET) to support priority; drain existing Redis queues before upgrading. Postgres/SQLite auto-migrate a priority column (no action). (#1693)
  • Worker::perform_later() returns the job ID (Result<String>), and Queue::enqueue() returns Result<Option<String>>. Existing perform_later(...).await?; keeps working. (#1624, fixes #1623)
  • PageResponse<T> exposes meta: PagerMeta instead of flat total_pages/total_items (also carries page/page_size). (#1685, fixes #1683)
  • View engine: use engines::TeraView::build_with_post_process(...) instead of TeraView::build()?.post_process(...) in after_routes.
  • Dependency majors: thiserror 1→2, tower 0.4→0.5, heck→0.5, byte-unit 4→5, ipnetwork 0.20→0.21, strum→0.27, redis 0.31→1, bb8-redis→0.26, opendal 0.54→0.57; serde_yamlserde_yaml_ng. Transitive for most apps.
  • Removed the dead loco-cli crate (superseded by loco-new, the published loco binary).
  • ExtraDbInitializer removed; use MultiDbInitializer. The single-extra-connection initializer (initializers.extra_db, which layered a bare Extension<DatabaseConnection>) is gone. Use MultiDbInitializer with a one-entry initializers.multi_db map instead, and extract the connection with Extension<MultiDb> + multi_db.get("<name>"). This collapses two near-identical initializers into one named-connections abstraction.
  • AppContext is now #[non_exhaustive]. Construct it with AppContext::builder(environment, db, config) (or builder(environment, config) without the with-db feature) followed by optional .queue_provider(..)/.mailer(..)/.storage(..)/.cache(..)/.shared_store(..) and .build(). Direct struct-literal construction and exhaustive pattern matches on AppContext from outside the crate no longer compile; field access (ctx.db, ctx.config, State/FromRef extraction) is unchanged. This makes future context fields non-breaking to add.
  • Storage MirrorStrategy and BackupStrategy merged into ReplicatedStrategy. The two strategies were the same primary-plus-secondaries replication engine; they are now one storage::strategies::replicated::ReplicatedStrategy with a single FailurePolicy enum. Migrate: MirrorStrategy::new(p, s, MirrorAll)ReplicatedStrategy::mirror(p, s, FailurePolicy::FailIfAny); BackupStrategy::new(p, s, BackupAll)ReplicatedStrategy::backup(p, s, FailurePolicy::FailIfAny). Old FailureMode maps: AllowMirrorFailure/ AllowBackupFailureAllowAll, AtLeastOneFailureAllowSingleFailure, CountFailure(n)FailAtFailures(n). Secondary writes for the former backup strategy now run concurrently (previously sequential); the collected errors and failure decision are unchanged.
  • Local storage driver no longer defaults its root to /. storage::drivers::local::new() previously rooted the filesystem store at /, so any key — including one derived from user input — resolved against the whole disk (e.g. downloading key etc/passwd read /etc/passwd). It now roots at the current working directory. Apps that relied on absolute-path keys should switch to local::new_with_prefix("/your/root") to opt back into an explicit absolute root.
  • Background queue reworked into a QueueProvider adapter interface. The bgworker::Queue enum (Postgres/Sqlite/Redis/None) is now a newtype over Arc<dyn QueueProvider>, so backends are pluggable (implement QueueProvider and wrap with Queue::from_provider). All existing methods (enqueue, register, run, ping, cancel_jobs, clear_by_status, requeue, …) keep the same signatures and behavior. Only two source-level changes affect callers: construct a no-op queue with Queue::empty() instead of Queue::None, and code that pattern-matched the enum variants (e.g. Queue::Postgres(pool, ..) to reach the raw pool) no longer compiles — use the provider methods instead.
  • Fallback middleware defaults to 404. When the built-in fallback is enabled without an explicit code, it now returns 404 Not Found (matching its docs and the bundled not-found page) instead of 200 OK. Apps that relied on the enabled fallback returning 200 must set code: 200 explicitly. The file-based fallback is unaffected (ServeFile reports its own status).
  • {env}.local.yaml now deep-merges over {env}.yaml. Previously the first existing file won and the other was ignored, so a .local.yaml had to restate the whole config. Both files are now layered with local precedence: mappings merge recursively; scalars and sequences in local replace the base value (sequences are not concatenated). Base keys now persist unless explicitly overridden.
  • More accurate HTTP status codes for errors. IntoResponse for Error previously collapsed ~28 of 35 variants to 500. Model(EntityNotFound) now returns 404, Model(EntityAlreadyExists) returns 409, model validation and form-body rejections return 4xx (matching JSON rejections) instead of 500. Genuinely-internal errors still return a generic 500. Handlers that asserted on the old 500s will observe the corrected codes.
  • JWT::algorithm() restricted to the HMAC family. It now takes a new loco_rs::auth::jwt::JWTAlgorithm enum (HS256/HS384/HS512) instead of jsonwebtoken::Algorithm. Asymmetric algorithms — which could never work with Loco's shared base64 secret and silently produced broken tokens — are no longer representable.
  • remote_ip middleware rebuilt on axum-client-ip; trusted_proxies removed. Previously this middleware walked X-Forwarded-For right-to-left, skipping any address in a configurable trusted_proxies CIDR list (or a built-in RFC-1918 + loopback list), so it could see through a chain of one or more trusted proxies. It now trusts exactly one configured source (source: ClientIpSource, default RightmostXForwardedFor) and does no CIDR filtering — for the default it takes the last comma-separated value of the last X-Forwarded-For header verbatim, private or not. Single reverse-proxy deployments are unaffected. Multi-hop topologies (CDN → LB → ingress) must now configure their innermost hop to set the client IP (e.g. nginx set_real_ip_from/real_ip_recursive), or point source at a provider header (CfConnectingIp, CloudFrontViewerAddress, XRealIp, ConnectInfo, …). Note: an old config's trusted_proxies: key is silently ignored (unknown field), so review remote_ip before upgrading — this is a silent security-relevant behavior change, not a load error. The RemoteIP extractor and its Display output are unchanged.
  • auth_jwt feature renamed to auth. Update features = ["auth_jwt"]["auth"] (it gates JWT auth and the ApiToken extractor, as before).
  • Background-queue features collapsed. bg_pg/bg_sqltworker (Postgres+SQLite; free once sqlx is compiled), bg_redisworker_redis (adds dep:redis). default now has worker (not Redis). A Redis queue needs worker_redis; the queue backend is selected at runtime by queue.kind.
  • Mailer Template::new(dir) now returns Result (call Template::new(dir)?). Email templates render through a full Tera instance so they support inheritance and shared templates. Standard usage via Mailer::mail_template is unchanged. (#1694)
  • Vars::cli_arg returns Result<&str> (was Result<&String>). Callers that relied on &String (e.g. .clone() into a String) should use .to_owned(). (#1732)

Added

  • First-class LLM / agent support. Root AGENTS.md teaches agents to build Loco apps; llms.txt / llms-full.txt are served from the site (llmstxt.org). Every loco new app ships an app-level AGENTS.md.
  • Priority queues with Worker::perform_later_with_priority(...); mailer jobs default to priority 100. (#1693)
  • Mailer implicit TLS (SMTPS / port 465) via mailer.smtp.tls. (#1774, fixes #1773)
  • Run the scheduler without a worker--scheduler flag + StartMode::ServerAndScheduler/WorkerAndScheduler. (#1742, fixes #1737)
  • Email headers support in the mailer (#1700).
  • Multi-recipient emails. Mailer::mail_multi / mail_template_multi and the MultiEmail / MultiArgs types send one email to multiple To/CC/BCC recipients (processed by a dedicated MultiMailerWorker). (#1764)
  • Email template inheritance & shared templates. Mailer templates support Tera {% extends %} / {% block %} and can share a common layout via Mailer::mail_template_with_shared / Template::new_with_shared. loco generate mailer now scaffolds a src/mailers/shared/ base layout that the welcome template extends. (#1694)
  • "Create user" task (#1670).
  • UuidUniqWithDefault and UuidWithDefault types (#1642).
  • Allow overriding a secure header (#1659).
  • Mailer::deliver_now / mail_template_now for synchronous sends. Complements Mailer::mail/mail_template (which enqueue via the background worker, like Rails deliver_later) with an inline send that bypasses the queue (Rails deliver_now).
  • MiddlewareStackExt for surgical middleware-stack edits. Inside Hooks::middlewares, tweak the default stack instead of rebuilding it: stack.insert_before("cors", ..), .insert_after(..), .replace(..), and .delete("logger") — matched by middleware name (Rails' config.middleware.insert_before/delete). Available via the prelude.
  • Optional JWT extraction. JWT now implements OptionalFromRequestParts, so a handler can take Option<JWT> to serve authenticated and anonymous callers from one endpoint (Some when a valid token is present, None otherwise).
  • Ergonomic verb-explicit route methods. Routes now has get/post/ put/delete/patch/head/options/trace builder methods — Routes::new().get("/ping", ping) alongside the existing .add("/ping", get(ping)). They record the HTTP verb directly (exact cargo loco routes output without relying on the debug-format regex). Purely additive; add is unchanged.
  • Opt-in background-job reaper (visibility timeout). Each queue backend's config accepts a reaper: { age_minutes, interval_seconds } block. When set, the worker periodically requeues jobs stranded in Processing (e.g. by a crashed worker) back to Queued, instead of requiring a manual cargo loco jobs requeue. Disabled by default — existing behavior is unchanged.
  • TLS to managed Postgres and Redis. Postgres TLS works via the connection URL (sslmode=require, sslrootcert=...) with no feature flag, and a new redis_tls feature enables rediss:// for both the queue and cache Redis backends (webpki roots, pure-Rust ring provider — no C toolchain). New how-to: "Connect to Postgres and Redis over TLS". (#1191, #1341)
  • Typed, streaming db::dump::<A>() — counterpart to db::seed::<A>() that streams rows through their entity Model straight to disk (memory bounded to a single row) with full type fidelity. New Hooks::dump (default dumps every table; override it to call db::dump per entity) backs cargo loco db seed --dump. (#1691)
  • logger::init_layer / logger::init_env_filter are now public building blocks, so an app overriding Hooks::init_logger can reuse Loco's formatting and filter policy while adding its own layers (e.g. tracing-flame, OTLP). (#1753)

Changed

  • Wrap TeraView in Arc to reduce runtime memory usage (#1703).
  • Refactor users model to reuse find_by_api_key in Authenticable (#1706).
  • Split error detail generic parameters (#1709).
  • Update loco-new for the new Rhai version (#1704).
  • Replaced hand-rolled Cargo.lock parsing with the cargo-lock crate; retired duct_sh.
  • Error → HTTP-status mapping is now exhaustive. The IntoResponse for Error match dropped its trailing _ => 500 wildcard: every variant (and every nested ModelError variant) is now classified explicitly, so adding a new error variant is a compile error until its status is chosen — it can no longer silently default to 500. The Error enum is also reorganized into client-facing vs internal/infra regions. Behavior is unchanged (all infra errors still map to 500); no variant was renamed, so existing code is unaffected.
  • Rust edition 2024. loco-rs, loco-gen, xtask, and the loco new-app generator now compile on edition 2024 (MSRV floor unchanged at 1.94; edition 2024 needs ≥ 1.85). Editions are per-crate, so apps depending on Loco need not change. Newly generated apps stay on edition 2021 for now.
  • Deduplicated the Postgres and SQLite background-queue providers: the shared Job/JobRegistry/RunOpts now live in one module behind a Driver trait (internal refactor, no behavior or API-path change).
  • In-memory cache now uses moka::future::Cache instead of wrapping the synchronous cache behind #[async_trait] (removes a sync-behind-async smell; no API change).
  • Cookie token extraction now uses axum_extra's Cookie::value() instead of hand-parsing the cookie string (byte-identical behavior).
  • Internal de-duplication pass (no public-API-path or behavior change unless noted): the response helpers in format are now single-sourced through RenderBuilder; the JWT/JWTWithUser extractors share one validate/decode helper; the six validate extractors are generated from shared decoder fns + two error-tier macros; the byte-identical Job struct is shared across the SQL and Redis queue backends; the twin cli::main functions share one dispatch_common; and duplicate env-var name constants were removed.
  • format's two response paths were converged onto axum's canonical behavior: RenderBuilder::json and RenderBuilder::redirect_with_header_key are now infallible with respect to bad input (they return a 500 response, matching axum::Json / axum::response::Redirect) instead of returning Err.

Fixed

  • db seed --dump datetime round-trip on SQLite. Loco's timestamptz columns default to CURRENT_TIMESTAMP, which SQLite stores as space-separated text ("YYYY-MM-DD HH:MM:SS"); dumps captured that verbatim and then failed chrono's RFC3339 parse on re-seed (Json("premature end of input")). Dumps now normalize such datetimes to RFC3339 (already-RFC3339 text is untouched). (#1736, #1691)
  • cargo fmt error in loco-new (#1669).
  • UUID pattern in form field generation (#1665).
  • Clippy warnings for recent Rust (#1705).
  • Add tests for the auth extractor (#1671).
  • Postgres/SQLite queue backends now behave consistently. Two divergences between the Postgres and SQLite job backends are fixed: (1) enqueue on Postgres previously swallowed a tag-serialization error (storing tags = null); it now propagates the error like SQLite. (2) complete_job without a repeat interval on Postgres stamped run_at = NOW() on the completed row while SQLite left it untouched; Postgres now leaves run_at as-is, matching SQLite (the interval path still reschedules run_at on both). The shared to_job row mapper is now single-sourced across both backends.
  • Storage mirror fan-out no longer stops at the first failing secondary. rename, copy, and upload_stream checked the failure mode inside the secondary loop and returned early on the first failure, silently leaving later mirrors stale (upload/delete were already correct). All five mutating methods now share one helper that attempts every secondary (concurrently) and applies the failure mode once.
  • Postgres BOOLEAN columns are no longer dropped from dump_tables. The decode probe chain had no bool arm, so PG booleans (which don't fall back to the numeric arms like SQLite's integer-backed booleans) were silently omitted.
  • Hooks::on_shutdown now runs in worker-only start modes. WorkerOnly and WorkerAndScheduler bypassed H::serve (the hook's only caller); the shutdown hook is now invoked on their shutdown path too.
  • Postgres admin/maintenance URI is derived with the url crate. Building it via db_uri.replace(db_name, "/postgres") corrupted the URI when the database name also appeared in the host or credentials.
  • Foreign-key names are normalized consistently. reference_id received a normalized table name in create_table but raw names in add_reference/remove_reference, so irregular plurals produced mismatched FK column/constraint names between creation and later add/remove.
  • ViewEngine extractor now rejects gracefully (HTTP 500) when the opt-in Tera layer is absent, instead of declaring Infallible and then panicking.
  • cargo loco routes lists every HTTP verb of a multi-method route (route introspection previously reported only the first).
  • Removed a fossilized 3-second sleep on every Redis queue boot (a leaked test-isolation artifact; Postgres/SQLite had no equivalent).
  • Password redaction in test snapshots (cleanup_user_model) now targets the quoted value precisely; the previous pattern had a degenerate quantifier that swallowed the field following password.
  • Postgres test-database cleanup now completes synchronously (a joined worker thread) instead of a fire-and-forget task, so parallel test runs no longer leak databases; PostgresTest also builds its connection strings with the url crate rather than a corruption-prone substring replace.
  • RenderBuilder::template now threads the builder's chained status/header/ etag/cookies through to the response; it previously delegated to the free html() and silently dropped them.
  • llms.txt: two Core concepts links pointed at doc pages that don't exist (the-app/configuration/, the-app/testing/); repointed to the sections that actually document them. A new cargo xtask llms-check CI step now verifies the curated LLM docs against the docs tree so these links can't drift silently.

Removed

  • Error enum narrowing. Removed four low-value/dependency-leaking variants that all mapped to HTTP 500 and were never matched: Error::EnvVar, Error::SemVer, Error::TaskJoinError, and Error::Hash (hashing errors now surface as Error::Message). Error remains #[non_exhaustive], so exhaustive matches already require a wildcard arm and are unaffected.
  • Deleted shipped-but-dead code: the never-compiled controller/middleware/_archive/content_etag.rs module and a commented-out block of backtrace-blocklist regexes.

v0.16.4

v0.16.3

Breaking Changes

In file src/initializers/view_engine.rs, modify the method after_routes:

Before

async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
	#[allow(unused_mut)]
	let mut tera_engine = engines::TeraView::build()?;
	if std::path::Path::new(I18N_DIR).exists() {
		let arc = ArcLoader::builder(&I18N_DIR, unic_langid::langid!("en-US"))
			.shared_resources(Some(&[I18N_SHARED.into()]))
			.customize(|bundle| bundle.set_use_isolating(false))
			.build()
			.map_err(|e| Error::string(&e.to_string()))?;
		#[cfg(debug_assertions)]
		tera_engine
			.tera
			.lock()
			.expect("lock")
			.register_function("t", FluentLoader::new(arc));

		#[cfg(not(debug_assertions))]
		tera_engine
			.tera
			.register_function("t", FluentLoader::new(arc));
		info!("locales loaded");
	}

	Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
}

After (use post_process to add i18n initialization code)

async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
	let tera_engine = if std::path::Path::new(I18N_DIR).exists() {
		let arc = std::sync::Arc::new(
			ArcLoader::builder(&I18N_DIR, unic_langid::langid!("en-US"))
				.shared_resources(Some(&[I18N_SHARED.into()]))
				.customize(|bundle| bundle.set_use_isolating(false))
				.build()
				.map_err(|e| Error::string(&e.to_string()))?,
		);
		info!("locales loaded");

		engines::TeraView::build()?.post_process(move |tera| {
			tera.register_function("t", FluentLoader::new(arc.clone()));
			Ok(())
		})?
	} else {
		engines::TeraView::build()?
	};

	Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
}

v0.16.2

v0.16.1

v0.16.0

Note: For detailed upgrade steps for breaking changes, see the upgrade guide.

  • Dependency updates:
    • Bumped [tokio] to 1.45 and [tokio-util] to 0.7 (#1435)
    • Bumped [colored] to 3.0 (#1437)
    • Bumped [rand] to 0.9 (#1439)
    • Bumped [duct] to 1.0 (#1438)
    • Bumped [redis] to 0.31, [bb8] to 0.9, and [bb8-redis] to 0.23 (commit 7e7be)
    • Updated Loco template crates (#1440)

v0.15.0

Breaking Changes

In module loco_rs::auth::jwt in struct JWT, the impl method generate_token signature has changed. Migration:

Before

jwt.generate_token(&expiration, pid.clone(), None);

After

jwt.generate_token(expiration, pid.clone(), Map::new());
//                 ^ no "&"                 ^ serde_json::map (doesn't allocate in constructor)

v0.14.1

v0.14

v0.13.2

BREAKING In your app.rs add the following injection comment at the bottom:

fn register_tasks(tasks: &mut Tasks) {
    tasks.register(tasks::user_report::UserReport);
    tasks.register(tasks::seed::SeedData);
    tasks.register(tasks::foo::Foo);
    // tasks-inject (do not remove)
}

v0.13.0

NOTE: update your migration listing module like so:

// migrations/src/lib.rs
  vec![
      Box::new(m20220101_000001_users::Migration),
      Box::new(m20231103_114510_notes::Migration),
      Box::new(m20240416_071825_roles::Migration),
      Box::new(m20240416_082115_users_roles::Migration),
      // inject-above (do not remove this comment)
  ]

Add the comment just before the closing array (inject-above)

$ generate scaffold posts title:string! content:string! written_by:references:users approved_by:references:users

NOTE: update your initializers after_routes like so:

// src/initializers/view_engine.rs
async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
    #[allow(unused_mut)]
    let mut tera_engine = engines::TeraView::build()?;
    if std::path::Path::new(I18N_DIR).exists() {
        let arc = ArcLoader::builder(&I18N_DIR, unic_langid::langid!("en-US"))
            .shared_resources(Some(&[I18N_SHARED.into()]))
            .customize(|bundle| bundle.set_use_isolating(false))
            .build()
            .map_err(|e| Error::string(&e.to_string()))?;
        #[cfg(debug_assertions)]
        tera_engine
            .tera
            .lock()
            .expect("lock")
            .register_function("t", FluentLoader::new(arc));

        #[cfg(not(debug_assertions))]
        tera_engine
            .tera
            .register_function("t", FluentLoader::new(arc));
        info!("locales loaded");
    }

    Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
}

v0.12.0

This release have been primarily about cleanups and simplification.

Please update:

  • loco-rs
  • loco-cli

Changes:

  • generators (BREAKING): all prefixes in starters (e.g. /api) are now local to each controller, and generators will be prefix-aware (--api generator will add an /api prefix to controllers) https://github.com/loco-rs/loco/pull/818

To migrate, please move prefixes from app.rs to each controller you use in controllers/, for example in notes controller:

Routes::new()
    .prefix("api/notes")
    .add("/", get(list))
  • starters: removed .devcontainer which can now be found in loco-devcontainer

  • starters: removed example notes scaffold (model, controllers, etc), and unified user and auth into a single file: auth.rs

  • generators: scaffold generator will now generate a CRUD with PUT and PATCH semantics for updating an entity https://github.com/loco-rs/loco/issues/896

  • cleanup: loco-extras was moved out of the repo, but we've incorporated MultiDB and ExtraDB from extras into loco-rs https://github.com/loco-rs/loco/pull/917

  • cargo loco doctor now checks for minimal required SeaORM CLI version

  • BREAKING Improved migration generator. If you have an existing migration project, add the following comment indicator to the top of the vec statement and right below the opening bracked like so in migration/src/lib.rs:

    fn migrations() -> Vec<Box<dyn MigrationTrait>> {
        vec![
            // inject-below (do not remove this comment)

v0.11.0

v0.10.1

  • Format(respond_to): Format extractor in controller can now be replaced with respond_to: RespondTo extractor for less typing.
  • When supplying data to views, you can now use data! instead of serde_json::json! for shorthand.
  • Refactor middlewares. https://github.com/loco-rs/loco/pull/785. Middleware selection, configuration, and tweaking is MUCH more powerful and convenient now. You can keep the middleware: section empty or remove it now, see more in the middleware docs
  • NEW (BREAKING) background worker subsystem is now queue agnostic. Providing for both Redis and Postgres with a change of configuration. This means you can now use a full-Postgres stack to remove Redis as a dependency if you wish. Here are steps to migrate your codebase:
// in your app.rs, change the worker registration code:

// BEFORE
fn connect_workers<'a>(p: &'a mut Processor, ctx: &'a AppContext) {
    p.register(DownloadWorker::build(ctx));
}

// AFTER
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()>{
    queue.register(DownloadWorker::build(ctx)).await?;
    Ok(())
}

// in your app.rs, replace the `worker` module references.
// REMOVE
worker::{AppWorker, Processor},
// REPLACE WITH
bgworker::{BackgroundWorker, Queue},

// in your workers change the signature, and add the `build` function

// BEFORE
impl worker::Worker<DownloadWorkerArgs> for DownloadWorker {
    async fn perform(&self, args: DownloadWorkerArgs) -> worker::Result<()> {

// AFTER
#[async_trait]
impl BackgroundWorker<DownloadWorkerArgs> for DownloadWorker {
    fn build(ctx: &AppContext) -> Self {
        Self { ctx: ctx.clone() }
    }
    async fn perform(&self, args: DownloadWorkerArgs) -> Result<()> {

// Finally, remove the `AppWorker` trait implementation completely.

// REMOVE
impl worker::AppWorker<DownloadWorkerArgs> for DownloadWorker {
    fn build(ctx: &AppContext) -> Self {
        Self { ctx: ctx.clone() }
    }
}

Finally, update your development.yaml and test.yaml with a kind:

queue:
  kind: Redis # add this to the existing `queue` section
  • UPGRADED (BREAKING): validator crate was upgraded which require some small tweaks to work with the new API:
// BEFORE:
#[validate(custom = "validation::is_valid_email")]
pub email: String,

// AFTER:
#[validate(custom (function = "validation::is_valid_email"))]
pub email: String,

Then update your Cargo.toml to take version 0.18:

# update
validator = { version = "0.18" }
  • UPGRADED (BREAKING): axum-test crate was upgraded Update your Cargo.toml to version 16:
# update
axum-test = { version = "16" }

v0.9.0

v0.8.1

v0.8.0

  • Added: loco-cli (loco new) now receives options from CLI and/or interactively asks for configuration options such as which asset pipeline, background worker type, or database provider to use.
  • Fix: custom queue names now merge with default queues.
  • Added remote_ip middleware for resolving client remote IP when under a proxy or loadbalancer, similar to the Rails remote_ip middleware.
  • Added secure_headers middleware for setting secure headers by default, similar to how https://github.com/github/secure_headers works. This is now ON by default to promote security-by-default.
  • Added: money, blob types to entitie generator.

0.7.0

  • Moving to timezone aware timestamps. From now on migrations will generate timestamps with time zone by default. Moving to TZ aware timestamps in combination with newly revamped timestamp code generation in SeaORM v1.0.0 finally allows for seamlessly moving between using sqlite and postgres with minimal or no entities code changes (resolved this long standing issue). TZ aware timestamps also aligns us with how Rails works today (initially Rails had a no-tz timestamps, and today the default is to use timestamps). If not specified the TZ is the server TZ, which is usually UTC, therefore semantically this is almost like a no-tz timestamp.

A few highlights:

Generated entities will now always use DateTimeWithTimeZone for the default timestamp fields:

...
Generating users.rs
    > Column `created_at`: DateTimeWithTimeZone, not_null
    > Column `updated_at`: DateTimeWithTimeZone, not_null
...

For better cross database provider compatibility, from now on prefer the tstz type instead of just ts when using generators (i.e. cargo loco generate model movie released:tstz)

0.6.2

0.6.1

0.6.0 https://github.com/loco-rs/loco/pull/610

v0.5.0 https://github.com/loco-rs/loco/pull/593

# before:
redis:
# after:
queue:
  • Breaking changes We have made a few parts of the context pluggable, such as the storage and new cache subsystems, this is why we decided to let you configure the context entirely before starting up your app. As a result, if you have a storage building hook code it should move to after_context, see example here. https://github.com/loco-rs/loco/pull/570

v0.4.0

  • Refactored model validation for better developer experience. Added a few traits and structs to loco::prelude for a smoother import story. Introducing Validatable:
impl Validatable for super::_entities::users::ActiveModel {
    fn validator(&self) -> Box<dyn Validate> {
        Box::new(Validator {
            name: self.name.as_ref().to_owned(),
            email: self.email.as_ref().to_owned(),
        })
    }
}

// now you can call `user.validate()` freely
  • Refactored type field mapping to be centralized. Now model, scaffold share the same field mapping, so no more gaps like https://github.com/loco-rs/loco/issues/513 (e.g. when calling loco generate model title:string the ability to map string into something useful in the code generation side) NOTE the _integer class of types are now just _int, e.g. big_int, so that it correlate with the int field name in a better way

  • Adding to to quiery dsl is_in and is_not_in. https://github.com/loco-rs/loco/pull/507

  • Added: in your configuration you can now use an initializers: section for initializer specific settings

    # Initializers Configuration
    initializers:
    # oauth2:
    #   authorization_code: # Authorization code grant type
    #     - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config.
    #       ... other fields
    
  • Docs: fix schema data types mapping. https://github.com/loco-rs/loco/pull/506

  • Let Result accept other errors. https://github.com/loco-rs/loco/pull/505

  • Allow trailing slashes in URIs by adding the NormalizePathLayer. https://github.com/loco-rs/loco/pull/481

  • BREAKING Move from Result<impl IntoResponse> to Result<Response>. This enables much greater flexibility building APIs, where with Result<Response> you mix and match response types based on custom logic (returning JSON and HTML/String in the same route).

  • Added: mime responders similar to respond_to in Rails:

  1. Use the Format extractor
  2. Match on respond_to
  3. Create different content for different response formats

The following route will always return JSON, unless explicitly asked for HTML with a Content-Type: text/html (or Accept: ) header:

pub async fn get_one(
    Format(respond_to): Format,
    Path(id): Path<i32>,
    State(ctx): State<AppContext>,
) -> Result<Response> {
    let item = load_item(&ctx, id).await?;
    match respond_to {
        RespondTo::Html => format::html(&format!("<html><body>{:?}</body></html>", item.title)),
        _ => format::json(item),
    }
}

0.3.2

0.3.1

0.2.3

v0.2.2

  • fix: public fields in mailer-op. e51b7e
  • fix: handle missing db permissions. e51b7e

v0.2.1

v0.2.0

// src/app.rs: add app context to routes function
impl Hooks for App {
  ...
  fn routes(_ctx: &AppContext) -> AppRoutes;
  ...
}
  • Add: Breaking changes change parameter type from &str to &Environment in src/app.rs
// src/app.rs: change parameter type for `environment` from `&str` to `&Environment`
impl Hooks for App {
    ...
    async fn boot(mode: StartMode, environment: &Environment) -> Result<BootResult> {
        create_app::<Self>(mode, environment).await
    }
    ...
  • Added: setting cookies:
format::render()
    .cookies(&[
        cookie::Cookie::new("foo", "bar"),
        cookie::Cookie::new("baz", "qux"),
    ])?
    .etag("foobar")?
    .json(notes)

v0.1.9

middlewares:
  compression:
    enable: true
#...
middlewares:
  etag:
    enable: true

usage:

  format::render()
      .etag("foobar")?
      .json(Entity::find().all(&ctx.db).await?)

Authentication: Added API Token Authentication!

  • See https://github.com/loco-rs/loco/pull/217 Now when you generate a saas starter or rest api starter you will get additional authentication methods for free:

  • Added: authentication added -- api authentication where each user has an API token in the schema, and you can authenticate with Bearer against that user.

  • Added: authentication added -- JWTWithUser extractor, which is a convenience for resolving the authenticated JWT claims into a current user from database

migrating an existing codebase

Add the following to your generated src/models/user.rs:

#[async_trait]
impl Authenticable for super::_entities::users::Model {
    async fn find_by_api_key(db: &DatabaseConnection, api_key: &str) -> ModelResult<Self> {
        let user = users::Entity::find()
            .filter(users::Column::ApiKey.eq(api_key))
            .one(db)
            .await?;
        user.ok_or_else(|| ModelError::EntityNotFound)
    }

    async fn find_by_claims_key(db: &DatabaseConnection, claims_key: &str) -> ModelResult<Self> {
        super::_entities::users::Model::find_by_pid(db, claims_key).await
    }
}

Update imports in this file to include model::Authenticable:

use loco_rs::{
    auth, hash,
    model::{Authenticable, ModelError, ModelResult},
    validation,
    validator::Validate,
};

v0.1.8

  • Added: loco version for getting an operable version string containing logical crate version and git SHA if available: 0.3.0 (<git sha>)

To migrate to this behavior from earlier versions, it requires adding the following to your app.rs app hooks:

    fn app_version() -> String {
        format!(
            "{} ({})",
            env!("CARGO_PKG_VERSION"),
            option_env!("BUILD_SHA")
                .or(option_env!("GITHUB_SHA"))
                .unwrap_or("dev")
        )
    }

Reminder: loco --version will give you the current Loco framework which your app was built against and loco version gives you your app version.

  • Added: loco generate migration for adding ad-hoc migrations
  • Added: added support in model generator for many-to-many link table generation via loco generate model --link
  • Docs: added Migration section, added relations documentation 1:M, M:M
  • Adding .devcontainer to starter projects https://github.com/loco-rs/loco/issues/170
  • Braking changes: Adding Hooks::boot application. Migration steps:
    // Load boot::{create_app, BootResult, StartMode} from loco_rs lib
    // Load migration: use migration::Migrator; Only when using DB
    // Adding boot hook with the following code
    impl Hooks for App {
      ...
      async fn boot(mode: StartMode, environment: &str) -> Result<BootResult> {
        // With DB:
        create_app::<Self, Migrator>(mode, environment).await
        // Without DB:
        create_app::<Self>(mode, environment).await
      }
      ...
    }
    

v0.1.7

v0.1.6

  • refactor: local settings are now <env>.local.yaml and available for all environments, for example you can add a local test.local.yaml and development.local.yaml
  • refactor: removed config-rs and now doing config loading by ourselves.
  • fix: email template rendering will not escape URLs
  • Config with variables: It is now possible to use tera templates in config YAML files

Example of pulling a port from environment:

server:
  port: { { get_env(name="NODE_PORT", default=5150) } }

It is possible to use any tera templating constructs such as loops, conditionals, etc. inside YAML configuration files.

DOCS:

ENHANCEMENTS:

0.1.5

NEW FEATURES

ENHANCEMENTS: