-
-
Notifications
You must be signed in to change notification settings - Fork 0
Attributes en
Complete guide to all attributes supported by entity-derive.
Applied to the struct with #[entity(...)]:
#[derive(Entity)]
#[entity(
table = "users",
schema = "core",
sql = "full",
dialect = "postgres",
uuid = "v7",
soft_delete,
returning = "full",
error = "AppError",
events,
hooks,
commands,
transactions
)]
pub struct User { /* ... */ }| Attribute | Required | Default | Description |
|---|---|---|---|
table |
Yes | — | Database table name |
schema |
No | "public" |
Database schema |
sql |
No | "full" |
SQL generation level |
dialect |
No | "postgres" |
Database dialect |
uuid |
No | "v7" |
UUID version for ID generation |
soft_delete |
No | false |
Enable soft delete |
returning |
No | "full" |
RETURNING clause mode |
error |
No | sqlx::Error |
Custom error type |
events |
No | false |
Generate lifecycle events |
hooks |
No | false |
Generate lifecycle hooks trait |
commands |
No | false |
Enable CQRS command pattern |
transactions |
No | false |
Generate transaction repository adapter |
Database table name.
#[entity(table = "users")] // → FROM users
#[entity(table = "user_profiles")] // → FROM user_profilesDatabase schema. Default: "public".
#[entity(table = "users")] // → FROM public.users
#[entity(table = "users", schema = "core")] // → FROM core.users
#[entity(table = "users", schema = "auth")] // → FROM auth.usersSQL generation level. Default: "full".
| Value | Repository Trait | PgPool Implementation | Use Case |
|---|---|---|---|
"full" |
Yes | Yes | Standard CRUD entities |
"trait" |
Yes | No | Custom queries (joins, CTEs) |
"none" |
No | No | DTOs only, no database |
#[entity(table = "users", sql = "full")] // Full automation (default)
#[entity(table = "users", sql = "trait")] // Only trait, implement SQL yourself
#[entity(table = "users", sql = "none")] // No database layer at allDatabase dialect for SQL generation. Default: "postgres".
| Dialect | Aliases | Client Type | Status |
|---|---|---|---|
"postgres" |
"pg", "postgresql"
|
sqlx::PgPool |
Stable |
"clickhouse" |
"ch" |
clickhouse::Client |
Planned |
"mongodb" |
"mongo" |
mongodb::Client |
Planned |
UUID version for auto-generated primary keys. Default: "v7".
| Version | Method | Properties |
|---|---|---|
"v7" |
Uuid::now_v7() |
Time-ordered, sortable (recommended) |
"v4" |
Uuid::new_v4() |
Random, widely compatible |
#[entity(table = "users", uuid = "v7")] // Time-ordered (default)
#[entity(table = "sessions", uuid = "v4")] // Random UUIDWhy UUID v7?
- Time-ordered: natural sorting by creation time
- Better database index performance
- No coordination required (unlike sequences)
- Globally unique across distributed systems
Enable soft delete to mark records as deleted instead of removing them.
#[derive(Entity)]
#[entity(table = "documents", soft_delete)]
pub struct Document {
#[id]
pub id: Uuid,
#[field(create, response)]
pub title: String,
#[field(skip)]
pub deleted_at: Option<DateTime<Utc>>, // Required field
}Generated methods:
-
delete()— Setsdeleted_at = NOW()instead of DELETE -
hard_delete()— Permanently removes the record -
restore()— Setsdeleted_at = NULL -
find_by_id()/list()— Automatically filter deleted records -
find_by_id_with_deleted()/list_with_deleted()— Include deleted records
Control what data is fetched back after INSERT/UPDATE. Default: "full".
| Mode | SQL Clause | Use Case |
|---|---|---|
"full" |
RETURNING * |
Get all fields including DB-generated (default) |
"id" |
RETURNING id |
Confirm insert, return pre-built entity |
"none" |
(no RETURNING) | Fire-and-forget, fastest option |
"col1, col2" |
RETURNING col1, col2 |
Return specific columns |
#[entity(table = "logs", returning = "none")] // Fastest
#[entity(table = "users", returning = "full")] // Get DB-generated values
#[entity(table = "events", returning = "id, created_at")] // Custom columnsCustom error type for repository. Default: sqlx::Error.
#[derive(Debug)]
pub enum AppError {
Database(sqlx::Error),
NotFound,
Validation(String),
}
impl std::error::Error for AppError {}
impl std::fmt::Display for AppError { /* ... */ }
// Required: convert from sqlx::Error
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
AppError::Database(err)
}
}
#[derive(Entity)]
#[entity(table = "users", error = "AppError")]
pub struct User { /* ... */ }
// Generated repository uses AppError:
// impl UserRepository for PgPool {
// type Error = AppError;
// ...
// }Generate lifecycle events enum. See Events for details.
#[entity(table = "orders", events)]Generated:
pub enum OrderEvent {
Created(Order),
Updated { id: Uuid, changes: UpdateOrderRequest },
Deleted(Uuid),
}Generate lifecycle hooks trait. See Hooks for details.
#[entity(table = "users", hooks)]Generated:
#[async_trait]
pub trait UserHooks: Send + Sync {
type Error: std::error::Error + Send + Sync;
async fn before_create(&self, dto: &mut CreateUserRequest) -> Result<(), Self::Error>;
async fn after_create(&self, entity: &User) -> Result<(), Self::Error>;
async fn before_update(&self, id: &Uuid, dto: &mut UpdateUserRequest) -> Result<(), Self::Error>;
async fn after_update(&self, entity: &User) -> Result<(), Self::Error>;
async fn before_delete(&self, id: &Uuid) -> Result<(), Self::Error>;
async fn after_delete(&self, id: &Uuid) -> Result<(), Self::Error>;
}Enable CQRS command pattern. See Commands for details.
#[entity(table = "users", commands)]
#[command(Register)]
#[command(Deactivate, requires_id)]Generate transaction repository adapter for type-safe multi-entity transactions.
#[derive(Entity)]
#[entity(table = "accounts", transactions)]
pub struct Account {
#[id]
pub id: Uuid,
#[field(create, update, response)]
pub balance: i64,
}Generated:
-
AccountTransactionRepo<'t>— Repository adapter for transaction context -
TransactionWithAccounttrait withwith_accounts()method
Usage:
use entity_core::prelude::*;
async fn transfer(pool: &PgPool, from: Uuid, to: Uuid, amount: i64) -> Result<(), AppError> {
Transaction::new(pool)
.with_accounts()
.run(|mut ctx| async move {
let from_acc = ctx.accounts().find_by_id(from).await?
.ok_or(AppError::NotFound)?;
ctx.accounts().update(from, UpdateAccountRequest {
balance: Some(from_acc.balance - amount),
}).await?;
ctx.accounts().update(to, UpdateAccountRequest {
balance: Some(to_acc.balance + amount),
}).await?;
Ok(())
})
.await
}Transaction methods:
-
create(dto)— Create entity within transaction -
find_by_id(id)— Find entity by ID -
update(id, dto)— Update entity -
delete(id)— Delete entity (respectssoft_delete) -
list(limit, offset)— List entities
Features:
- Automatic rollback on error or panic
- Type-safe builder pattern
- Full CRUD support within transaction
Applied to individual fields.
Marks the primary key field.
Behavior:
- Auto-generates UUID (v7 by default, configurable with
uuidattribute) - Always included in
ResponseDTO - Excluded from
CreateRequestandUpdateRequest
#[id]
pub id: Uuid,Marks auto-generated fields (timestamps, sequences).
Behavior:
- Gets
Default::default()inFrom<CreateRequest> - Excluded from
CreateRequestandUpdateRequest - Can be included in
Responsewith#[field(response)]
#[auto]
#[field(response)]
pub created_at: DateTime<Utc>,Controls DTO inclusion. Combine multiple options:
#[field(create)] // Only in CreateRequest
#[field(update)] // Only in UpdateRequest
#[field(response)] // Only in Response
#[field(create, response)] // In Create and Response
#[field(create, update, response)] // In all three
#[field(skip)] // Excluded from all DTOsInclude field in CreateRequest DTO.
#[field(create)]
pub email: String,
// Generated:
pub struct CreateUserRequest {
pub email: String,
}Include field in UpdateRequest DTO.
Important: Non-optional fields are automatically wrapped in Option<T> for partial updates.
#[field(update)]
pub name: String, // Not Option
// Generated:
pub struct UpdateUserRequest {
pub name: Option<String>, // Wrapped automatically
}Include field in Response DTO.
#[field(response)]
pub email: String,
// Generated:
pub struct UserResponse {
pub id: Uuid, // Always included (has #[id])
pub email: String, // Included
}Exclude field from all DTOs. Use for sensitive data.
#[field(skip)]
pub password_hash: String,Important: skip overrides all other field options. The field will only exist in:
- Original entity struct
-
Rowstruct (for database reads) -
Insertablestruct (for database writes)
Generate query filter fields. See Filtering for details.
#[filter] // Exact match: WHERE field = $n
#[filter(eq)] // Same as above
#[filter(like)] // Pattern match: WHERE field ILIKE $n
#[filter(range)] // Range: WHERE field >= $n AND field <= $mForeign key relation. See Relations for details.
#[belongs_to(User)]
pub user_id: Uuid,Generated: find_user() method in repository.
One-to-many relation (entity-level). See Relations for details.
#[has_many(Post)]
pub struct User { /* ... */ }Generated: find_posts() method in repository.
Generate partial view struct (entity-level).
#[projection(Public: id, name, avatar)]
#[projection(Admin: id, name, email, role)]
pub struct User { /* ... */ }Generated:
UserPublic { id, name, avatar }UserAdmin { id, name, email, role }-
From<User>implementations -
find_by_id_public(),find_by_id_admin()methods
Applied at entity level with #[command(...)].
| Syntax | Effect |
|---|---|
#[command(Name)] |
Uses all #[field(create)] fields |
#[command(Name: field1, field2)] |
Uses only specified fields (adds requires_id) |
#[command(Name, requires_id)] |
Adds ID field, no other fields |
#[command(Name, source = "create")] |
Explicitly use create fields (default) |
#[command(Name, source = "update")] |
Use update fields (optional, adds requires_id) |
#[command(Name, source = "none")] |
No payload fields |
#[command(Name, payload = "Type")] |
Uses custom payload struct |
#[command(Name, result = "Type")] |
Uses custom result type |
#[command(Name, kind = "create")] |
Hint: creates entity (default) |
#[command(Name, kind = "update")] |
Hint: modifies entity |
#[command(Name, kind = "delete")] |
Hint: removes entity (returns ()) |
#[command(Name, kind = "custom")] |
Hint: custom operation |
See Commands for detailed documentation.
#[derive(Entity)]
#[entity(
table = "posts",
schema = "blog",
sql = "full",
dialect = "postgres",
uuid = "v7",
soft_delete,
returning = "full",
events,
hooks,
commands
)]
#[has_many(Comment)]
#[projection(Summary: id, title, author_id, created_at)]
#[command(Publish)]
#[command(Archive, requires_id)]
pub struct Post {
#[id]
pub id: Uuid,
#[field(create, update, response)]
#[filter(like)]
pub title: String,
#[field(create, update, response)]
pub content: String,
#[field(create, response)]
#[belongs_to(User)]
#[filter]
pub author_id: Uuid,
#[field(update, response)]
pub published: bool,
#[field(response)]
#[filter(range)]
pub view_count: i64,
#[field(skip)]
pub moderation_notes: String,
#[field(skip)]
pub deleted_at: Option<DateTime<Utc>>,
#[auto]
#[field(response)]
#[filter(range)]
pub created_at: DateTime<Utc>,
#[auto]
#[field(response)]
pub updated_at: DateTime<Utc>,
}| I want to... | Attributes |
|---|---|
| Auto-generate primary key | #[id] |
| Use random UUID |
uuid = "v4" on entity |
| Use time-ordered UUID |
uuid = "v7" (default) |
| Accept in POST body | #[field(create)] |
| Accept in PATCH body | #[field(update)] |
| Return in API response | #[field(response)] |
| Accept and return | #[field(create, update, response)] |
| Hide from all APIs | #[field(skip)] |
| Auto-generate timestamp |
#[auto] + #[field(response)]
|
| Read-only (DB managed) |
#[field(response)] only |
| Write-only (no return) |
#[field(create)] only |
| Custom SQL queries | sql = "trait" |
| DTOs only, no DB | sql = "none" |
| Soft delete records |
soft_delete on entity |
| Custom error type |
error = "MyError" on entity |
| Filter by exact value |
#[filter] on field |
| Filter by pattern |
#[filter(like)] on field |
| Filter by range |
#[filter(range)] on field |
| Track entity changes |
events on entity |
| Run code on lifecycle |
hooks on entity |
| Use domain commands |
commands on entity + #[command(...)]
|
| Use multi-entity transactions |
transactions on entity |
| Define relationship |
#[belongs_to(Entity)] or #[has_many(Entity)]
|
| Partial entity view | #[projection(Name: fields)] |
🇬🇧 English | 🇷🇺 Русский | 🇰🇷 한국어 | 🇪🇸 Español | 🇨🇳 中文
🇬🇧 English | 🇷🇺 Русский | 🇰🇷 한국어 | 🇪🇸 Español | 🇨🇳 中文
Getting Started
Features
Advanced
Начало работы
Возможности
Продвинутое
시작하기
기능
고급
Comenzando
Características
Avanzado
入门
功能
高级