Co-Pilot / 辅助式
更新于 24 days ago

vespera

Ddev-five-git
0.0k
dev-five-git/vespera
84
Agent 评分

💡 摘要

Vespera通过类似FastAPI的功能和自动OpenAPI生成简化了Rust中的API开发。

🎯 适合人群

寻找API解决方案的Rust开发者希望简化API创建的后端工程师从FastAPI转向Rust的开发者采用Rust进行微服务的团队寻求Rust集成的OpenAPI爱好者

🤖 AI 吐槽:看起来很能打,但别让配置把人劝退。

安全分析低风险

风险:Low。建议检查:是否执行 shell/命令行指令;是否发起外网请求(SSRF/数据外发);文件读写范围与路径穿越风险。以最小权限运行,并在生产环境启用前审计代码与依赖。


name: vespera description: Build APIs with Vespera - FastAPI-like DX for Rust/Axum. Covers route handlers, Schema derivation, and OpenAPI generation.

Vespera Usage Guide

Vespera = FastAPI DX for Rust. Zero-config OpenAPI 3.1 generation via compile-time macro scanning.

Quick Start

// 1. Main entry - vespera! macro handles everything let app = vespera!( openapi = "openapi.json", // writes file at compile time title = "My API", version = "1.0.0", docs_url = "/docs", // Swagger UI redoc_url = "/redoc" // ReDoc alternative ); // 2. Route handlers - MUST be pub async fn #[vespera::route(get, path = "/{id}", tags = ["users"])] pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... } // 3. Custom types - derive Schema for OpenAPI inclusion #[derive(Serialize, Deserialize, vespera::Schema)] pub struct User { id: u32, name: String }

Type Mapping Reference

| Rust Type | OpenAPI Schema | Notes | |-----------|----------------|-------| | String, &str | string | | | i8-i128, u8-u128 | integer | | | f32, f64 | number | | | bool | boolean | | | Vec<T> | array + items | | | Option<T> | T (nullable context) | Parent marks as optional | | HashMap<K,V> | object + additionalProperties | | | () | empty response | 204 No Content | | Custom struct | $ref | Must derive Schema |

Extractor Mapping Reference

| Axum Extractor | OpenAPI Location | Notes | |----------------|------------------|-------| | Path<T> | path parameter | T can be tuple or struct | | Query<T> | query parameters | Struct fields become params | | Json<T> | requestBody | application/json | | Form<T> | requestBody | application/x-www-form-urlencoded | | State<T> | ignored | Internal, not API | | Extension<T> | ignored | Internal, not API | | TypedHeader<T> | header parameter | | | HeaderMap | ignored | Too dynamic |


Route Handler Requirements

// ❌ Private function - NOT discovered async fn get_users() -> Json<Vec<User>> { ... } // ❌ Non-async function - NOT supported pub fn get_users() -> Json<Vec<User>> { ... } // ✅ Must be pub async fn pub async fn get_users() -> Json<Vec<User>> { ... }

File Structure → URL Mapping

src/routes/
├── mod.rs           → /              (root routes)
├── users.rs         → /users
├── posts.rs         → /posts
└── admin/
    ├── mod.rs       → /admin
    └── stats.rs     → /admin/stats

Handler path is: {file_path} + {#[route] path}

// In src/routes/users.rs #[vespera::route(get, path = "/{id}")] pub async fn get_user(...) // → GET /users/{id}

Serde Integration

Vespera respects serde attributes:

#[derive(Serialize, Deserialize, Schema)] #[serde(rename_all = "camelCase")] // ✅ Respected in schema pub struct UserResponse { user_id: u32, // → "userId" in JSON Schema #[serde(rename = "fullName")] // ✅ Respected name: String, // → "fullName" in JSON Schema #[serde(default)] // ✅ Marks as optional in schema bio: Option<String>, #[serde(skip)] // ✅ Excluded from schema internal_id: u64, }

Debugging Tips

Schema Not Appearing

  1. Check #[derive(Schema)] on the type
  2. Check type is used in a route handler's input/output
  3. Check for generic types - all type params need Schema
// Generic types need Schema on all params #[derive(Schema)] struct Paginated<T: Schema> { // T must also derive Schema items: Vec<T>, total: u32, }

Macro Expansion

# See what vespera! generates cargo expand # Validate OpenAPI output npx @apidevtools/swagger-cli validate openapi.json

Environment Variables

| Variable | Purpose | Default | |----------|---------|---------| | VESPERA_DIR | Route folder name | routes | | VESPERA_OPENAPI | OpenAPI output path | none | | VESPERA_TITLE | API title | API | | VESPERA_VERSION | API version | CARGO_PKG_VERSION | | VESPERA_DOCS_URL | Swagger UI path | none | | VESPERA_REDOC_URL | ReDoc path | none | | VESPERA_SERVER_URL | Server URL | http://localhost:3000 |


schema_type! Macro (RECOMMENDED)

ALWAYS prefer schema_type! over manually defining request/response structs.

Benefits:

  • Single source of truth (your model)
  • Auto-generated From impl for easy conversion
  • Automatic type resolution (enums, custom types → absolute paths)
  • SeaORM relation support (HasOne, BelongsTo, HasMany)
  • No manual field synchronization

Why Not Manual Structs?

// ❌ BAD: Manual struct definition - requires sync with Model #[derive(Serialize, Deserialize, Schema)] pub struct UserResponse { pub id: i32, pub name: String, pub email: String, // Forgot to add new field? Schema out of sync! } // ✅ GOOD: Derive from Model - always in sync schema_type!(UserResponse from crate::models::user::Model, omit = ["password_hash"]);

Basic Syntax

// Pick specific fields schema_type!(CreateUserRequest from crate::models::user::Model, pick = ["name", "email"]); // Omit specific fields schema_type!(UserResponse from crate::models::user::Model, omit = ["password_hash", "internal_id"]); // Add new fields (NOTE: no From impl generated when using add) schema_type!(UpdateUserRequest from crate::models::user::Model, pick = ["name"], add = [("id": i32)]); // Rename fields schema_type!(UserDTO from crate::models::user::Model, rename = [("id", "user_id")]); // Partial updates (all fields become Option<T>) schema_type!(UserPatch from crate::models::user::Model, partial); // Partial updates (specific fields only) schema_type!(UserPatch from crate::models::user::Model, partial = ["name", "email"]); // Custom serde rename strategy schema_type!(UserSnakeCase from crate::models::user::Model, rename_all = "snake_case"); // Custom OpenAPI schema name schema_type!(Schema from Model, name = "UserSchema"); // Skip Schema derive (won't appear in OpenAPI) schema_type!(InternalDTO from Model, ignore); // Disable Clone derive schema_type!(LargeResponse from SomeType, clone = false);

Same-File Model Reference

When the model is in the same file, use simple name with name parameter:

// In src/models/user.rs pub struct Model { pub id: i32, pub name: String, pub status: UserStatus, // Custom enum - auto-resolved to absolute path } pub enum UserStatus { Active, Inactive } // Simple `Model` path works - module path inferred from file location vespera::schema_type!(Schema from Model, name = "UserSchema");

Cross-File References

Reference structs from other files using full module paths:

// In src/routes/users.rs use vespera::schema_type; // Reference model from src/models/user.rs schema_type!(CreateUserRequest from crate::models::user::Model, pick = ["name", "email"]);

The macro reads the source file at compile time - no special annotations needed on the source struct.

Auto-Generated From Impl

When add is NOT used, schema_type! generates a From impl for easy conversion:

// This: schema_type!(UserResponse from crate::models::user::Model, omit = ["password_hash"]); // Generates: pub struct UserResponse { id, name, email, created_at } impl From<crate::models::user::Model> for UserResponse { fn from(source: crate::models::user::Model) -> Self { Self { id: source.id, name: source.name, ... } } } // Usage: let model: Model = db.find_user(id).await?; Json(model.into()) // Easy conversion!

Note: From is NOT generated when add is used (can't auto-populate added fields).

Parameters

| Parameter | Description | Example | |-----------|-------------|---------| | pick | Include only these fields | pick = ["name", "email"] | | omit | Exclude these fields | omit = ["password"] | | rename | Rename fields | rename = [("id", "user_id")] | | add | Add new fields (disables From impl) | add = [("extra": String)] | | partial | Make fields optional for PATCH | partial or partial = ["name"] | | name | Custom OpenAPI schema name | name = "UserSchema" | | rename_all | Serde rename strategy | rename_all = "camelCase" | | ignore | Skip Schema derive | bare keyword | | clone | Control Clone derive (default: true) | clone = false |

SeaORM Integration (RECOMMENDED)

schema_type! has first-class SeaORM support with automatic relation handling:

// src/models/memo.rs #[derive(Clone, Debug, DeriveEntityModel)] #[sea_orm(table_name = "memo")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub title: String, pub user_id: i32, pub status: MemoStatus, // Custom enum pub user: BelongsTo<super::user::Entity>, // → Option<Box<UserSchema>> pub comments: HasMany<super::comment::Entity>, // → Vec<CommentSchema> pub created_at: DateTimeWithTimeZone, // → chrono::DateTime<FixedOffset> } #[derive(EnumIter, DeriveActiveEnum, Serialize, Deserialize, Schema)] pub enum MemoStatus { Draft, Published, Archived } // Generates Schema with proper types - no imports needed! vespera::schema_type!(Schema from Model, name = "MemoSchema");

Automatic Type Conversions:

| SeaORM Type | Generated Type | Notes | |-------------|---------------|-------| | HasOne<Entity> | Box<Schema> or Option<Box<Schema>> | Based on FK nullability | | BelongsTo<Entity> | Option<Box<Schema>> | Always optional | | HasMany<Entity> | Vec<Schema> | | | DateTimeWithTimeZone | vespera::chrono::DateTime<FixedOffset> | No SeaORM import needed | | Custom enums | crate::module::EnumName | Auto-resolved to absolute path |

Circular Reference Handling: Automatically detected and handled by inlining fields.

Complete Example

// src/models/user.rs (Sea-ORM entity) #[derive(Clone, Debug, DeriveEntityModel, Serialize, Deserialize)] #[sea_orm(table_name = "us
五维分析
清晰度8/10
创新性8/10
实用性9/10
完整性8/10
可维护性9/10
优缺点分析

优点

  • 零配置的OpenAPI生成
  • Rust的强类型安全
  • 与现有Rust项目的轻松集成
  • 支持SeaORM等高级功能

缺点

  • 仅限于Rust生态系统
  • 对新手来说学习曲线较陡
  • 复杂API可能需要额外设置

相关技能

pytorch

S
toolCode Lib / 代码库
92/ 100

“它是深度学习的瑞士军刀,但祝你好运能从47种安装方法里找到那个不会搞崩你系统的那一个。”

agno

S
toolCode Lib / 代码库
90/ 100

“它承诺成为智能体领域的Kubernetes,但得看开发者有没有耐心学习又一个编排层。”

nuxt-skills

S
toolCo-Pilot / 辅助式
90/ 100

“这本质上是一份组织良好的小抄,能把你的 AI 助手变成一只 Nuxt 框架的复读机。”

免责声明:本内容来源于 GitHub 开源项目,仅供展示和评分分析使用。

版权归原作者所有 dev-five-git.