SpellChecker instance once at startup, then reuse it across requests using check_async() for non-blocking operation.
FastAPI
Basic Endpoint
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from myspellchecker import SpellChecker
from myspellchecker.core.constants import ValidationLevel
app = FastAPI(title="Myanmar Spell Checker API")
checker = SpellChecker() # Create once, reuse
class CheckRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=10000)
level: str = Field(default="syllable")
class ErrorInfo(BaseModel):
text: str
position: int
error_type: str
suggestions: list[str]
confidence: float
class CheckResponse(BaseModel):
text: str
has_errors: bool
corrected_text: str
errors: list[ErrorInfo]
@app.post("/check", response_model=CheckResponse)
async def check_spelling(request: CheckRequest):
level = ValidationLevel(request.level)
result = await checker.check_async(request.text, level=level)
return CheckResponse(
text=result.text,
has_errors=result.has_errors,
corrected_text=result.corrected_text,
errors=[
ErrorInfo(
text=e.text,
position=e.position,
error_type=e.error_type,
suggestions=e.suggestions[:5],
confidence=e.confidence,
)
for e in result.errors
],
)
Testing with curl
# Check single text
curl -X POST http://localhost:8000/check \
-H "Content-Type: application/json" \
-d '{"text": "မြန်မာနိုင်ငံသည်", "level": "syllable"}'
# Batch check
curl -X POST http://localhost:8000/check/batch \
-H "Content-Type: application/json" \
-d '{"texts": ["မြန်မာ", "နိုင်ငံ"], "level": "syllable"}'
# Health check
curl http://localhost:8000/health
Batch Endpoint
class BatchRequest(BaseModel):
texts: list[str]
@app.post("/check/batch")
async def check_batch(request: BatchRequest):
results = await checker.check_batch_async(request.texts)
return {
"results": [
{"text": r.text, "has_errors": r.has_errors, "error_count": len(r.errors)}
for r in results
],
"summary": {
"total_texts": len(results),
"texts_with_errors": sum(1 for r in results if r.has_errors),
},
}
With Lifespan Management
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.checker = SpellChecker()
yield
app = FastAPI(lifespan=lifespan)
@app.post("/check")
async def check_spelling(request: Request, body: CheckRequest):
checker = request.app.state.checker
result = await checker.check_async(body.text)
return {"has_errors": result.has_errors, "errors": [e.to_dict() for e in result.errors]}
CORS Configuration
from fastapi.middleware.cors import CORSMiddleware
import os
allowed_origins_env = os.getenv("ALLOWED_ORIGINS")
if allowed_origins_env:
origins = [o.strip() for o in allowed_origins_env.split(",")]
allow_credentials = True
else:
origins = ["*"]
allow_credentials = False
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=allow_credentials,
allow_methods=["*"],
allow_headers=["*"],
)
Production Deployment
# Development with auto-reload
uvicorn app:app --reload --port 8000
# Production with Gunicorn
gunicorn app:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000
WebSocket
FastAPI WebSocket
from fastapi import FastAPI, WebSocket
import json
app = FastAPI()
checker = SpellChecker()
@app.websocket("/ws/check")
async def websocket_check(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
message = json.loads(data)
result = await checker.check_async(message["text"])
await websocket.send_json({
"has_errors": result.has_errors,
"errors": [
{"text": e.text, "suggestions": e.suggestions[:3]}
for e in result.errors
],
})
except Exception:
pass
Common Patterns
Error Handling
from myspellchecker.core.exceptions import MyanmarSpellcheckError
@app.post("/check")
async def check_spelling(request: CheckRequest):
try:
result = await checker.check_async(request.text)
return {"status": "success", "result": result.to_dict()}
except MyanmarSpellcheckError as e:
return {"status": "error", "message": str(e)}, 500
except Exception:
return {
"status": "degraded",
"message": "Spell check unavailable",
"original_text": request.text,
}
Health Check
@app.get("/health")
async def health_check():
try:
result = checker.check("မြန်မာ")
return {"status": "healthy", "checker": "ok"}
except Exception as e:
return {"status": "unhealthy", "error": str(e)}, 500
Redis Caching
import redis
import hashlib
import json
redis_client = redis.Redis()
def cached_check(text: str, ttl: int = 3600) -> dict:
key = f"spell:{hashlib.md5(text.encode()).hexdigest()}"
cached = redis_client.get(key)
if cached:
return json.loads(cached)
result = checker.check(text)
output = {
"has_errors": result.has_errors,
"errors": [{"text": e.text} for e in result.errors],
}
redis_client.setex(key, ttl, json.dumps(output))
return output
See Also
- Async API -
check_async()andcheck_batch_async()details - Batch Processing - Efficient batch checking
- Docker Guide - Containerized deployment
- Performance Tuning - Optimization strategies