Why Use This This skill provides specialized capabilities for psincraian's codebase.
Use Cases Developing new features in the psincraian repository Refactoring existing code to follow psincraian standards Understanding and working with psincraian's codebase structure
Install Guide 2 steps 1 2 Install inside Ananke
Click Install Skill, paste the link below, then press Install.
https://github.com/psincraian/myfy/tree/main/plugins/claude-code/skills/routing-api Skill Snapshot Auto scan of skill assets. Informational only.
Valid SKILL.md Checks against SKILL.md specification
Source & Community
Updated At Jan 12, 2026, 08:05 PM
Skill Stats
SKILL.md 227 Lines
Total Files 1
Total Size 0 B
License NOASSERTION
---
name: routing-api
description: 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.
---
# Web Routing in myfy
myfy provides FastAPI-like routing with full DI integration.
## Route Decorators
```python
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}`:
```python
@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:
```python
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:
```python
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)
```python
@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:
```python
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()
```python
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
```python
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
```python
from myfy.web.exceptions import WebError
class RateLimitExceeded(WebError):
status_code = 429
error_type = "rate_limit_exceeded"
```
## Rate Limiting
```python
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:
```python
# 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