backbone-core 3.0.1

Backbone Framework Core - Foundation for generic CRUD system
Documentation

Backbone Core

Status: โœ… FULLY IMPLEMENTED Last Updated: 2026-06-06

๐Ÿฆด Foundation for generic CRUD system with 11 standard endpoints

Backbone Core is a foundational library that provides generic CRUD (Create, Read, Update, Delete) operations for any entity in Backbone Framework. It implements 11 standard Backbone endpoints consistently across both HTTP REST and gRPC protocols.

๐Ÿ“‹ Table of Contents

๐Ÿ“– Documentation

In-depth guides live in docs/:

๐ŸŽฏ Overview

Backbone Core is a protocol-agnostic, generic CRUD foundation that enables any service to automatically get:

  • โœ… 11 Standard Backbone endpoints for every entity
  • โœ… Both HTTP REST and gRPC protocol support
  • โœ… Pagination, filtering, and sorting
  • โœ… Bulk operations and soft delete
  • โœ… Type-safe generic implementations
  • โœ… JSON or form-encoded request bodies (auto-detected by Content-Type)
  • โœ… Production-ready error handling
  • โœ… Optional OpenAPI/Swagger schema generation (feature openapi, default-off)

The 11 Backbone Endpoints

# HTTP Method HTTP Endpoint gRPC Method Purpose
1 GET /api/v1/{collection} list() List with pagination, filtering, sorting
2 POST /api/v1/{collection} create() Create new entity
3 GET /api/v1/{collection}/:id get_by_id() Get entity by ID
4 PUT /api/v1/{collection}/:id update() Full entity update
5 PATCH /api/v1/{collection}/:id partial_update() Partial update (selected fields)
6 DELETE /api/v1/{collection}/:id soft_delete() Soft delete (mark as deleted)
7 POST /api/v1/{collection}/bulk bulk_create() Create multiple entities
8 POST /api/v1/{collection}/upsert upsert() Update or insert if not exists
9 GET /api/v1/{collection}/trash list_deleted() List deleted entities
10 POST /api/v1/{collection}/:id/restore restore() Restore soft-deleted entity
11 DELETE /api/v1/{collection}/empty empty_trash() Permanently delete all trash

Atomic batch endpoints

All-or-nothing operations applied inside a single transaction: ids are validated up-front, and if any id is missing (or already in the target state) the whole batch is rolled back with 400 Bad Request โ€” no partial writes. Batches are capped at MAX_BATCH_SIZE (1,000) items; larger payloads are rejected with 400. Lifecycle hooks and CRUD events fire per affected entity.

HTTP Method HTTP Endpoint Service method Purpose
PUT /api/v1/{collection}/bulk bulk_update() Full-update many entities ([{ "id", ...fields }])
PATCH /api/v1/{collection}/bulk bulk_partial_update() Partial-update many โ€” shared { ids, patch } or per-id { items: [{ id, patch }] }
POST /api/v1/{collection}/delete/bulk bulk_soft_delete() Soft-delete many by id ({ "ids": [...] })
POST /api/v1/{collection}/restore/bulk bulk_restore() Restore many soft-deleted by id
POST /api/v1/{collection}/restore/all restore_all() Restore every soft-deleted entity
DELETE /api/v1/{collection}/trash/bulk bulk_permanent_delete() Permanently delete many trashed by id

๐Ÿš€ Features

๐Ÿ”„ Protocol Agnostic

  • HTTP REST: Standard REST endpoints with JSON responses
  • gRPC: High-performance RPC with Protocol Buffers
  • Same interface: Both protocols provide identical functionality

๐Ÿ“Š Advanced CRUD Operations

  • Pagination: Automatic pagination with metadata
  • Depth-capped pagination: list/query endpoints return 400 Bad Request once a request would page beyond MAX_PAGINATION_OFFSET (10,000 rows โ€” ~100 pages at the MAX_PER_PAGE clamp of 100), avoiding expensive deep OFFSET scans and prompting clients to add filters instead.
  • Filtering: HashMap-based field filtering
  • Sorting: Multi-field sorting with configurable order
  • Sparse fieldsets: read endpoints (list, get_by_id, list_deleted, get_deleted_by_id) accept ?fields=a,b,c to trim each response object to the requested top-level keys plus the always-on id. Comma-separated, whitespace- trimmed; unknown keys are ignored and an empty/absent value returns every field. fields, include, and with are reserved query keys โ€” stripped before filters reach the repository, so they never leak into the WHERE clause.
  • Relation expansion (?include=): list and get_by_id accept ?include=<rel> (alias ?with=) to hydrate declared to-one relations, injecting each related row as a sibling object keyed by the relation name. Comma-separated; only relations the entity declares are honored, unknown names are ignored. Batched โ€” one WHERE id = ANY(...) per relation across the whole page (no N+1). Opt in per entity via the EntityRepoMeta::relations() hook (default none). Runs after field security and before sparse projection. v1 limitation: the expanded object is the raw related row (keys camelCased), not run through the target's response DTO or its @private security โ€” don't enable it for a target with private fields. See docs/api-reference.md.
  • Field-level security (@private / @owner): read endpoints strip an entity's @private response fields unless the caller may see them. An injectable AccessScope (Platform | Tenant(id), read from an axum Extension set by your auth middleware) decides visibility: Platform sees all, Tenant(id) sees private fields only when the row's @owner field equals id, and an absent scope fails closed. Opt in per entity via the EntityRepoMeta::private_fields() / owner_field() hooks (default no-op). Enforced before sparse projection, so it always beats a ?fields= request. See docs/api-reference.md.
  • Client-error aware: list/query endpoints return 400 Bad Request (not 500) when a bad filter or sort key produces a Postgres column "..." does not exist (SQLSTATE 42703) or invalid input syntax error โ€” e.g. a typo or a stray camelCase param like sortOrder.
  • Bulk Operations: Efficient bulk create and upsert
  • Atomic batch operations: transactional, all-or-nothing bulk update / partial-update / soft-delete / restore / permanent-delete โ€” a missing id rolls back the whole batch (400), and batches are capped at MAX_BATCH_SIZE (1,000)
  • Soft Delete: Trash management with restore functionality

๐Ÿ“จ Flexible Request Bodies

  • JSON or form: BackboneCrudHandler decodes create/update/upsert/bulk bodies via the JsonOrForm extractor, accepting both application/json and application/x-www-form-urlencoded.
  • Lenient default: falls back to JSON when the Content-Type is missing or unrecognized.
  • Drop-in: use JsonOrForm(x): JsonOrForm<T> anywhere you would write Json(x): Json<T> in your own handlers.

๐Ÿ›ก๏ธ Type Safety

  • Generic Types: Works with any entity implementing Entity trait
  • Compile-time Safety: Catch errors at compile time, not runtime
  • Proper Error Handling: Comprehensive anyhow::Error support

๐Ÿ“– Usage

1. Add Dependency

[dependencies]
backbone-core = "2.0.0"

2. Define Your Entity

use backbone_core::entity::*;
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use chrono::{DateTime, Utc};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
    id: Uuid,
    name: String,
    email: String,
    created_at: DateTime<Utc>,
    updated_at: DateTime<Utc>,
    deleted_at: Option<DateTime<Utc>>,
}

impl Entity for User {
    fn id(&self) -> &Uuid { &self.id }
    fn created_at(&self) -> DateTime<Utc> { self.created_at }
    fn updated_at(&self) -> DateTime<Utc> { self.updated_at }
    fn deleted_at(&self) -> Option<DateTime<Utc>> { self.deleted_at }
}

3. Implement HTTP Handler

use backbone_core::http::*;
use backbone_core::entity::*;

struct UserService;

impl BackboneHttpHandler<User> for UserService {
    fn list(&self, request: ListRequest) -> Result<ApiResponse<Vec<User>>> {
        let users = vec![]; // Fetch from database
        Ok(ApiResponse::success(users))
    }

    fn create(&self, request: User) -> Result<ApiResponse<User>> {
        Ok(ApiResponse::success(request))
    }
}

4. Implement gRPC Service

use backbone_core::grpc::*;

struct UserGrpcService;

impl BackboneGrpcService<User> for UserGrpcService {
    fn list(&self, request: GrpcListRequest) -> Result<GrpcResponse<GrpcListResponse<User>>> {
        let response = GrpcListResponse {
            items: users,
            total: users.len() as u64,
        };
        Ok(GrpcResponse::success(response))
    }
}

๐Ÿ”ง Technical Details

Core Traits

Entity Trait

pub trait Entity: Serialize + for<'de> Deserialize<'de> {
    fn id(&self) -> &Uuid;
    fn created_at(&self) -> DateTime<Utc>;
    fn updated_at(&self) -> DateTime<Utc>;
    fn deleted_at(&self) -> Option<DateTime<Utc>>;
}

Repository Traits

  • Repository - Basic CRUD operations
  • SearchableRepository - Search and filtering
  • SoftDeletableRepository - Soft delete operations
  • PaginatedRepository - Pagination support
  • BulkRepository - Bulk operations

๐Ÿ“š Examples

Pagination with Filtering

let list_request = ListRequest {
    page: Some(1),
    limit: Some(20),
    sort_by: Some("name".to_string()),
    filters: Some(HashMap::from([
        ("status".to_string(), "active".to_string()),
    ("role".to_string(), "admin".to_string()),
    ])),
};

๐Ÿงช Testing

Run Tests

# Run all tests
cargo test

# Run integration tests (requires database)
cargo test --test '*integration_tests*'

๐Ÿ”— Dependencies

[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.0", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"
thiserror = "1.0"

# gRPC
tonic = "0.12"
prost = "0.13"

# Web Framework
axum = "0.7"
tower = "0.4"

Feature flags

All optional capabilities are off by default (this crate is plumbing โ€” pull only what you need). See docs/configuration.md for the full matrix.

Feature Enables
postgres / database sqlx-backed PostgresRepository
prost Protobuf well-known types for gRPC
openapi utoipa::ToSchema on the HTTP types + openapi::BackboneComponents
full postgres + prost

๐Ÿค Contributing

  1. Follow existing code style
  2. Write tests for new features
  3. Update documentation

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


๐Ÿฆด Backbone Core - Foundation for generic CRUD operations