# 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
- [Overview](#overview)
- [Features](#features)
- [Usage](#usage)
- [Technical Details](#technical-details)
- [Examples](#examples)
- [Testing](#testing)
- [Contributing](#contributing)
## ๐ Documentation
In-depth guides live in [`docs/`](docs/README.md):
- [architecture.md](docs/architecture.md) โ how the crate is structured
- [usage.md](docs/usage.md) โ wire up CRUD for your entity, with examples
- [api-reference.md](docs/api-reference.md) โ every endpoint, query param, and response shape
- [configuration.md](docs/configuration.md) โ feature flags and runtime limits
- [openapi.md](docs/openapi.md) โ generate & serve an OpenAPI/Swagger spec (feature `openapi`)
## ๐ฏ 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
| 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.
| `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](docs/api-reference.md#relation-expansion-include).
- **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](docs/api-reference.md#field-level-security-private--owner).
- **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`](src/extractors.rs) 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
```toml
[dependencies]
backbone-core = "2.0.0"
```
### 2. Define Your Entity
```rust
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
```rust
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
```rust
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
```rust
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<T>** - Basic CRUD operations
- **SearchableRepository<T>** - Search and filtering
- **SoftDeletableRepository<T>** - Soft delete operations
- **PaginatedRepository<T>** - Pagination support
- **BulkRepository<T>** - Bulk operations
## ๐ Examples
### Pagination with Filtering
```rust
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
```bash
# Run all tests
cargo test
# Run integration tests (requires database)
cargo test --test '*integration_tests*'
```
## ๐ Dependencies
```toml
[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](docs/configuration.md) for the full matrix.
| `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](LICENSE) file for details.
---
**๐ฆด Backbone Core - Foundation for generic CRUD operations**