API Design — ChemLib¶
Overview¶
The API is built on FastAPI with automatic OpenAPI documentation. All endpoints are under /api/ and return JSON. The UI interacts with the system exclusively through these endpoints.
Base URL: http://localhost:8000
- API docs: http://localhost:8000/docs (Swagger UI)
- ReDoc: http://localhost:8000/redoc
- UI pages: http://localhost:8000/ (Jinja2 templates)
Click diagram to zoom and pan:
Endpoint Summary¶
| Method | Path | Description | Service |
|---|---|---|---|
| Compounds | |||
| POST | /api/compounds/ |
Create compound from SMILES | CompoundService |
| POST | /api/compounds/upload |
Import compounds from SDF file | CompoundService |
| GET | /api/compounds/ |
List compounds with property filters | CompoundService |
| GET | /api/compounds/{id} |
Get compound detail | CompoundService |
| PUT | /api/compounds/{id} |
Update compound name/metadata | CompoundService |
| DELETE | /api/compounds/{id} |
Delete compound | CompoundService |
| POST | /api/compounds/search/similar |
Similarity search | CompoundService |
| POST | /api/compounds/search/substructure |
Substructure search | CompoundService |
| Fragments | |||
| POST | /api/fragments/decompose/{compound_id} |
Decompose compound into fragments | FragmentService |
| GET | /api/fragments/ |
List fragments with filters | FragmentService |
| GET | /api/fragments/{id} |
Get fragment detail | FragmentService |
| GET | /api/fragments/{id}/compatible |
Get compatible fragments | FragmentService |
| DELETE | /api/fragments/{id} |
Delete fragment | FragmentService |
| Assembly | |||
| POST | /api/assembly/start |
Start new assembly from a fragment | AssemblyService |
| POST | /api/assembly/{id}/add-fragment |
Add fragment to assembly | AssemblyService |
| POST | /api/assembly/{id}/finalize |
Finalize and score molecule | AssemblyService |
| GET | /api/assembly/{id} |
Get assembly state and history | AssemblyService |
| GET | /api/assembly/ |
List all assembled molecules | AssemblyService |
| DELETE | /api/assembly/{id} |
Delete assembled molecule | AssemblyService |
| Visualization | |||
| GET | /api/viz/{parent_type}/{parent_id}/3d |
Get 3D MOL block for viewer | ConformerService |
| GET | /api/viz/{parent_type}/{parent_id}/conformers |
List conformers with energies | ConformerService |
| POST | /api/viz/{parent_type}/{parent_id}/generate-3d |
Generate 3D conformers | ConformerService |
| GET | /api/viz/{parent_type}/{parent_id}/2d |
Get 2D depiction (SVG) | VizService |
| Scoring | |||
| GET | /api/scoring/{parent_type}/{parent_id} |
Get full scoring report | ScoringService |
| POST | /api/scoring/evaluate |
Score a SMILES string (no DB) | ScoringService |
Detailed Endpoint Specifications¶
Compounds¶
POST /api/compounds/¶
Create a new compound from a SMILES string.
Request:
Response (201):
{
"id": 1,
"name": "Phenylacetic acid",
"canonical_smiles": "O=C(O)Cc1ccccc1",
"inchi_key": "WLJVXDMOQOGPHL-UHFFFAOYSA-N",
"formula": "C8H8O2",
"mw": 136.15,
"logp": 1.46,
"tpsa": 37.3,
"hbd": 1,
"hba": 2,
"num_rotatable": 2,
"num_rings": 1,
"qed_score": 0.72,
"sa_score": 1.84,
"lipinski_pass": true,
"created_at": "2026-03-21T12:00:00Z"
}
Error (422): Invalid SMILES
Error (409): Duplicate compound
POST /api/compounds/upload¶
Import multiple compounds from an SDF file.
Request: multipart/form-data with file field sdf_file
Response (201):
{
"imported": 42,
"skipped": 3,
"errors": [
{"index": 5, "error": "Invalid structure"},
{"index": 12, "error": "Duplicate (InChIKey: ABC...)"},
{"index": 30, "error": "RDKit sanitization failed"}
]
}
GET /api/compounds/¶
List compounds with property-based filters.
Query Parameters:
- mw_min, mw_max — Molecular weight range
- logp_min, logp_max — LogP range
- hbd_max — Max H-bond donors
- hba_max — Max H-bond acceptors
- lipinski_pass — Boolean filter
- search — Substring search on name
- sort_by — Column name (default: created_at)
- sort_order — asc or desc
- limit — Page size (default: 50, max: 500)
- offset — Pagination offset
Response (200):
POST /api/compounds/search/similar¶
Find compounds similar to a query structure.
Request:
Response (200):
{
"query_smiles": "Oc1ccccc1",
"threshold": 0.7,
"results": [
{
"compound": { /* CompoundResponse */ },
"similarity": 0.85
}
]
}
POST /api/compounds/search/substructure¶
Find compounds containing a substructure (SMARTS or SMILES pattern).
Request:
Fragments¶
POST /api/fragments/decompose/{compound_id}¶
Run BRICS decomposition on a compound and store resulting fragments.
Response (201):
{
"compound_id": 1,
"compound_smiles": "O=C(O)Cc1ccccc1",
"fragments": [
{
"id": 1,
"smiles": "[3*]C(=O)O",
"attachment_points": [3],
"mw": 45.02,
"num_heavy_atoms": 3
},
{
"id": 2,
"smiles": "[16*]c1ccccc1",
"attachment_points": [16],
"mw": 77.08,
"num_heavy_atoms": 6
}
]
}
GET /api/fragments/{id}/compatible¶
Get fragments with attachment points compatible with the given fragment (BRICS rules).
Response (200):
{
"fragment_id": 1,
"fragment_smiles": "[3*]C(=O)O",
"attachment_points": [3],
"compatible_fragments": [
{
"id": 5,
"smiles": "[4*]CCCN",
"attachment_points": [4],
"compatible_at": [3, 4]
}
]
}
Assembly¶
POST /api/assembly/start¶
Begin a new molecule assembly from an initial fragment.
Request:
Response (201):
{
"id": 1,
"name": "My candidate molecule",
"current_smiles": "[3*]C(=O)O",
"steps": [
{
"step_number": 1,
"fragment_id": 1,
"fragment_smiles": "[3*]C(=O)O",
"intermediate_smiles": "[3*]C(=O)O"
}
],
"available_attachment_points": [3]
}
POST /api/assembly/{id}/add-fragment¶
Add a fragment to the growing molecule.
Request:
Response (200):
{
"id": 1,
"current_smiles": "O=C(O)CCCN",
"steps": [ /* updated step list */ ],
"available_attachment_points": [],
"is_valid": true,
"validation_warnings": []
}
POST /api/assembly/{id}/finalize¶
Finalize assembly: compute all properties, scores, and optionally generate 3D conformers.
Request:
Response (200):
{
"id": 1,
"canonical_smiles": "O=C(O)CCCN",
"properties": {
"mw": 103.12,
"logp": -0.67,
"tpsa": 63.32,
"hbd": 3,
"hba": 3,
"formula": "C4H9NO2"
},
"scoring": {
"qed_score": 0.45,
"sa_score": 2.1,
"lipinski_pass": true,
"lipinski_violations": [],
"veber_pass": true,
"pains_pass": true,
"pains_alerts": []
},
"conformers_generated": 50,
"lowest_energy_kcal": -42.3
}
Visualization¶
GET /api/viz/{parent_type}/{parent_id}/3d¶
Get the 3D structure for the molecule viewer.
Path Parameters:
- parent_type: compound or assembled
- parent_id: integer ID
Query Parameters:
- conformer_id — Specific conformer (default: lowest energy)
- format — sdf (default) or mol
Response (200):
{
"mol_block": "...MOL block with 3D coordinates...",
"energy": -42.3,
"force_field": "MMFF94",
"is_minimized": true,
"conformer_id": 5
}
GET /api/viz/{parent_type}/{parent_id}/conformers¶
List all conformers with their energies for the conformer browser.
Response (200):
{
"parent_type": "assembled",
"parent_id": 1,
"conformers": [
{"id": 5, "energy": -42.3, "is_lowest": true, "rmsd": 0.0},
{"id": 3, "energy": -41.8, "is_lowest": false, "rmsd": 1.2},
{"id": 7, "energy": -40.1, "is_lowest": false, "rmsd": 2.5}
]
}
POST /api/viz/{parent_type}/{parent_id}/generate-3d¶
Trigger 3D conformer generation and energy minimization.
Request:
Response (202): Accepted (long-running operation)
{
"status": "generating",
"parent_type": "assembled",
"parent_id": 1,
"num_conformers_requested": 50
}
GET /api/viz/{parent_type}/{parent_id}/2d¶
Get 2D depiction as SVG.
Response (200): SVG image (Content-Type: image/svg+xml)
Scoring¶
GET /api/scoring/{parent_type}/{parent_id}¶
Get full drug-likeness report for a stored molecule.
Response (200):
{
"molecule": "O=C(O)CCCN",
"lipinski": {
"passes": true,
"violations": 0,
"mw": 103.12,
"logp": -0.67,
"hbd": 3,
"hba": 3,
"rules": {
"mw_ok": true,
"logp_ok": true,
"hbd_ok": true,
"hba_ok": true
}
},
"veber": {
"passes": true,
"rotatable_bonds": 3,
"tpsa": 63.32
},
"qed": 0.45,
"sa_score": 2.1,
"pains": {
"passes": true,
"alerts": []
},
"overall_recommendation": "GOOD_CANDIDATE"
}
POST /api/scoring/evaluate¶
Score a SMILES string without storing it. Useful for quick evaluation.
Request:
Response (200): Same format as above.
API Design Principles¶
-
Consistent response format: All list endpoints return
{total, items, limit, offset}. All single-item endpoints return the resource directly. -
HTTP status codes:
200— Success201— Created202— Accepted (async operation started)400— Bad request (invalid assembly, incompatible fragments)404— Not found409— Conflict (duplicate)422— Validation error (invalid SMILES, bad parameters)-
500— Server error -
Pagination: All list endpoints support
limitandoffsetquery parameters. -
Filtering: Property-based filters use query parameters on list endpoints.
-
Error responses: Always include a
detailfield with a human-readable message. -
No business logic in routes: Routes call service methods and return their results.
FastAPI App Structure¶
# chemlib/main.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from chemlib.api import compounds, fragments, assembly, visualization, scoring
app = FastAPI(
title="ChemLib",
description="Chemical Compound Library for Fragment-Based Drug Design",
version="0.1.0",
)
# API routers
app.include_router(compounds.router)
app.include_router(fragments.router)
app.include_router(assembly.router)
app.include_router(visualization.router)
app.include_router(scoring.router)
# Static files and templates for UI
app.mount("/static", StaticFiles(directory="chemlib/static"), name="static")
templates = Jinja2Templates(directory="chemlib/templates")
# UI page routes (serve HTML)
@app.get("/")
async def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
Authentication & Rate Limiting¶
Out of scope for Phase 1. The API runs locally without authentication. Future phases may add: - API key authentication - JWT tokens for user sessions - Rate limiting via SlowAPI
