Skip to content

Custom Backend

You can also implement your own backend for your Esmerald application as well. The way of doing it is by subclassing the SessionBackend from the package and implement the methods.

SessionBackend

Main class of all available backends from Esmerald Sessions.

from esmerald_sessions import SessionBackend

Available backends

Esmerald Sessions brings some built-in backends to be used within any Esmerald application.

AioMemCacheSessionBackend

from esmerald_sessions import AioMemCacheSessionBackend

AioRedisSessionBackend

from esmerald_sessions import AioRedisSessionBackend

MemCacheSessionBackend

from esmerald_sessions import MemCacheSessionBackend

RedisSessionBackend

from esmerald_sessions import RedisSessionBackend

Implement a custom backend

As mentioned before, all the custom backends must inherit from SessionBackend and implement the methods.

from typing import TYPE_CHECKING, Any, Optional

from esmerald_sessions import SessionBackend

if TYPE_CHECKING:
    from pydantic.typing import DictAny


class MyCustomBackend(SessionBackend):
    async def get(self, key: str, **kwargs: "DictAny") -> Optional["DictAny"]:
        # Add logic here
        ...

    async def set(
        self, key: str, value: "DictAny", expire: Optional[int], **kwargs: "DictAny"
    ) -> Optional[str]:
        # Add logic here
        ...

    async def delete(self, key: str, **kwargs: "DictAny") -> Any:
        # Add logic here
        ...

Example

backend.py
from typing import TYPE_CHECKING, Any, Optional

from esmerald_sessions import SessionBackend

if TYPE_CHECKING:
    from pydantic.typing import DictAny


class Client:
    # Your new client and logic here
    ...


class MyCustomBackend(SessionBackend):
    client: Optional[Client]

    def __init__(self, client: Client, **kwargs: "DictAny") -> None:
        super().__init__(**kwargs)
        self.client = client

    async def get(self, key: str, **kwargs: "DictAny") -> Optional["DictAny"]:
        value = await self.client.get(key, **kwargs)
        return value if value else None

    async def set(
        self, key: str, value: "DictAny", expire: Optional[int], **kwargs: "DictAny"
    ) -> Optional[str]:
        return await self.client.set(key, value, expire=expire, **kwargs)

    async def delete(self, key: str, **kwargs: "DictAny") -> Any:
        return await self.client.delete(key, **kwargs)

Now we can add the new client to the application.

backend.py
from enums import BackendType
from esmerald import Esmerald, Gateway, get, post
from esmerald.requests import Request
from esmerald.responses import JSONResponse
from esmerald.utils.crypto import get_random_secret_key
from starlette.middleware import Middleware

from esmerald_sessions import SessionConfig, SessionMiddleware

from .backend import MyCustomBackend


@get()
async def view_session(request: Request) -> JSONResponse:
    return JSONResponse({"session": request.session})


@post()
async def setup_session(request: Request) -> JSONResponse:
    request.session.update({"data": "session.data"})
    return JSONResponse({"session": request.session})


@post()
async def clear_session(request: Request) -> JSONResponse:
    request.session.clear()
    return JSONResponse({"session": request.session})


custom_client = MyCustomBackend(...)
session_config = SessionConfig(
    secret_key=get_random_secret_key(),
    cookie_name="cookie",
    backend_type=BackendType.redis,
    custom_session_backend=custom_client,
)

app = Esmerald(
    routes=[
        Gateway("/view-session", handler=view_session),
        Gateway("/setup-session", handler=setup_session),
        Gateway("/clear-session", handler=clear_session),
    ],
    middleware=[Middleware(SessionMiddleware, config=session_config)],
)

As you can see, the difference between the built-in backends and the custom is the fact that the custom should always be passed into the custom_session_backend property.