Skip to main content
mySpellChecker integrates with any Python web framework. The key pattern: create a SpellChecker instance once at startup, then reuse it across requests. Use check_async() for async frameworks and check() for sync ones.

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": [...]}

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

Flask

Basic Setup

from flask import Flask, request, jsonify
from myspellchecker import SpellChecker

app = Flask(__name__)
checker = SpellChecker()

@app.route("/check", methods=["POST"])
def check_spelling():
    data = request.get_json()
    text = data.get("text", "")

    result = checker.check(text)

    return jsonify({
        "has_errors": result.has_errors,
        "errors": [
            {
                "text": e.text,
                "position": e.position,
                "suggestions": e.suggestions[:5],
            }
            for e in result.errors
        ],
    })

if __name__ == "__main__":
    app.run()

With Application Factory

from flask import Flask

def create_app():
    app = Flask(__name__)
    checker = SpellChecker()
    app.extensions["spellchecker"] = checker

    from . import routes
    app.register_blueprint(routes.bp)
    return app

# routes.py
from flask import Blueprint, current_app, request, jsonify

bp = Blueprint("spell", __name__)

@bp.route("/check", methods=["POST"])
def check():
    checker = current_app.extensions["spellchecker"]
    result = checker.check(request.json["text"])
    return jsonify({"has_errors": result.has_errors})

Django

As a Service

# services/spellcheck.py
from myspellchecker import SpellChecker

class SpellCheckService:
    _instance = None

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = SpellChecker()
        return cls._instance

    @classmethod
    def check(cls, text: str):
        return cls.get_instance().check(text)

View Example

# views.py
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
import json

from .services.spellcheck import SpellCheckService

@csrf_exempt
@require_POST
def check_spelling(request):
    data = json.loads(request.body)
    result = SpellCheckService.check(data.get("text", ""))

    return JsonResponse({
        "has_errors": result.has_errors,
        "errors": [
            {"text": e.text, "suggestions": e.suggestions[:5]}
            for e in result.errors
        ],
    })

Django REST Framework

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

class SpellCheckRequestSerializer(serializers.Serializer):
    text = serializers.CharField()

class SpellCheckView(APIView):
    def post(self, request):
        serializer = SpellCheckRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        result = SpellCheckService.check(serializer.validated_data["text"])

        return Response({
            "has_errors": result.has_errors,
            "errors": [
                {"text": e.text, "position": e.position, "suggestions": e.suggestions[:5]}
                for e in result.errors
            ],
        })

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

Celery

Task Definition

# tasks.py
from celery import Celery
from myspellchecker import SpellChecker

app = Celery("spellcheck")
checker = None

@app.task
def check_text(text: str) -> dict:
    global checker
    if checker is None:
        checker = SpellChecker()

    result = checker.check(text)
    return {
        "has_errors": result.has_errors,
        "error_count": len(result.errors),
        "errors": [
            {"text": e.text, "suggestions": e.suggestions[:5]}
            for e in result.errors
        ],
    }

# Usage
result = check_text.delay("မြန်မာနိုင်ငံ")
output = result.get()

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": {...}}
    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