Skip to main content

Overview

The Template Service manages notification templates for the distributed notification system. Built with FastAPI and Python, it provides a simple, lightweight API for retrieving email and push notification templates stored in a JSON file.

Purpose and Responsibilities

  • Template storage - Store notification templates with subject, body, and variables
  • Template retrieval - Provide templates by code via REST API
  • Version management - Track template versions for auditing
  • Variable definition - Define required variables for each template

Tech Stack

  • Framework: FastAPI (latest)
  • Language: Python 3.9+
  • Server: Uvicorn (ASGI server)
  • Storage: JSON file (templates.json)

Configuration

Port: 8002 (configurable) Environment Variables:
PORT=8002  # Default if not specified
Server Configuration (main.py:42-43):
if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8002, reload=True)

Template Structure

Templates are stored in templates.json with the following structure.

Template Schema

File: templates.json
{
  "welcome_email": {
    "subject": "Welcome to Our Platform!",
    "body": "Hi {{name}},\n\nThanks for signing up. We're glad to have you onboard!",
    "version": "2.0.0",
    "variables": ["name"]
  },
  "password_reset": {
    "subject": "Reset Your Password",
    "body": "Hi {{name}},\n\nClick here to reset your password: {{link}}",
    "version": "1.5.4",
    "variables": ["name", "link"]
  },
  "account_deactivated": {
    "subject": "Your Account Has Been Deactivated",
    "body": "Hi {{name}},\n\nWe noticed you deactivated your account. We hope to see you back soon.",
    "version": "1.0.0",
    "variables": ["name"]
  }
}
Template Fields:
FieldTypeDescription
subjectstringEmail subject line or push notification title
bodystringTemplate body with {{variable}} placeholders
versionstringSemantic version for template versioning
variablesarrayList of required variable names
Variables use Mustache-style {{variable}} syntax and are replaced by downstream services (Email/Push).

API Endpoints

GET /api/v1/templates/:code

Retrieve a template by its code. Example Request:
GET /api/v1/templates/welcome_email
Implementation (main.py:7-23):
@app.get("/api/v1/templates/{code}")
def get_template(code: str):
    """
    Fetch a notification template by its code.
    Example: GET /api/v1/templates/welcome_email
    """
    with open("templates.json", "r") as f:
      templates = json.load(f)
    template = templates.get(code)
    if not template:
        raise HTTPException(status_code=404, detail="Template not found")
    return {
        "success": True, 
        "data": template, 
        "message": "Template fetched successfully"
    }
Response (200 OK):
{
  "success": true,
  "data": {
    "subject": "Welcome to Our Platform!",
    "body": "Hi {{name}},\n\nThanks for signing up. We're glad to have you onboard!",
    "version": "2.0.0",
    "variables": ["name"]
  },
  "message": "Template fetched successfully"
}
Error Response (404 Not Found):
{
  "detail": "Template not found"
}

GET /

Root endpoint with welcome message. Response (200 OK):
{
  "message": "Welcome to the Template Service"
}

GET /health

Health check endpoint for monitoring and orchestration. Implementation (main.py:33-38):
@app.get("/health")
def health_check():
    """
    Health check endpoint — for CI/CD, monitoring, or orchestration tools.
    """
    return {"status": "ok", "service": "template-service"}
Response (200 OK):
{
  "status": "ok",
  "service": "template-service"
}

Template Variables

Templates support dynamic variables that are replaced by downstream services. Variable Syntax:
  • Use double curly braces: {{variable_name}}
  • Variables are case-sensitive
  • Undefined variables are left as-is (not replaced)
Example Template:
Subject: Reset Your Password
Body: Hi {{name}},

Click here to reset your password: {{link}}

This link will expire in 24 hours.
Variable Replacement (performed by Email/Push services):
{
  "name": "John Doe",
  "link": "https://example.com/reset?token=abc123"
}
Rendered Output:
Subject: Reset Your Password
Body: Hi John Doe,

Click here to reset your password: https://example.com/reset?token=abc123

This link will expire in 24 hours.

Template Versioning

Each template includes a semantic version number for tracking changes. Version Format: MAJOR.MINOR.PATCH
  • MAJOR: Breaking changes (e.g., removed variables)
  • MINOR: New features (e.g., added variables)
  • PATCH: Bug fixes or minor text changes
Example:
{
  "welcome_email": {
    "version": "2.0.0",
    "variables": ["name", "link"]
  }
}
Version changes help track template evolution and coordinate updates across teams.

Adding New Templates

To add a new template, edit templates.json and add a new entry. Example:
{
  "order_confirmation": {
    "subject": "Order Confirmed - #{{order_id}}",
    "body": "Hi {{name}},\n\nYour order #{{order_id}} has been confirmed!\n\nTotal: ${{total}}",
    "version": "1.0.0",
    "variables": ["name", "order_id", "total"]
  }
}
Restart the service after editing templates.json for changes to take effect (unless using auto-reload).

Running the Service

Development (with auto-reload)

python main.py
The service will start on http://0.0.0.0:8002 with auto-reload enabled.

Production

uvicorn main:app --host 0.0.0.0 --port 8002 --workers 4

Docker

docker build -t template-service .
docker run -p 8002:8002 template-service
Dockerfile Example:
FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY main.py templates.json ./

EXPOSE 8002
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8002"]

API Documentation

FastAPI automatically generates interactive API documentation. Swagger UI: http://localhost:8002/docs ReDoc: http://localhost:8002/redoc OpenAPI Spec: http://localhost:8002/openapi.json Configuration (main.py:4):
app = FastAPI(title="Template Service")

Performance Considerations

  • File I/O: Templates are loaded from disk on each request
  • Caching: Consider adding in-memory caching for production
  • Concurrency: Uvicorn supports multiple workers for high traffic
Optimization Example (with caching):
import json
from functools import lru_cache

@lru_cache(maxsize=1)
def load_templates():
    with open("templates.json", "r") as f:
        return json.load(f)

@app.get("/api/v1/templates/{code}")
def get_template(code: str):
    templates = load_templates()
    template = templates.get(code)
    if not template:
        raise HTTPException(status_code=404, detail="Template not found")
    return {
        "success": True,
        "data": template,
        "message": "Template fetched successfully"
    }
The @lru_cache decorator caches template data in memory, reducing file I/O overhead.

Future Enhancements

Database Backend: Replace JSON file storage with a database (PostgreSQL, MongoDB) for:
  • Multi-user template management
  • Template history and rollbacks
  • Real-time updates without restarts
Template Validation: Add validation to ensure:
  • All declared variables are present in the body
  • No undeclared variables are used
  • Subject/body fields are not empty
Internationalization (i18n): Support multiple languages:
{
  "welcome_email": {
    "en": {
      "subject": "Welcome!",
      "body": "Hi {{name}}..."
    },
    "es": {
      "subject": "¡Bienvenido!",
      "body": "Hola {{name}}..."
    }
  }
}

Error Handling

FastAPI provides automatic error handling with detailed error responses. 404 Example (main.py:18):
if not template:
    raise HTTPException(status_code=404, detail="Template not found")
Response:
{
  "detail": "Template not found"
}

Build docs developers (and LLMs) love