celery-fastapi 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celery_fastapi-0.1.6/PKG-INFO +513 -0
- celery_fastapi-0.1.6/README.md +451 -0
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/celery_fastapi/__init__.py +13 -3
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/celery_fastapi/app.py +32 -1
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/celery_fastapi/cli.py +235 -10
- celery_fastapi-0.1.6/celery_fastapi/core.py +2045 -0
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/pyproject.toml +18 -1
- celery_fastapi-0.1.4/PKG-INFO +0 -459
- celery_fastapi-0.1.4/README.md +0 -401
- celery_fastapi-0.1.4/celery_fastapi/core.py +0 -1193
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/LICENSE +0 -0
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/celery_fastapi/py.typed +0 -0
- {celery_fastapi-0.1.4 → celery_fastapi-0.1.6}/celery_fastapi/server.py +0 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: celery-fastapi
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: Automatic REST API generation for Celery tasks with FastAPI
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: celery,fastapi,rest,api,tasks,async,queue
|
|
8
|
+
Author: ilkerkara
|
|
9
|
+
Author-email: ilkerkara@outlook.com.tr
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.16
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
25
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Provides-Extra: cli
|
|
29
|
+
Provides-Extra: eventlet
|
|
30
|
+
Provides-Extra: gevent
|
|
31
|
+
Provides-Extra: gunicorn
|
|
32
|
+
Provides-Extra: multipart
|
|
33
|
+
Provides-Extra: orjson
|
|
34
|
+
Provides-Extra: otel
|
|
35
|
+
Provides-Extra: rabbitmq
|
|
36
|
+
Provides-Extra: redis
|
|
37
|
+
Provides-Extra: server
|
|
38
|
+
Provides-Extra: standard
|
|
39
|
+
Provides-Extra: ujson
|
|
40
|
+
Provides-Extra: uvicorn
|
|
41
|
+
Requires-Dist: celery (>=5.3.0,<=5.6.3)
|
|
42
|
+
Requires-Dist: eventlet (>=0.33.0) ; extra == "eventlet"
|
|
43
|
+
Requires-Dist: fastapi (>=0.100.0)
|
|
44
|
+
Requires-Dist: gevent (>=23.0.0) ; extra == "gevent"
|
|
45
|
+
Requires-Dist: gunicorn (>=21.0.0) ; extra == "gunicorn" or extra == "all"
|
|
46
|
+
Requires-Dist: httpx (>=0.27.0) ; extra == "all"
|
|
47
|
+
Requires-Dist: kombu (>=5.3.0) ; extra == "rabbitmq" or extra == "all"
|
|
48
|
+
Requires-Dist: opentelemetry-api (>=1.20.0) ; extra == "otel" or extra == "all"
|
|
49
|
+
Requires-Dist: orjson (>=3.9.0) ; extra == "orjson" or extra == "all"
|
|
50
|
+
Requires-Dist: pydantic (>=2.0.0)
|
|
51
|
+
Requires-Dist: python-multipart (>=0.0.6) ; extra == "multipart" or extra == "all"
|
|
52
|
+
Requires-Dist: redis (>=5.0.0) ; extra == "redis" or extra == "standard" or extra == "all"
|
|
53
|
+
Requires-Dist: rich (>=13.0.0) ; extra == "cli" or extra == "standard" or extra == "all"
|
|
54
|
+
Requires-Dist: typer (>=0.9.0) ; extra == "cli" or extra == "standard" or extra == "all"
|
|
55
|
+
Requires-Dist: ujson (>=5.8.0) ; extra == "ujson"
|
|
56
|
+
Requires-Dist: uvicorn[standard] (>=0.23.0) ; extra == "uvicorn" or extra == "gunicorn" or extra == "server" or extra == "cli" or extra == "standard" or extra == "all"
|
|
57
|
+
Project-URL: Documentation, https://github.com/karailker/celery-fastapi#readme
|
|
58
|
+
Project-URL: Homepage, https://github.com/karailker/celery-fastapi
|
|
59
|
+
Project-URL: Repository, https://github.com/karailker/celery-fastapi
|
|
60
|
+
Description-Content-Type: text/markdown
|
|
61
|
+
|
|
62
|
+
# Celery FastAPI
|
|
63
|
+
|
|
64
|
+
[](https://github.com/karailker/celery-fastapi/actions/workflows/ci.yml)
|
|
65
|
+
[](https://badge.fury.io/py/celery-fastapi)
|
|
66
|
+
[](https://pypi.org/project/celery-fastapi/)
|
|
67
|
+
[](https://opensource.org/licenses/MIT)
|
|
68
|
+
[](https://github.com/karailker/celery-fastapi/stargazers)
|
|
69
|
+
|
|
70
|
+
Automatic REST API generation for Celery tasks with FastAPI. This package seamlessly bridges Celery and FastAPI, automatically creating REST endpoints for all your registered Celery tasks.
|
|
71
|
+
|
|
72
|
+
## Why celery-fastapi?
|
|
73
|
+
|
|
74
|
+
Without it, exposing Celery to other services means hand-writing a FastAPI
|
|
75
|
+
route, a request model and a status route for every task, then keeping them in
|
|
76
|
+
sync with the task signatures. celery-fastapi derives all of that from your
|
|
77
|
+
Celery app:
|
|
78
|
+
|
|
79
|
+
| | Hand-written routes | celery-fastapi |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| New task becomes an endpoint | write route + model | automatic |
|
|
82
|
+
| Request validation / OpenAPI | per route | from the task signature and type hints |
|
|
83
|
+
| Status, revoke, workers, health | write yourself | built in |
|
|
84
|
+
| Auth, rate limit, idempotency, metrics | write yourself | one parameter each |
|
|
85
|
+
| Typed clients (TypeScript, Python, ...) | write the schema | `celery-fastapi openapi` |
|
|
86
|
+
|
|
87
|
+
It is a thin bridge, not a new task queue: workers, brokers and results stay
|
|
88
|
+
plain Celery.
|
|
89
|
+
|
|
90
|
+
## Features
|
|
91
|
+
|
|
92
|
+
- 🚀 **Automatic endpoint generation** — REST APIs created automatically for all Celery tasks
|
|
93
|
+
- 🔧 **Zero configuration** — Works out of the box with sensible defaults
|
|
94
|
+
- 📊 **Task monitoring** — Built-in endpoints for task status, revocation, and worker info
|
|
95
|
+
- 🎯 **App-scoped operations** — Only manages tasks from your specific Celery app, not the entire cluster
|
|
96
|
+
- 🖥️ **CLI support** — Run as a standalone server from command line
|
|
97
|
+
- 📦 **Modular design** — Use as a library or standalone application
|
|
98
|
+
- 🔄 **Queue-aware routing** — Respects Celery queue assignments
|
|
99
|
+
- 📝 **OpenAPI documentation** — Full Swagger/ReDoc support
|
|
100
|
+
- ⚡ **Full Celery options** — All task options (countdown, eta, priority, etc.)
|
|
101
|
+
- 🧮 **Batch execution** — Submit groups of tasks in a single request via `/tasks/batch`
|
|
102
|
+
- 🔗 **Workflow primitives** — Chain and chord orchestration via `/tasks/chain` and `/tasks/chord`
|
|
103
|
+
- 🛡️ **Input validation** — Pydantic-driven validation on `task_name`/`queue` at trust boundary
|
|
104
|
+
- 📦 **Pydantic task params** — Tasks annotated with `BaseModel` subclasses get first-class payload models and OpenAPI schemas
|
|
105
|
+
- 🪝 **Bridge hooks** — `pre_hooks`/`post_hooks` run around task dispatch (auth, audit, notify)
|
|
106
|
+
- 🗺️ **Discovery & mapping** — Hide tasks with `exclude` and rename public names with `name_mapping`
|
|
107
|
+
- 🔀 **Custom error mapping** — Map exception types to HTTP status codes via `error_mapping`
|
|
108
|
+
- 🧩 **Middleware & dependencies** — Inject FastAPI middleware and `Depends` providers
|
|
109
|
+
- 💾 **Pluggable rate limiting** — In-memory by default; bring your own store via `BaseRateLimitStorage`
|
|
110
|
+
- 🔌 **WebSocket streaming** — Live task status updates via `/tasks/{task_id}/ws`
|
|
111
|
+
- 📡 **Server-Sent Events** — Proxy-friendly status stream via `/tasks/{task_id}/events`
|
|
112
|
+
- 🔐 **Secure by default** — Destructive endpoints are opt-in; one `api_key` protects the rest
|
|
113
|
+
- ♻️ **Idempotency keys** — Retries with the same `Idempotency-Key` never double-submit
|
|
114
|
+
- 📈 **Prometheus metrics & OpenTelemetry** — Opt-in `/metrics` and dispatch spans
|
|
115
|
+
- 🗓️ **Beat schedule listing** — Read-only `/schedules`
|
|
116
|
+
- 🤖 **MCP server (experimental)** — Expose tasks as tools for AI agents
|
|
117
|
+
- 🧾 **OpenAPI export** — `celery-fastapi openapi` for client generation in CI
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
- Python 3.11 – 3.16 (3.11–3.14 tested in CI; 3.15 (release candidate) and 3.16 run as non-blocking jobs once available)
|
|
122
|
+
- FastAPI 0.100.0+
|
|
123
|
+
- Celery 5.3.0+ (tested up to 5.6.3)
|
|
124
|
+
|
|
125
|
+
## Installation
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Basic installation
|
|
129
|
+
pip install celery-fastapi
|
|
130
|
+
|
|
131
|
+
# With CLI support
|
|
132
|
+
pip install celery-fastapi[cli]
|
|
133
|
+
|
|
134
|
+
# With uvicorn server
|
|
135
|
+
pip install celery-fastapi[server]
|
|
136
|
+
|
|
137
|
+
# With gunicorn for production
|
|
138
|
+
pip install celery-fastapi[gunicorn]
|
|
139
|
+
|
|
140
|
+
# With Redis broker
|
|
141
|
+
pip install celery-fastapi[redis]
|
|
142
|
+
|
|
143
|
+
# With RabbitMQ broker
|
|
144
|
+
pip install celery-fastapi[rabbitmq]
|
|
145
|
+
|
|
146
|
+
# All extras (recommended for production)
|
|
147
|
+
pip install celery-fastapi[all]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Or with Poetry:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
poetry add celery-fastapi
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Quick Start
|
|
157
|
+
|
|
158
|
+
### As a Python Module
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from celery import Celery
|
|
162
|
+
from celery_fastapi import CeleryFastAPIBridge, create_app
|
|
163
|
+
|
|
164
|
+
celery_app = Celery("tasks", broker="redis://localhost:6379/0")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@celery_app.task
|
|
168
|
+
def add(x, y):
|
|
169
|
+
return x + y
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Option 1: Using create_app factory
|
|
173
|
+
app = create_app(celery_app)
|
|
174
|
+
|
|
175
|
+
# Option 2: Using the Bridge class for more control
|
|
176
|
+
from fastapi import FastAPI
|
|
177
|
+
|
|
178
|
+
fastapi_app = FastAPI(title="My Task API")
|
|
179
|
+
bridge = CeleryFastAPIBridge(celery_app, fastapi_app)
|
|
180
|
+
bridge.register_routes()
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Run with uvicorn:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
uvicorn myapp:app --reload
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Try it in five minutes
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
cd examples/quickstart && docker compose up --build # then open http://localhost:8000/docs
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
See [examples/quickstart](examples/quickstart) and, for Kubernetes health probes,
|
|
196
|
+
[examples/k8s-sidecar.yaml](examples/k8s-sidecar.yaml).
|
|
197
|
+
|
|
198
|
+
### Using the CLI
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
celery-fastapi serve examples.celery_app:celery_app --port 8000 --reload
|
|
202
|
+
celery-fastapi serve examples.celery_app:celery_app -w 4 --host 0.0.0.0
|
|
203
|
+
celery-fastapi routes examples.celery_app:celery_app
|
|
204
|
+
celery-fastapi tasks examples.celery_app:celery_app
|
|
205
|
+
celery-fastapi workers examples.celery_app:celery_app
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Security
|
|
209
|
+
|
|
210
|
+
celery-fastapi is **secure by default**, because an HTTP API in front of a task
|
|
211
|
+
queue is a powerful thing to expose:
|
|
212
|
+
|
|
213
|
+
- **Admin endpoints are off.** `POST /purge`, `POST /trigger` (runs *any* task
|
|
214
|
+
by name), `DELETE /tasks/{id}` and `POST /tasks/batch/revoke` only exist when
|
|
215
|
+
you pass `enable_admin_endpoints=True` (`--enable-admin` on the CLI).
|
|
216
|
+
- **One key protects everything.** `api_key="..."` (`CELERY_FASTAPI_API_KEY`)
|
|
217
|
+
requires the `X-API-Key` header (configurable) on every endpoint except the
|
|
218
|
+
`/healthz` and `/ping` probes. WebSockets also accept `?api_key=`. Keys are
|
|
219
|
+
compared in constant time.
|
|
220
|
+
- **Your own auth still works.** `dependencies=[...]` are applied to every HTTP
|
|
221
|
+
endpoint except the probes (use them for OAuth/JWT).
|
|
222
|
+
- **Only your tasks.** Per-task, batch, chain and chord endpoints refuse task
|
|
223
|
+
names outside the app; task and queue names are validated.
|
|
224
|
+
- **Rate limiting** covers every endpoint that submits work.
|
|
225
|
+
|
|
226
|
+
Always run it behind TLS, and prefer environment variables over command-line
|
|
227
|
+
arguments for secrets. See [SECURITY.md](SECURITY.md) for reporting issues.
|
|
228
|
+
|
|
229
|
+
> **Upgrading from 0.1.5:** if you call `/purge`, `/trigger` or the revoke
|
|
230
|
+
> endpoints, add `enable_admin_endpoints=True`. Everything else keeps working.
|
|
231
|
+
|
|
232
|
+
## API Endpoints
|
|
233
|
+
|
|
234
|
+
All endpoints are prefixed with the configured `prefix` (empty by default).
|
|
235
|
+
|
|
236
|
+
### Task Execution
|
|
237
|
+
|
|
238
|
+
`POST /{task_name_with_slashes}` — Execute a task.
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"args": [1, 2],
|
|
243
|
+
"kwargs": {},
|
|
244
|
+
"countdown": 60,
|
|
245
|
+
"priority": 5,
|
|
246
|
+
"queue": "high_priority"
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"task_id": "abc123-def456-...",
|
|
253
|
+
"status": "PENDING"
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Send an `Idempotency-Key` header to make retries safe: the same key with the
|
|
258
|
+
same body returns the original `task_id` (response header
|
|
259
|
+
`Idempotent-Replayed: true`); the same key with a different body returns `409`.
|
|
260
|
+
Keys are remembered in memory for `idempotency_ttl` seconds (24h by default),
|
|
261
|
+
per process.
|
|
262
|
+
|
|
263
|
+
`POST /trigger` *(admin)* — Trigger any task by name (`queue` required here).
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"task_name": "myapp.add",
|
|
268
|
+
"queue": "celery",
|
|
269
|
+
"args": [1, 2]
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### Pydantic task parameters
|
|
274
|
+
|
|
275
|
+
When a task is annotated with a `pydantic.BaseModel` subclass, the generated
|
|
276
|
+
payload model uses that model directly, so nested schemas appear in OpenAPI:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
class Item(BaseModel):
|
|
280
|
+
name: str
|
|
281
|
+
qty: int = 1
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@celery_app.task(name="myapp.process")
|
|
285
|
+
def process(payload: Item) -> dict:
|
|
286
|
+
return payload.model_dump()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{"payload": {"name": "widget", "qty": 3}}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Workflow Primitives
|
|
294
|
+
|
|
295
|
+
`POST /tasks/chain` — Run tasks sequentially, passing results forward.
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"tasks": [
|
|
300
|
+
{"task_name": "myapp.add", "args": [1, 2]},
|
|
301
|
+
{"task_name": "myapp.add", "args": [3, 4]}
|
|
302
|
+
]
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`POST /tasks/chord` — Run a header group, then a callback once all complete.
|
|
307
|
+
|
|
308
|
+
```json
|
|
309
|
+
{
|
|
310
|
+
"header": [
|
|
311
|
+
{"task_name": "myapp.add", "args": [1, 2]},
|
|
312
|
+
{"task_name": "myapp.add", "args": [3, 4]}
|
|
313
|
+
],
|
|
314
|
+
"callback": "myapp.greet"
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Batch Execution
|
|
319
|
+
|
|
320
|
+
`POST /tasks/batch` — Submit a group of tasks.
|
|
321
|
+
|
|
322
|
+
`POST /tasks/batch/revoke` *(admin)* — Revoke tasks by list of IDs.
|
|
323
|
+
|
|
324
|
+
### Task Status
|
|
325
|
+
|
|
326
|
+
- `GET /tasks/{task_id}` — Full task status (state, result, traceback, date_done).
|
|
327
|
+
- `GET /tasks/{task_id}/result` — Task result only.
|
|
328
|
+
- `GET /tasks` — List active, scheduled, reserved, revoked tasks (filtered to this
|
|
329
|
+
app). Query: `worker`, `name` (substring), `limit` and `offset` (per worker list).
|
|
330
|
+
- `GET /tasks/{task_id}/events` — Server-Sent Events status stream.
|
|
331
|
+
- `DELETE /tasks/{task_id}` *(admin)* — Revoke a single task.
|
|
332
|
+
|
|
333
|
+
A task that is unknown or has not started reports `state: "PENDING"` (HTTP 200),
|
|
334
|
+
which matches Celery; `GET /tasks/{id}/result` answers `202` until it finishes.
|
|
335
|
+
|
|
336
|
+
### Discovery & Management
|
|
337
|
+
|
|
338
|
+
- `GET /available-tasks` — List tasks registered in THIS app (respects `name_mapping`/`exclude`).
|
|
339
|
+
- `GET /workers` — List active workers, filtered to this app's tasks (includes `active_queues`).
|
|
340
|
+
- `GET /schedules` — Celery Beat entries (`beat_schedule`) for exposed tasks. Read-only:
|
|
341
|
+
enabling/disabling entries depends on your scheduler and is not managed here.
|
|
342
|
+
- `POST /purge` *(admin)* — Purge all pending tasks.
|
|
343
|
+
|
|
344
|
+
### Health Check
|
|
345
|
+
|
|
346
|
+
- `GET /healthz` — Health check for local Celery worker.
|
|
347
|
+
- `GET /ping` — Ping local Celery worker.
|
|
348
|
+
|
|
349
|
+
### Observability
|
|
350
|
+
|
|
351
|
+
- `GET /metrics` *(opt-in: `enable_metrics=True`)* — Prometheus text format:
|
|
352
|
+
`celery_fastapi_tasks_submitted_total{task}`, `..._dispatch_errors_total{task}`,
|
|
353
|
+
`..._rate_limited_total`, `..._broker_up`, `..._workers_online`.
|
|
354
|
+
- **OpenTelemetry** *(opt-in: `enable_tracing=True`, `pip install celery-fastapi[otel]`)* —
|
|
355
|
+
each dispatch runs in a `celery_fastapi.send_task` span with `celery.task_name`
|
|
356
|
+
and `celery.queue` attributes, using whatever tracer provider your app configured.
|
|
357
|
+
|
|
358
|
+
### MCP (experimental)
|
|
359
|
+
|
|
360
|
+
`enable_mcp=True` adds `POST /mcp`, a minimal Model Context Protocol endpoint
|
|
361
|
+
(JSON-RPC over HTTP: `initialize`, `tools/list`, `tools/call`). Every exposed
|
|
362
|
+
task becomes a tool whose input schema comes from the task signature, plus a
|
|
363
|
+
`get_task_status` tool. It honours `api_key`, `dependencies`, hooks and the rate
|
|
364
|
+
limit. Streaming/SSE transports and resources/prompts are not implemented.
|
|
365
|
+
|
|
366
|
+
### Typed clients
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
celery-fastapi openapi myapp.celery:app -o openapi.json
|
|
370
|
+
npx @openapitools/openapi-generator-cli generate -i openapi.json -g typescript-fetch -o client
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### WebSocket Streaming
|
|
374
|
+
|
|
375
|
+
`WS /tasks/{task_id}/ws` — Stream task status updates as JSON frames.
|
|
376
|
+
|
|
377
|
+
## Configuration
|
|
378
|
+
|
|
379
|
+
### CeleryFastAPIBridge Options
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
from celery_fastapi import (
|
|
383
|
+
CeleryFastAPIBridge,
|
|
384
|
+
BaseRateLimitStorage,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
bridge = CeleryFastAPIBridge(
|
|
388
|
+
celery_app=celery_app,
|
|
389
|
+
fastapi_app=fastapi_app, # Optional
|
|
390
|
+
prefix="/api/v1", # URL prefix
|
|
391
|
+
include_status_endpoints=True,
|
|
392
|
+
task_filter=lambda name: not name.startswith("internal."),
|
|
393
|
+
rate_limit=100, # req/min per client
|
|
394
|
+
rate_limit_storage=None, # BaseRateLimitStorage instance
|
|
395
|
+
enable_admin_endpoints=False, # /purge, /trigger, revoke
|
|
396
|
+
api_key=None, # require X-API-Key everywhere except /healthz, /ping
|
|
397
|
+
enable_metrics=False, # /metrics (Prometheus)
|
|
398
|
+
enable_mcp=False, # /mcp (experimental)
|
|
399
|
+
enable_tracing=False, # OpenTelemetry spans
|
|
400
|
+
idempotency_ttl=86400, # seconds an Idempotency-Key is remembered
|
|
401
|
+
exclude={"internal.secret"}, # Hide from API
|
|
402
|
+
name_mapping={"my.add": "public_add"}, # Rename in listings
|
|
403
|
+
middleware=[my_http_middleware],
|
|
404
|
+
dependencies=[auth_provider],
|
|
405
|
+
pre_hooks=[audit_hook], # receive payload
|
|
406
|
+
post_hooks=[notify_hook], # receive response
|
|
407
|
+
error_mapping={ValueError: 422},
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Pluggable Rate Limit Storage
|
|
412
|
+
|
|
413
|
+
Rate limiting is backed by `BaseRateLimitStorage`. The default is in-memory
|
|
414
|
+
(single process). For several workers use the bundled Redis store, or implement
|
|
415
|
+
the ABC for another shared store:
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
from redis import Redis
|
|
419
|
+
from celery_fastapi import CeleryFastAPIBridge, RedisRateLimitStorage
|
|
420
|
+
|
|
421
|
+
bridge = CeleryFastAPIBridge(
|
|
422
|
+
celery_app,
|
|
423
|
+
rate_limit=100,
|
|
424
|
+
rate_limit_storage=RedisRateLimitStorage(Redis()),
|
|
425
|
+
)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
`pip install celery-fastapi[redis]` provides the `redis` package.
|
|
429
|
+
|
|
430
|
+
## Integration with Existing FastAPI App
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
from fastapi import FastAPI
|
|
434
|
+
from celery_fastapi import CeleryFastAPIBridge
|
|
435
|
+
|
|
436
|
+
app = FastAPI()
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@app.get("/health")
|
|
440
|
+
def health_check():
|
|
441
|
+
return {"status": "healthy"}
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
bridge = CeleryFastAPIBridge(celery_app, app, prefix="/celery")
|
|
445
|
+
bridge.register_routes()
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## CLI Reference
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
celery-fastapi serve Start the server (uvicorn)
|
|
452
|
+
celery-fastapi serve-gunicorn Start with Gunicorn
|
|
453
|
+
celery-fastapi openapi Print the OpenAPI schema (no server)
|
|
454
|
+
celery-fastapi routes List all generated routes
|
|
455
|
+
celery-fastapi tasks List registered Celery tasks
|
|
456
|
+
celery-fastapi workers Show active workers
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
`serve`, `serve-gunicorn`, `routes` and `openapi` accept `--enable-admin`,
|
|
460
|
+
`--api-key` (or `CELERY_FASTAPI_API_KEY`), `--metrics` and `--mcp`.
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
celery-fastapi serve examples.celery_app:celery_app \
|
|
464
|
+
--host 0.0.0.0 --port 8000 --reload --workers 4 \
|
|
465
|
+
--log-level info --ssl-keyfile key.pem --ssl-certfile cert.pem
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Development
|
|
469
|
+
|
|
470
|
+
```bash
|
|
471
|
+
git clone https://github.com/karailker/celery-fastapi.git
|
|
472
|
+
cd celery-fastapi
|
|
473
|
+
poetry install --extras all
|
|
474
|
+
poetry run pytest
|
|
475
|
+
poetry run ruff check .
|
|
476
|
+
poetry run mypy celery_fastapi
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Integration test suite (broker/backend matrix)
|
|
480
|
+
|
|
481
|
+
The repo ships a `docker-compose.yml` that brings up every broker/backend the
|
|
482
|
+
test matrix exercises:
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
docker compose up -d # redis, rabbitmq, postgres, mysql, memcached, mongodb
|
|
486
|
+
poetry run pytest tests/test_integration_broker_backend.py
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Then start one worker for the end-to-end test and run it:
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
poetry run celery -A tests.broker_workers:live_app worker --loglevel=info
|
|
493
|
+
poetry run pytest tests/test_integration_broker_backend.py::test_live_redis_broker_backend
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
The matrix covers all stable Celery brokers (Redis, RabbitMQ) crossed with nine
|
|
497
|
+
result backends (redis, rpc, cache+memory, cache+memcached, db+sqlite,
|
|
498
|
+
db+postgresql, db+mysql, mongodb, filesystem) — 18 combinations, each verified
|
|
499
|
+
for bridge construction, broker dispatch, and result-backend round-trip. The
|
|
500
|
+
`rpc` backend skips the round-trip layer (it needs a live reply consumer); the
|
|
501
|
+
single live worker test uses Redis end to end. Missing drivers skip individually.
|
|
502
|
+
|
|
503
|
+
## Contributing & project
|
|
504
|
+
|
|
505
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, checks, PR conventions
|
|
506
|
+
- [SECURITY.md](SECURITY.md) — how to report a vulnerability
|
|
507
|
+
- [ROADMAP.md](ROADMAP.md) — what is shipped and what is next
|
|
508
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
|
509
|
+
|
|
510
|
+
## License
|
|
511
|
+
|
|
512
|
+
MIT License — see [LICENSE](LICENSE).
|
|
513
|
+
|