smithery/psincraian

routing-api

myfy web routing with FastAPI-like decorators. Use when working with WebModule, @route decorators, path parameters, query parameters, request bodies, AuthModule for authentication, RateLimitModule for rate limiting, or error handling.

Installation

$ npx skills add smithery/psincraian --skill routing-api

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/psincraian.

npx skills add smithery/psincraian

Browse all from smithery/psincraian

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,705 B
  • docs SUMMARY.md 253 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Web Routing in myfy

myfy provides FastAPI-like routing with full DI integration.

Route Decorators

from myfy.web import route

@route.get("/path")
async def handler() -> dict:
    return {"message": "hello"}

@route.post("/path", status_code=201)
async def create() -> dict:
    return {"created": True}

@route.put("/path/{id}")
async def update(id: int) -> dict:
    return {"updated": id}

@route.delete("/path/{id}", status_code=204)
async def delete(id: int) -> None:
    pass

@route.patch("/path/{id}")
async def partial_update(id: int) -> dict:
    return {"patched": id}

Path Parameters

Extract from URL template using {param}:

@route.get("/users/{user_id}/posts/{post_id}")
async def get_post(user_id: int, post_id: int) -> dict:
    return {"user": user_id, "post": post_id}

Path parameters are:

  • Automatically type-converted based on annotation
  • Must match function parameter names exactly
  • Must be valid Python identifiers

Query Parameters

Use Query for explicit query parameters:

from myfy.web import Query

@route.get("/search")
async def search(
    q: str = Query(default=""),           # With default value
    limit: int = Query(default=10),       # Integer query param
    page: int = Query(alias="p"),         # Aliased (?p=1 in URL)
) -> dict:
    return {"query": q, "limit": limit, "page": page}

Request Body

Use Pydantic models or dataclasses for request bodies:

from pydantic import BaseModel

class UserCreate(BaseModel):
    email: str
    name: str

@route.post("/users", status_code=201)
async def create_user(body: UserCreate, session: AsyncSession) -> dict:
    user = User(**body.model_dump())
    session.add(user)
    await session.commit()
    return {"id": user.id}

Request bodies are automatically:

  • Parsed from JSON
  • Validated by Pydantic
  • Type-checked at runtime

Parameter Classification

Parameters are classified in this order:

  1. Path parameters - Names matching {param} in route path
  2. Query parameters - Annotated with Query(...)
  3. Body parameter - Pydantic model, dataclass, or dict
  4. DI dependencies - Everything else (resolved from container)
@route.post("/users/{user_id}/orders")
async def create_order(
    user_id: int,                    # 1. Path param (matches {user_id})
    limit: int = Query(default=10),  # 2. Query param (explicit Query)
    body: OrderCreate,               # 3. Request body (Pydantic model)
    session: AsyncSession,           # 4. DI dependency
    settings: AppSettings,           # 4. DI dependency
) -> dict:
    ...

Authentication

Use Authenticated for protected routes:

from myfy.web import Authenticated, AuthModule
from dataclasses import dataclass

@dataclass
class User(Authenticated):
    email: str

# Register auth provider
def my_auth(request: Request) -> User | None:
    token = request.headers.get("Authorization")
    if not token:
        return None  # Results in 401
    return User(id="123", email="[email protected]")

app.add_module(AuthModule(authenticated_provider=my_auth))

# Protected route - returns 401 if not authenticated
@route.get("/profile")
async def profile(user: User) -> dict:
    return {"id": user.id, "email": user.email}

Error Handling

Quick Errors with abort()

from myfy.web import abort

@route.get("/users/{user_id}")
async def get_user(user_id: int, session: AsyncSession) -> dict:
    user = await session.get(User, user_id)
    if not user:
        abort(404, "User not found")
    return {"user": user}

Typed Errors

from myfy.web import errors

raise errors.NotFound("User not found")
raise errors.BadRequest("Invalid email", field="email")
raise errors.Unauthorized("Invalid token")
raise errors.Forbidden("Access denied")
raise errors.Conflict("Email already exists")

Custom Exceptions

from myfy.web.exceptions import WebError

class RateLimitExceeded(WebError):
    status_code = 429
    error_type = "rate_limit_exceeded"

Rate Limiting

from myfy.web.ratelimit import RateLimitModule, rate_limit, RateLimitKey

# Add module
app.add_module(RateLimitModule())

# Rate limit by IP (default)
@route.get("/api/data")
@rate_limit(100)  # 100 requests per minute per IP
async def get_data() -> dict:
    ...

# Rate limit by authenticated user
@route.get("/api/profile")
@rate_limit(50, key=RateLimitKey.USER)
async def get_profile(user: User) -> dict:
    ...

Response Types

Routes can return:

# Dict (serialized to JSON)
@route.get("/json")
async def json_response() -> dict:
    return {"key": "value"}

# Pydantic model (serialized to JSON)
@route.get("/model")
async def model_response() -> UserResponse:
    return UserResponse(id=1, name="John")

# None for 204 No Content
@route.delete("/users/{id}", status_code=204)
async def delete_user(id: int) -> None:
    ...

Best Practices

  1. Always use async - All handlers should be async functions
  2. Type all parameters - Use type hints for auto-classification
  3. Use Pydantic for bodies - Get free validation
  4. Return typed responses - Prefer Pydantic models over dicts
  5. Use appropriate status codes - 201 for creation, 204 for deletion
  6. Handle errors explicitly - Use abort() or typed errors
  7. Document with docstrings - Add OpenAPI-compatible docs