Skip to content

Extensions

An extension wires a third-party service into your app: it registers DI providers in setup(app) and can hook the ASGI lifespan via startup() / shutdown().

List them in extensions.py at your app root:

# extensions.py
from endocore.extensions import RedisExtension, CacheExtension, EmailExtension

extensions = [
    RedisExtension(url="redis://localhost:6379/0"),
    CacheExtension(backend="redis"),
    EmailExtension(host="smtp.example.com", port=587, use_tls=True),
]

At boot, EndoCore calls each setup(app) and adds each extension's startup/shutdown to the lifespan — so connections open and close cleanly without touching hooks.py.

Shipped extensions

  • Redis — a Redis client provider (endocore[redis]).
  • Celery — a Celery app provider (endocore[celery]).
  • Email — an SMTP email client (stdlib, no extra dep).
  • CacheCacheExtension(backend=...) configures the cache and injects it as cache.
  • Metrics — a Prometheus registry provider (endocore[metrics]).
  • Tracing — an OpenTelemetry TracerProvider (endocore[otel]).

Using an injected service

Once an extension registers a provider, inject it by name (or type) in any handler:

async def handler(request, redis, cache):
    cache.set("k", "v", ttl=60)
    redis.publish("events", "hello")
    ...

Write your own

Subclass Extension — that's the whole contract:

from endocore.extensions import Extension

class KafkaExtension(Extension):
    name = "kafka"

    def __init__(self, *, brokers):
        self.brokers = brokers
        self._producer = None

    def _client(self):
        if self._producer is None:
            from kafka import KafkaProducer
            self._producer = KafkaProducer(bootstrap_servers=self.brokers)
        return self._producer

    def setup(self, app):
        app.provide("kafka", self._client, singleton=True)

    async def shutdown(self):
        if self._producer:
            self._producer.close()
# extensions.py
from Extensions.kafka import KafkaExtension
extensions = [KafkaExtension(brokers="localhost:9092")]

That's how "support for many services" scales — the framework ships a few common ones and gives everyone the same clean pattern for the rest.