Skip to content

Migration guide ​

Corvis is at its first version (0.1.0), so there are no breaking changes between versions to document yet. When a version that has them is released, this page will explain how to upgrade.

In the meantime, this guide is for those coming from a decorator-based Node.js framework written in TypeScript and organized into modules, controllers, and providers: the model is the same, so migration is mostly a matter of translating syntax. If your starting point is an Express or Fastify application without that structure, start with Migrating from Express or Migrating from Fastify.

Structure equivalences ​

In TypeScriptIn Corvis
A module with imports, controllers, providers, and exports#[module(imports = [...], controllers = [...], providers = [...], exports = [...])] on a struct
Global module#[global] above #[module], or #[module(global, ...)]
Static method that returns a dynamic module (forRoot)fn for_root(...) -> DynamicModule, built with DynamicModule::new::<Self>()
Class marked as injectable#[injectable] on a struct
Constructor dependencyArc<T> field: the constructor is generated for you
Optional dependencyOption<Arc<T>> field
Injection by token#[inject("TOKEN")] on the field
Value, class, factory, and alias providersProvider::use_value, use_class, use_factory, use_existing
Transient scope#[injectable(scope = "transient")]
Global guard, pipe, interceptor, or filter registered as a providerProvider::app_guard::<G>(), app_pipe, app_interceptor, app_filter

See Modules, Providers, and Custom providers.

Controller equivalences ​

In TypeScriptIn Corvis
Controller decorator with a path#[controller("cats")]
HTTP method decorators#[get], #[post(":id")], #[put], #[patch], #[delete], #[all], #[options], #[head] inside a #[routes] impl
Status code, header, redirect, version#[http_code(204)], #[header("Cache-Control", "none")], #[redirect("url", 301)], #[version("2")]
Handler parameters#[body], #[param("id", ParseIntPipe)], #[query], #[headers("authorization")], #[ip], #[req], #[res]
Custom parameter decoratorimpl FromContext for MyType and the parameter without an attribute
Metadata#[set_metadata("roles", ["admin"])]
Per-route guards, interceptors, pipes, and filters#[use_guards(...)], #[use_interceptors(...)], #[use_pipes(...)], #[use_filters(...)]
Throwing an HTTP exceptionReturning Err(NotFoundException::new("...")) in an HttpResult

Class attributes (#[use_guards], #[set_metadata]...) go below #[controller], because Rust processes attributes in order.

Bootstrap equivalences ​

In TypeScriptIn Corvis
Creating the application from the root moduleCorvisFactory::create::<AppModule>().await?
Global prefixapp.set_global_prefix("api")
Global pipes, guards, interceptors, and filtersapp.use_global_pipes(...) and its equivalents
CORSapp.enable_cors(CorsOptions::default())
Versioningapp.enable_versioning(VersioningOptions::uri().default_version("1"))
Shutdown hooksapp.enable_shutdown_hooks()
Getting a providerapp.get::<CatsService>()?
Listeningapp.listen(3000).await

Differences worth knowing ​

  • No request scope. There are only singleton and transient providers. For per-request data use the request extensions (req.insert, req.get) or FromContext. See Injection scopes.
  • No forwardRef. A circular dependency between providers is a startup error. See Circular dependencies.
  • Interceptors without observables. CallHandler::handle() returns a Future: you can transform, time, and cache the response, but not compose stream operators.
  • Pipes over JSON. Pipes receive and return serde_json::Value; the result is deserialized into the parameter's type.
  • Global enhancers and gateways. Global guards, pipes, and interceptors do not apply to WebSocket gateways. See Gateways.
  • No GraphQL or microservices for now.

Released under the MIT License.