Quick Start

Get started with SmartRoute in 5 minutes.

Installation

pip install smartroute

Your First Router

From test

Create a service with instance-scoped routing:

from smartroute import RoutedClass, Router, route

class Service(RoutedClass):
    def __init__(self, label: str):
        self.label = label
        self.api = Router(self, name="api")

    @route("api")
    def describe(self):
        return f"service:{self.label}"

# Each instance is isolated
first = Service("alpha")
second = Service("beta")

assert first.api.get("describe")() == "service:alpha"
assert second.api.get("describe")() == "service:beta"

Key concept: Routers are instantiated in __init__ with Router(self, ...) - each instance gets its own isolated router.

Custom Entry Names

From test

Use prefixes and explicit names for cleaner method registration:

class SubService(RoutedClass):
    def __init__(self, prefix: str):
        self.prefix = prefix
        self.routes = Router(self, name="routes", prefix="handle_")

    @route("routes")
    def handle_list(self):
        return f"{self.prefix}:list"

    @route("routes", name="detail")
    def handle_detail(self, ident: int):
        return f"{self.prefix}:detail:{ident}"

sub = SubService("users")

# Prefix stripped: "handle_list" → "list"
assert sub.routes.get("list")() == "users:list"

# Custom name used: "handle_detail" → "detail"
assert sub.routes.get("detail")(10) == "users:detail:10"

Building Hierarchies

From test

Create nested router structures:

class RootAPI(RoutedClass):
    def __init__(self):
        self.api = Router(self, name="api")
        self.users = SubService("users")
        self.products = SubService("products")

        self.api.attach_instance(self.users, name="users")
        self.api.attach_instance(self.products, name="products")

root = RootAPI()

# Access with dotted paths
assert root.api.get("users.list")() == "users:list"
assert root.api.get("products.detail")(5) == "products:detail:5"

Adding Plugins

From test

Extend behavior with plugins. Built-in plugins (logging, pydantic) are pre-registered; scope/channel policies live in the SmartPublisher ecosystem plugin.

class PluginService(RoutedClass):
    def __init__(self):
        self.api = Router(self, name="api").plug("logging")

    @route("api")
    def do_work(self):
        return "ok"

svc = PluginService()
result = svc.api.get("do_work")()  # Automatically logged

Validating Arguments

From test

Use Pydantic for automatic validation:

pip install smartroute[pydantic]
class ValidateService(RoutedClass):
    def __init__(self):
        self.api = Router(self, name="api").plug("pydantic")

    @route("api")
    def concat(self, text: str, number: int = 1) -> str:
        return f"{text}:{number}"

svc = ValidateService()

# Valid inputs
assert svc.api.get("concat")("hello", 3) == "hello:3"
assert svc.api.get("concat")("hi") == "hi:1"

# Invalid inputs raise ValidationError
# svc.api.get("concat")(123, "oops")  # ValidationError!

Next Steps

Now that you’ve learned the basics:

Need Help?