# forge-jobs-api
[](https://crates.io/crates/forge-jobs-api)
[](https://docs.rs/forge-jobs-api)
[](https://github.com/dandush03/forge#license)
HTTP transport for [`forge-jobs`](../forge-jobs/) — Axum routes over
the same handler bodies the in-process Tauri / desktop binding would
call. Lets ops poke a deployed queue without an in-process Rust
binding and gives sidecar tooling a stable JSON contract.
## Install
```toml
[dependencies]
forge-jobs = "0.1"
forge-jobs-api = "0.1"
# For Postgres-backed deploys:
forge-jobs = { version = "0.1", features = ["postgres"] }
forge-jobs-api = { version = "0.1", features = ["postgres"] }
```
## Use
The crate exposes:
- `forge_jobs_api::router()` — Axum `Router` you mount under your
service. Routes are versioned-flat (no `/v1/` prefix today; the
internal API is still settling).
- `forge_jobs_api::handlers::*` — pure async functions over
`&Storage`. Same bodies the Tauri plugin's IPC commands call, so
the two transports can't drift apart. Test against an in-memory
SQLite — no router or HTTP container needed.
- `forge_jobs_api::dto::*` — request/response shapes shared between
HTTP and IPC consumers.
- `forge_jobs_api::Error` — handler error type. Implements
`axum::response::IntoResponse` so each variant maps to a sensible
HTTP status code (400 / 404 / 409 / 429 / 500).
### ⚠️ Security — the routes are unauthenticated
`router()` returns an Axum `Router` with **no auth, no rate
limiting, no body-size limit, no CORS**. Some routes mutate state
(`POST /queue/:name/backoff`). You MUST either:
1. **Bind to loopback** (`127.0.0.1`) so only same-host
processes can hit it, or
2. **Mount behind your own middleware** that enforces auth before
the request reaches `router()`:
```rust,ignore
use axum::Router;
let app: Router = Router::new()
.nest("/api/queue", forge_jobs_api::router(storage))
.layer(my_auth_middleware);
```
A future minor release will add an optional `auth` feature with a
pluggable token check; today, that's on you.
### Wiring it up
```rust,ignore
use std::sync::Arc;
use forge_jobs::storage::{DatabaseConfig, QueuePaths};
use forge_jobs::{HandlerRegistry, QueueRuntime};
use forge_jobs_api::router;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let paths = /* your QueuePaths impl */;
let storage = DatabaseConfig::load(&paths)?.open_storage(&paths).await?;
// Spawn workers (queue background). `with_queues` declares which
// queues this worker consumes — required; read from the env in prod
// via `forge_jobs::queues_from_env()`.
let runtime = QueueRuntime::new(storage.clone(), HandlerRegistry::new(), Arc::new(forge_jobs::DefaultRouter))
.with_queues(["default".to_owned()]);
runtime.ensure_queue("default", 4).await?;
let _handle = runtime.start().await?;
// Mount the HTTP API. Bound to loopback because routes are
// unauthenticated — see the Security section above before
// changing to 0.0.0.0.
let app = router(storage);
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await?;
axum::serve(listener, app).await?;
Ok(())
}
```
### `jobs-server` binary
The crate also ships a small reference binary:
```bash
# Default: SQLite via $JOBS_DATA_DIR / $JOBS_CONFIG_DIR env vars.
cargo run -p forge-jobs-api --bin jobs-server
# Postgres:
cargo run -p forge-jobs-api --bin jobs-server --features postgres
```
Bind address and worker count are env-configured — see
`src/bin/jobs-server.rs`.
## Routes
| `GET` | `/queue/overview` | All queues' status counts + live workers + retention settings |
| `POST` | `/queue/:name/backoff` | Set per-queue exponential backoff curve |
| `GET` | `/storage/info` | Backend identifier + boot-time facts |
| `GET` | `/metrics` | Prometheus exposition (queue depth, latency percentiles) |
More routes land as the in-process IPC surface formalizes. The
Tauri-plugin's commands and these handlers share the same DTOs, so
divergence is structurally impossible.
## Status
`0.1` — tracks `forge-jobs` major version. The handler bodies are
stable; route shapes may evolve before `1.0` as we tighten the
externally-facing contract.
## License
Dual-licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
<http://opensource.org/licenses/MIT>)
at your option. Contributions intentionally submitted for inclusion
in this crate shall be dual-licensed as above, without any additional
terms or conditions.