Plug-in recommendation engine router for FastAPI
Project description
SearchRec
A recommendation engine that mounts directly into any FastAPI app. Tracks click sessions, learns what gets browsed together, and serves "you may also like" recommendations with an admin dashboard to visualise the model.
Install into any FastAPI app in under a minute.
Install
pip install searchrec
Integration
from fastapi import FastAPI
from searchrec import create_router, SearchRec, searchrec_lifespan
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
async with searchrec_lifespan("./rec.db"):
yield
app = FastAPI(lifespan=lifespan)
app.include_router(
create_router(db_path="./rec.db", frontend_prefix="/rec"),
prefix="/rec",
)
Then push your product catalogue once (e.g. at startup or via a management command):
rec = SearchRec(db_path="./rec.db")
await rec.sync_products([
{"item_id": "shirt_001", "name": "Blue Tee", "category": "Clothing", "price": 29.99},
{"item_id": "shoe_001", "name": "Runner X", "category": "Shoes", "price": 89.99},
# ...
])
After users have browsed the store, train the model:
POST /rec/api/retrain
Then recommendations are live:
GET /rec/api/recommend/shirt_001
frontend_prefix must match prefix=
The bundled frontend injects window.REC_BASE via a /config.js endpoint so all API calls resolve correctly when the router is mounted at a sub-path. The value of frontend_prefix must be identical to the prefix= argument passed to include_router.
# Mounted at /rec — both must say "/rec"
app.include_router(create_router(frontend_prefix="/rec"), prefix="/rec")
# Mounted at root — omit both (default is "")
app.include_router(create_router())
Composable lifespan
If your app already has a lifespan, compose with searchrec_lifespan:
from searchrec import searchrec_lifespan
@asynccontextmanager
async def lifespan(app):
async with searchrec_lifespan("./rec.db"):
# your own startup work here
yield
# your own shutdown work here
Alternatively, create_router registers a @router.on_event("startup") handler as a fallback for apps that don't use a lifespan.
How it works
Every time a user clicks items in the same session, those items get a +1 in a co-click matrix. After enough sessions, cosine similarity is computed over the matrix to produce ranked recommendations. Similarity scores are cached so serving is a single database lookup with no ML at request time.
User clicks -> /api/track (batched)
|
click_events table
|
POST /api/retrain (offline)
load sessions -> co-click matrix -> cosine similarity -> cache
|
GET /api/recommend/{item_id} -> DB lookup -> top-N items
Configuration
Pass keyword arguments to create_router, or build a SearchRecConfig object:
from searchrec import create_router, SearchRecConfig
config = SearchRecConfig(
db_path="./rec.db",
min_sessions_to_retrain=10, # minimum sessions before retrain proceeds
min_coclick=2, # minimum co-clicks for a pair to count
top_n=10, # recommendations cached per item
recommend_limit_default=10, # default ?limit= on /api/recommend
recommend_limit_max=50, # maximum ?limit= allowed
serve_frontend=True, # set False to disable bundled UI routes
frontend_prefix="/rec", # must match prefix= in include_router
)
app.include_router(create_router(config=config), prefix="/rec")
API
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /api/track |
Ingest a batch of click events (max 100) |
| POST | /api/retrain |
Run the full recommendation pipeline |
| GET | /api/recommend/{item_id} |
Top similar items for an item |
| PUT/POST | /api/items |
Upsert a batch of products |
| DELETE | /api/items/{item_id} |
Remove a product |
| GET | /api/items/search?q= |
Search the product catalogue |
| GET | /api/items/{item_id} |
Single item detail |
| GET | /api/admin/stats |
Summary stats (clicks, coverage, avg score) |
| GET | /api/admin/clicks/top |
Top items by click count |
| GET | /api/admin/clicks/timeseries |
Daily click volume (last N days) |
| GET | /api/admin/matrix |
Co-click matrix for top N items |
| GET | /api/admin/export |
Download all recommendations as JSON |
| GET | /api/admin/export/csv |
Download all recommendations as CSV |
Bundled UI
When serve_frontend=True (the default), the router also serves:
| Route | Description |
|---|---|
/ |
Store, search products and click to see recommendations |
/admin |
Admin dashboard |
/style.css |
Shared stylesheet |
/tracker.js |
Click event tracker (auto-loaded by the store UI) |
/config.js |
Injects window.REC_BASE for correct API prefixing |
Admin dashboard
- Stats: total clicks, recommendation coverage, avg similarity score, last retrain time
- Click volume: 14-day line chart
- Co-click matrix: heatmap of the top 20 most-clicked items, hover for exact counts
- Top items: horizontal bar chart ranked by click count
- Item inspector: search any product and inspect its recommendations with similarity scores
Stack
| Layer | Tech |
|---|---|
| Backend | Python, FastAPI, aiosqlite |
| ML | numpy, scikit-learn (cosine similarity) |
| Database | SQLite (WAL mode) |
| Frontend | Vanilla JS, HTML/CSS |
| Charts | Chart.js |
Demo app
ecommerce-rec/ contains a standalone demo with a seed script to populate sample products and click sessions:
cd ecommerce-rec/backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python seed.py
uvicorn main:app --reload
Roadmap
- Phase 2: Word2Vec embeddings on click sequences + FAISS approximate nearest-neighbour search
- Nightly automated retrain (cron)
- User authentication on the admin dashboard
- Native integrations for Next.js, Express, and NestJS
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file searchrec-0.1.1.tar.gz.
File metadata
- Download URL: searchrec-0.1.1.tar.gz
- Upload date:
- Size: 29.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f12768e05446dccee61ace74cffca6cefb9225e25f9a5ea934e89f70deeadcd4
|
|
| MD5 |
f6cfb02af7d7cca4d3d14b8b74933234
|
|
| BLAKE2b-256 |
5d6ca4accfc9f10a40775be69ec6129531b52b7768b7abb3dfc1dbf25c12ade0
|
File details
Details for the file searchrec-0.1.1-py3-none-any.whl.
File metadata
- Download URL: searchrec-0.1.1-py3-none-any.whl
- Upload date:
- Size: 30.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35910643d6bfafea19daa5ea2cbe9f63feeda8e13d63a44e19292f3160d38b5f
|
|
| MD5 |
93eaaeb7bdc5e1ee42a27687cc65bf9c
|
|
| BLAKE2b-256 |
24a79b573c7198e49c479a2172aa09cf0b4ab816895cdf18ec29f595ca5bf8b3
|