Создание сервисов на бекенде
Обзор
Каждый backend API endpoint в Kontinuum определяется одной сервисной функцией с атрибутом #[api]. Proc-macro kontinuum-macros автоматически генерирует:
- Tauri-команду (для desktop-приложения) — с
#[tauri::command]+#[specta::specta] - HTTP-хэндлер (для headless-режима) — Axum handler с
#[cfg(feature = "headless")]
Бизнес-логика пишется один раз, транспортный слой генерируется автоматически.
Пошаговое руководство
1. Создай сервисную функцию
backend/src/services/example.rs:
use kontinuum_macros::api;
use std::sync::{Arc, Mutex};
use crate::identity::manager::IdentityManager;
// GET без параметров:
#[api(GET, "/api/items")]
pub async fn get_items_svc(
identity_manager: &Arc<Mutex<IdentityManager>>,
) -> Result<Vec<Item>, String> {
let mgr = identity_manager.lock().map_err(|e| format!("Lock error: {e}"))?;
// бизнес-логика
Ok(vec![])
}
// POST с пользовательскими параметрами:
#[api(POST, "/api/items")]
pub async fn create_item_svc(
name: String,
count: u32,
identity_manager: &Arc<Mutex<IdentityManager>>,
) -> Result<Item, String> {
let mut mgr = identity_manager.lock().map_err(|e| format!("Lock error: {e}"))?;
// бизнес-логика
Ok(Item { id: "1".into(), name })
}2. Создай командный модуль
backend/src/commands/example.rs:
use serde::{Deserialize, Serialize};
use specta::Type;
/// DTO-типы (экспортируются в TypeScript через Specta)
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct Item {
pub id: String,
pub name: String,
}
// Re-export — импортирует сгенерированные Tauri-команды, HTTP-хэндлеры и Request-структуры
pub use crate::services::example::*;3. Зарегистрируй модули
backend/src/services/mod.rs:
pub mod example;backend/src/commands/mod.rs:
pub mod example;4. Зарегистрируй Tauri-команды
backend/src/setup/handlers.rs — добавь в collect_commands!:
commands::example::get_items,
commands::example::create_item,5. Зарегистрируй HTTP-маршруты
backend/src/api/router.rs — добавь в build_api_router():
.route("/api/items", get(commands::example::get_items_http))
.route("/api/items", post(commands::example::create_item_http))6. Добавь маршрут во frontend transport
frontend/src/shared/api/transport/command-routes.ts — добавь:
get_items: { method: 'GET', path: '/api/items' },
create_item: { method: 'POST', path: '/api/items' },7. Перегенерируй TypeScript биндинги
nx run backend:gen:bindingsSpecta автоматически создаст типы Item и команды getItems() / createItem() в frontend/src/shared/api/bindings.ts.
Как работает #[api] макрос
Классификация параметров
Макрос анализирует сигнатуру функции и автоматически классифицирует каждый параметр:
| Сигнатура параметра | Классификация | Tauri (desktop) | HTTP (headless) |
|---|---|---|---|
mgr: &Arc<Mutex<T>> | Manager | State<'_, Arc<Mutex<T>>> | &app_state.{name} |
event_bus: &EventBus | EventBus | State<'_, EventBus> | &app_state.event_bus |
shared_state: &SharedState | SharedState | State<'_, SharedState> | &app_state.shared_state |
app: tauri::AppHandle | AppHandle | app: tauri::AppHandle | HTTP-хэндлер не создаётся |
Всё остальное (String, u32, struct...) | UserParam | Передаётся напрямую | Десериализуется из JSON body |
Что генерируется
Для функции:
#[api(POST, "/api/vault/setup")]
pub async fn setup_vault_svc(
pin: String,
vault_manager: &Arc<Mutex<VaultManager>>,
device_manager: &Arc<Mutex<DeviceManager>>,
) -> Result<(), String> { ... }Макрос генерирует:
Tauri-команда:
#[tauri::command]
#[specta::specta]
pub async fn setup_vault(
vault_manager: tauri::State<'_, Arc<Mutex<VaultManager>>>,
device_manager: tauri::State<'_, Arc<Mutex<DeviceManager>>>,
pin: String,
) -> Result<(), String> {
setup_vault_svc(pin, &vault_manager, &device_manager).await
}HTTP-хэндлер (только при feature = "headless"):
#[cfg(feature = "headless")]
#[derive(serde::Deserialize)]
pub struct SetupVaultReq {
pub pin: String,
}
#[cfg(feature = "headless")]
pub async fn setup_vault_http(
axum::extract::State(app_state): axum::extract::State<HttpAppState>,
axum::Json(req): axum::Json<SetupVaultReq>,
) -> axum::response::Response {
crate::api::error::result_to_response(
setup_vault_svc(req.pin, &app_state.vault_manager, &app_state.device_manager).await
)
}Dependency Injection
Менеджеры и состояние инжектируются автоматически:
- Tauri: через
app.manage(Arc::clone(&manager))вsetup/mod.rs - HTTP: через
HttpAppState(структура вapi/http_state.rs), передаётся в Axum router через.with_state(app_state)
Имя параметра в сервисной функции должно совпадать с именем поля в HttpAppState:
// http_state.rs
pub struct HttpAppState {
pub identity_manager: Arc<Mutex<IdentityManager>>,
pub device_manager: Arc<Mutex<DeviceManager>>,
pub vault_manager: Arc<Mutex<VaultManager>>,
pub pairing_manager: Arc<Mutex<PairingManager>>,
pub shared_state: SharedState,
pub event_bus: EventBus,
}Правила именования
| Что | Конвенция | Пример |
|---|---|---|
| Сервисная функция | snake_case + _svc | get_items_svc |
| Tauri-команда (генерируется) | без _svc | get_items |
| HTTP-хэндлер (генерируется) | без _svc + _http | get_items_http |
| Request-структура (генерируется) | PascalCase + Req | CreateItemReq |
| Имя менеджера | совпадает с полем HttpAppState | vault_manager |
Ключевые файлы
| Файл | Назначение |
|---|---|
backend/macros/src/lib.rs | Entry point proc-macro |
backend/macros/src/classify.rs | Классификация параметров |
backend/macros/src/codegen.rs | Генерация Tauri + HTTP кода |
backend/src/services/ | Сервисные функции (бизнес-логика) |
backend/src/commands/ | DTO-типы + pub use services::* |
backend/src/setup/handlers.rs | Регистрация Tauri-команд (specta) |
backend/src/api/router.rs | Регистрация HTTP-маршрутов (Axum) |
backend/src/api/http_state.rs | HttpAppState — состояние для Axum |
backend/src/api/error.rs | result_to_response() — обёртка ошибок |
frontend/src/shared/api/transport/command-routes.ts | Маппинг команд → HTTP routes |
Чеклист
- [ ] Функция в
services/с#[api(METHOD, "/path")]и суффиксом_svc - [ ] Возвращаемый тип —
Result<T, String> - [ ] DTO-типы в
commands/с#[derive(Serialize, Deserialize, Type)] - [ ]
pub use crate::services::module::*;вcommands/ - [ ] Модуль зарегистрирован в
services/mod.rsиcommands/mod.rs - [ ] Tauri-команда добавлена в
setup/handlers.rs→collect_commands! - [ ] HTTP-маршрут добавлен в
api/router.rs(кроме tauri-only) - [ ] Маршрут добавлен в
frontend/.../command-routes.ts - [ ]
nx run backend:gen:bindingsдля TypeScript типов