The generate resource command

The generate resource command generates a model, plus its operations, including CRUD operations and whatever else. The command is used with arguments identical to the generate model command, like this:

$ saas-rs generate resource invoice --service user --version 1 customer_id address_1 address_2 city state zip postal_code country_iso2
$ make
$ git add -A
$ git commit -m "saas-rs generate resource invoice --service user --version 1 customer_id address_1 address_2 city state zip postal_code country_iso2" 

The following content is added or changed in your Rust workspace:

crates/
├── config_store/
│   ├── src/
│   │   └── bucket.rs
├── protocol/
│   ├── src/
│   │   └── generated/
│   │       ├── acme_user_v1.rs
│   │       └── acme_user_v1_serde.rs
├── user_server/
│   ├── src/
│   │   └── v1/
│   │       └── mod.rs
│   └── tests/
│       └── integration_config_store_invoices.rs
proto/
└── acme/
    └── user/
        └── v1/
            ├── invoice.proto
            ├── invoice_resource.proto
            └── user_service.proto

An examination of the proto/acme/user/v1/invoice_resource.proto file shows the generated default CRUD operations:

syntax = "proto3";

package acme.user.v1;

import "google/protobuf/field_mask.proto";
import "acme/user/v1/error.proto";
import "acme/user/v1/invoice.proto";

message InvoiceFilter {
    optional string id = 1;
}

message CreateInvoiceRequest {
    Invoice invoice = 1;
}

message CreateInvoiceResponse {
    Invoice invoice = 1;
}

message DeleteInvoiceRequest {
    string id = 1;
}

message DeleteInvoiceResponse {
}

message FindInvoiceRequest {
    string id = 1;
}

message FindInvoiceResponse {
    Invoice invoice = 1;
}

message FindManyInvoicesRequest {
    InvoiceFilter filter = 1;
    google.protobuf.FieldMask field_mask = 2;
    optional uint32 offset = 3;
    optional uint32 limit = 4;
}

message FindManyInvoicesResponse {
    repeated Invoice invoices = 1;
}

message UpdateInvoiceRequest {
    Invoice invoice = 1;
}

message UpdateInvoiceResponse {
    Invoice invoice = 1;
}

message ValidateInvoiceRequest {
    Invoice invoice = 1;
    bool existing = 2;
}

message ValidateInvoiceResponse {
    repeated ErrorObject errors = 1;
}

And for this new Protobuf file to be found by the Prost code generator, it needs to be referenced by the proto/acme/user/v1/user_sevice.proto file:

import "acme/user/v1/invoice_resource.proto";

An examination of the main gRPC service implementation file at crates/user_server/src/mod.rs shows the rpc stubs that were generated with Not Implemented Yet placeholders to ensure your workspace compiles:

impl User for UserGrpcServerV1 {
    ...
    
    async fn create_invoice(
        &self,
        _req: Request<CreateInvoiceRequest>,
    ) -> Result<Response<CreateInvoiceResponse>, Status> {
        todo!("NIY")
    }
        
    async fn delete_invoice(
        &self,
        _req: Request<DeleteInvoiceRequest>,
    ) -> Result<Response<DeleteInvoiceResponse>, Status> {
        todo!("NIY")
    }

    async fn find_invoice(&self, _req: Request<FindInvoiceRequest>) -> Result<Response<FindInvoiceResponse>, Status> {
        todo!("NIY")
    }

    async fn find_many_invoices(
        &self,
        _req: Request<FindManyInvoicesRequest>,
    ) -> Result<Response<FindManyInvoicesResponse>, Status> {
        todo!("NIY")
    }

    async fn update_invoice(
        &self,
        _req: Request<UpdateInvoiceRequest>,
    ) -> Result<Response<UpdateInvoiceResponse>, Status> {
        todo!("NIY")
    }

    async fn validate_invoice(
        &self,
        _req: Request<ValidateInvoiceRequest>,
    ) -> Result<Response<ValidateInvoiceResponse>, Status> {
        todo!("NIY")
    }
    ...
}

A Fork based graphical diff view does the best job of showing the changes that were interleaved into the proto/acme/user/v1/user_service.proto file:

Diff view of service proto file

Making Further Changes

The default CRUD code that was generated is just a starting point, and you are free to make changes to customize things to your liking. For example, you might:

  • Add new operations that are above and beyond the usual CRUD operations
  • Customize the pagination mechanism used by the find many operation
  • Customize the fields that can be filtered on during find many operations
  • Remove Delete and Update operations for resources that will be read-only, such as static lookup tables. The SaaS RS CLI does this for the saas-rs list generators command, which returns a static list of Generator records:
$ saas-rs list generators
┌──────────────────────┬─────────┬────────────────┬─────────────────────────────────┐
│ id                   ┆ type    ┆ name           ┆ description                     │
│ ---                  ┆ ---     ┆ ---            ┆ ---                             │
│ str                  ┆ str     ┆ str            ┆ str                             │
╞══════════════════════╪═════════╪════════════════╪═════════════════════════════════╡
│ d18nptv1t0sfku8vjl00 ┆ Feature ┆ api-keys       ┆ Adds API Key management and au… │
│ d126jjn1t0s8usbjajpg ┆ Feature ┆ file-transfer  ┆ Adds file upload+download capa… │
│ d1bc0dv1t0sda0dg0la0 ┆ Feature ┆ issue-tracking ┆ Adds issue tracking support to… │
│ d1bc2uf1t0sdd7jbftmg ┆ Feature ┆ service-broker ┆ Adds service broker support to… │
└──────────────────────┴─────────┴────────────────┴─────────────────────────────────┘

©2025 SaaS RS | Website | GitHub | GitLab | Contact