Skip to content

Создание сервисов на бекенде

Обзор

Каждый backend API endpoint в Kontinuum определяется одной сервисной функцией с атрибутом #[api]. Proc-macro kontinuum-macros автоматически генерирует:

  1. Tauri-команду (для desktop-приложения) — с #[tauri::command] + #[specta::specta]
  2. HTTP-хэндлер (для headless-режима) — Axum handler с #[cfg(feature = "headless")]

Бизнес-логика пишется один раз, транспортный слой генерируется автоматически.

plantuml Diagram

Пошаговое руководство

1. Создай сервисную функцию

backend/src/services/example.rs:

rust
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:

rust
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:

rust
pub mod example;

backend/src/commands/mod.rs:

rust
pub mod example;

4. Зарегистрируй Tauri-команды

backend/src/setup/handlers.rs — добавь в collect_commands!:

rust
commands::example::get_items,
commands::example::create_item,

5. Зарегистрируй HTTP-маршруты

backend/src/api/router.rs — добавь в build_api_router():

rust
.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 — добавь:

typescript
get_items: { method: 'GET', path: '/api/items' },
create_item: { method: 'POST', path: '/api/items' },

7. Перегенерируй TypeScript биндинги

bash
nx run backend:gen:bindings

Specta автоматически создаст типы Item и команды getItems() / createItem() в frontend/src/shared/api/bindings.ts.

Как работает #[api] макрос

Классификация параметров

Макрос анализирует сигнатуру функции и автоматически классифицирует каждый параметр:

Сигнатура параметраКлассификацияTauri (desktop)HTTP (headless)
mgr: &Arc<Mutex<T>>ManagerState<'_, Arc<Mutex<T>>>&app_state.{name}
event_bus: &EventBusEventBusState<'_, EventBus>&app_state.event_bus
shared_state: &SharedStateSharedStateState<'_, SharedState>&app_state.shared_state
app: tauri::AppHandleAppHandleapp: tauri::AppHandleHTTP-хэндлер не создаётся
Всё остальное (String, u32, struct...)UserParamПередаётся напрямуюДесериализуется из JSON body

Что генерируется

Для функции:

rust
#[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-команда:

rust
#[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"):

rust
#[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:

rust
// 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 + _svcget_items_svc
Tauri-команда (генерируется)без _svcget_items
HTTP-хэндлер (генерируется)без _svc + _httpget_items_http
Request-структура (генерируется)PascalCase + ReqCreateItemReq
Имя менеджерасовпадает с полем HttpAppStatevault_manager

Ключевые файлы

ФайлНазначение
backend/macros/src/lib.rsEntry 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.rsHttpAppState — состояние для Axum
backend/src/api/error.rsresult_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.rscollect_commands!
  • [ ] HTTP-маршрут добавлен в api/router.rs (кроме tauri-only)
  • [ ] Маршрут добавлен в frontend/.../command-routes.ts
  • [ ] nx run backend:gen:bindings для TypeScript типов