Dapr Integration Skill
Quick Start
- Read Phase 5 Constitution -
constitution-prompt-phase-5.md - Check Dapr installation -
dapr --version - Initialize Dapr -
dapr initordapr init -kfor Kubernetes - Create component files - In
dapr-components/directory - Configure sidecar - Annotations for Kubernetes deployments
- Test locally -
dapr runcommands
Dapr Building Blocks Overview
| Building Block | Purpose | Phase 5 Usage |
|---|---|---|
| Pub/Sub | Event messaging | Task events, reminders, audit logs |
| State | Key-value storage | Cache, session state |
| Secrets | Secret management | API keys, DB credentials |
| Service Invocation | Service-to-service calls | Microservice communication |
| Jobs API | Scheduled tasks | Recurring task scheduling |
Component Configuration
Pub/Sub Component (Kafka)
Create dapr-components/pubsub.yaml:
yaml1apiVersion: dapr.io/v1alpha1 2kind: Component 3metadata: 4 name: taskpubsub 5 namespace: todo-app 6spec: 7 type: pubsub.kafka 8 version: v1 9 metadata: 10 - name: brokers 11 value: "kafka:9092" 12 - name: consumerGroup 13 value: "todo-consumer-group" 14 - name: authType 15 value: "none" 16 - name: disableTls 17 value: "true" 18scopes: 19 - backend 20 - notification-service 21 - recurring-service 22 - audit-service
State Store Component
Create dapr-components/statestore.yaml:
yaml1apiVersion: dapr.io/v1alpha1 2kind: Component 3metadata: 4 name: statestore 5 namespace: todo-app 6spec: 7 type: state.redis 8 version: v1 9 metadata: 10 - name: redisHost 11 value: "redis:6379" 12 - name: redisPassword 13 value: "" 14 - name: actorStateStore 15 value: "true" 16scopes: 17 - backend
Secrets Component
Create dapr-components/secrets.yaml:
yaml1apiVersion: dapr.io/v1alpha1 2kind: Component 3metadata: 4 name: kubernetes-secrets 5 namespace: todo-app 6spec: 7 type: secretstores.kubernetes 8 version: v1 9 metadata: []
Python SDK Integration
Installation
bash1uv add dapr dapr-ext-fastapi
Pub/Sub Publisher
python1from dapr.clients import DaprClient 2 3async def publish_task_event(event_type: str, task_data: dict): 4 """Publish task event to Kafka via Dapr.""" 5 with DaprClient() as client: 6 client.publish_event( 7 pubsub_name="taskpubsub", 8 topic_name="task-events", 9 data=json.dumps({ 10 "event_type": event_type, 11 "task": task_data, 12 "timestamp": datetime.utcnow().isoformat() 13 }), 14 data_content_type="application/json" 15 )
Pub/Sub Subscriber (FastAPI)
python1from dapr.ext.fastapi import DaprApp 2from fastapi import FastAPI 3 4app = FastAPI() 5dapr_app = DaprApp(app) 6 7@dapr_app.subscribe(pubsub="taskpubsub", topic="task-events") 8async def handle_task_event(event: dict): 9 """Handle incoming task events.""" 10 event_type = event.get("event_type") 11 task_data = event.get("task") 12 13 if event_type == "task.created": 14 await process_new_task(task_data) 15 elif event_type == "task.completed": 16 await process_completed_task(task_data)
State Management
python1from dapr.clients import DaprClient 2 3async def save_state(key: str, value: dict): 4 """Save state to Dapr state store.""" 5 with DaprClient() as client: 6 client.save_state( 7 store_name="statestore", 8 key=key, 9 value=json.dumps(value) 10 ) 11 12async def get_state(key: str) -> dict | None: 13 """Get state from Dapr state store.""" 14 with DaprClient() as client: 15 state = client.get_state(store_name="statestore", key=key) 16 return json.loads(state.data) if state.data else None
Service Invocation
python1from dapr.clients import DaprClient 2 3async def invoke_notification_service(user_id: str, message: str): 4 """Invoke notification service via Dapr.""" 5 with DaprClient() as client: 6 response = client.invoke_method( 7 app_id="notification-service", 8 method_name="send", 9 data=json.dumps({ 10 "user_id": user_id, 11 "message": message 12 }), 13 http_verb="POST" 14 ) 15 return response.json()
Jobs API (Scheduled Tasks)
python1from dapr.clients import DaprClient 2 3async def schedule_reminder(reminder_id: str, due_at: datetime): 4 """Schedule a reminder using Dapr Jobs API.""" 5 with DaprClient() as client: 6 # Create a scheduled job 7 client.start_workflow( 8 workflow_component="dapr", 9 workflow_name="reminder-workflow", 10 input={ 11 "reminder_id": reminder_id, 12 "scheduled_time": due_at.isoformat() 13 } 14 )
Kubernetes Deployment Annotations
yaml1apiVersion: apps/v1 2kind: Deployment 3metadata: 4 name: backend 5spec: 6 template: 7 metadata: 8 annotations: 9 dapr.io/enabled: "true" 10 dapr.io/app-id: "backend" 11 dapr.io/app-port: "8000" 12 dapr.io/enable-api-logging: "true" 13 dapr.io/log-level: "info" 14 dapr.io/config: "dapr-config" 15 spec: 16 containers: 17 - name: backend 18 image: evolution-todo/backend:latest
Local Development with Dapr
Run with Dapr Sidecar
bash1# Run backend with Dapr 2dapr run --app-id backend \ 3 --app-port 8000 \ 4 --dapr-http-port 3500 \ 5 --components-path ./dapr-components \ 6 -- uv run uvicorn src.main:app --host 0.0.0.0 --port 8000 7 8# Run notification service with Dapr 9dapr run --app-id notification-service \ 10 --app-port 8002 \ 11 --dapr-http-port 3502 \ 12 --components-path ./dapr-components \ 13 -- uv run uvicorn services.notification.main:app --host 0.0.0.0 --port 8002
Test Pub/Sub
bash1# Publish test event 2dapr publish --publish-app-id backend \ 3 --pubsub taskpubsub \ 4 --topic task-events \ 5 --data '{"event_type":"task.created","task":{"id":"123","title":"Test"}}'
Verification Checklist
- Dapr CLI installed (
dapr --version) - Dapr initialized (
dapr initordapr init -k) - Component files created in
dapr-components/ - Python SDK installed (
dapr,dapr-ext-fastapi) - Pub/Sub working (publish → subscribe)
- State store working (save → get)
- Service invocation working
- Kubernetes annotations configured
- All services have Dapr sidecars
Event Topics
| Topic | Publisher | Subscribers | Purpose |
|---|---|---|---|
task-events | Backend | Notification, Audit, WebSocket | Task CRUD events |
reminder-events | Recurring Service | Notification, Backend | Reminder triggers |
audit-events | All Services | Audit Service | Audit logging |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Sidecar not starting | Missing annotations | Add dapr.io/enabled: "true" |
| Pub/Sub not working | Component not loaded | Check component scope |
| Connection refused | Wrong port | Verify app-port matches app |
| State not persisting | Redis not running | Start Redis container |