The generate service command
The generate service
command generates code that (1) defines a new network service, (2) implements the server-side
handling of the network protocol, (3) builds the binary, and (4) demonstrates some minimal integration testing and
acceptance testing for the new service.
The generate service
command is used like this:
$ saas-rs generate service user
The following content is added to the Rust workspace for you:
crates/
├── protocol/
│ ├── src/
│ │ ├── lib.rs
│ │ └── generated/
│ │ ├── acme_user_v1.rs
│ │ └── acme_user_v1_serde.rs
│ ├── Cargo.toml
│ └── build.rs
├── user_server/
│ ├── debian/
│ │ └── service
│ ├── src/
│ │ ├── consts/
│ │ │ ├── env_vars.rs
│ │ │ └── mod.rs
│ │ ├── v1/
│ │ │ ├── controllers/
│ │ │ │ ├── account.rs
│ │ │ │ ├── authentication.rs
│ │ │ │ ├── linked_account.rs
│ │ │ │ └── mod.rs
│ │ │ └── middlewawre/
│ │ │ ├── authorization.rs
│ │ │ └── mod.rs
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── tests/
│ │ ├── acceptance_login_with_github.rs
│ │ ├── integration_config_store_accounts.rs
│ │ ├── integration_config_store_linked_accounts.rs
│ │ └── testsupport.rs
│ └── Cargo.toml
proto/
└── acme/
└── user/
└── v1/
├── access_token.proto
├── access_token_resource.proto
├── account.proto
├── account_resource.proto
├── error.proto
├── linked_account.proto
├── linked_account_resource.proto
└── user_service.proto
The following top-level folders have the following purpose:
- proto - This folder defines your network protocols, including user-facing, admin-facing, and machine-facing services.
The contents consist of Protobuf files containing both view models (over-the-wire models) and gRPC service
definitions. The main gRPC service definition file is
user_service.proto
. - protocol - This folder contains your network protocols, in Rust form. These Rust files are generated by Tonic
and Prost, and derived from the proto files. In the
generated/
folder, theacme_user_v1.rs
module holds the generated structs for your view models and their resource actions, while theacme_user_v1_serde.rs
module holds pbjson support so that every Protobuf message (struct) can be serialized to JSON and BSON using camelCased keys. - user_server - this folder contains your new gRPC service endpoint. It compiles into a stand-alone binary that
runs with
cargo run --bin acme-user-server
, and also is made available in library form via thelib.rs
module, in case you want to embed it elsewhere if you want to combine user-facing, admin-facing, and machine-to-machine-facing gRPC services in a single all-inclusive binary.
As always, perform make
and then stage and commit the generated files before you start making changes:
$ make
cargo fmt --all
cargo build
Compiling protocol v0.1.0 (/Users/davidr/workspaces/foo/crates/protocol)
Compiling acme-user-server v0.1.0 (/Users/davidr/workspaces/foo/crates/user_server)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.46s
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.73s
$ git add -A
$ git commit -m "saas-rs generate service user"
Publishing a new v2
of your protocol
To publish a new v2
version of your protocol, the generate service
command is used like this:
$ saas-rs generate service user --version 2
It is up to you to decide how to implement the new protocol. One common strategy is to create v2 controllers that initially delegate to the old v1 controllers, and are then customized as needed.
You would typically leave the v1 controllers in your code for at least a few years while you give your customers (including your web developers) ample time to migrate off of the v1 protocol.
Running your new server
To run the server that implements your new "acme user v1" network service, a Config Store adapter is required. Configuration of a Config Store is explained elsewhere, so for now you can launch your server with the built-in in-memory ConfigStore adapter:
$ CONFIG_STORE_URL=memory://localhost cargo run --bin acme-user-server
Compiling protocol v0.1.0 (/Users/davidr/workspaces/foo/crates/protocol)
Compiling acme-user-server v0.1.0 (/Users/davidr/workspaces/foo/crates/user_server)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.39s
Running `target/debug/acme-user-server`
[2025-07-17T18:01:00Z INFO acme_user_server] Attached to config store config_store_url=memory://localhost
[2025-07-17T18:01:00Z INFO acme_user_server] Attached to session store session_store_url=redis://localhost:6379/8
[2025-07-17T18:01:00Z INFO acme_user_server] Attached to object store object_store_url=mongodb://localhost:27017/acme
[2025-07-17T18:01:00Z INFO acme_user_server] Created admin account
[2025-07-17T18:01:00Z INFO acme_user_server] Listening on 0.0.0.0:3000 for http
Or to run on a non-default port, use Foreman / Heroku / Dokku / CloudFoundry semantics:
$ PORT=1234 CONFIG_STORE_URL=memory://localhost cargo run --bin acme-user-server
...
[2025-07-17T18:04:18Z INFO acme_user_server] Listening on 0.0.0.0:1234 for http
Integration and Acceptance Testing
To perform integration testing and acceptance testing, ConfigStore and SessionStore adapters must be defined. Otherwise the tests will skip during testing, and only unit tests will run.
Integration and Acceptance tests can be invoked using in-memory storage adapters with:
$ export TEST_CONFIG_STORE_URL=memory://localhost
$ export TEST_SESSION_STORE_URL=memory://localhost
$ make check
Running tests/integration_config_store_accounts.rs (target/debug/deps/integration_config_store_accounts-97dc931ee3e81b10)
running 6 tests
test cannot_delete_non_existent_account ... ok
test cannot_find_non_existent_account ... ok
test cannot_update_non_existent_account ... ok
test can_create_account_with_caller_assigned_id ... ok
test can_create_account_and_auto_assign_id ... ok
test can_perform_crud_operations ... ok
Running tests/acceptance_login_with_github.rs (target/debug/deps/acceptance_login_with_github-ea31e740e423d74c)
running 1 test
test can_login_for_the_first_time ... ok
...
Debian APT Packaging
A crates/user_server/debian/service
file is also generated, which you can fill out in greater detail if you want
to package your new service with cargo-deb and publish it to a Debian APT
repository such as Deb Simple. You can do so on Linux with:
$ cargo install cargo-deb
$ cargo deb -p acme-user-server