skillby maneeshanif

dapr-integration

Integrate Dapr building blocks for event-driven microservices - Pub/Sub, State Management, Secrets, Service Invocation, and Jobs API. Use when implementing event-driven architecture for Phase 5. (project)

Installs: 0
Used in: 1 repos
Updated: 2d ago
$npx ai-builder add skill maneeshanif/dapr-integration

Installs to .claude/skills/dapr-integration/

# Dapr Integration Skill

## Quick Start

1. **Read Phase 5 Constitution** - `constitution-prompt-phase-5.md`
2. **Check Dapr installation** - `dapr --version`
3. **Initialize Dapr** - `dapr init` or `dapr init -k` for Kubernetes
4. **Create component files** - In `dapr-components/` directory
5. **Configure sidecar** - Annotations for Kubernetes deployments
6. **Test locally** - `dapr run` commands

## 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`:

```yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: taskpubsub
  namespace: todo-app
spec:
  type: pubsub.kafka
  version: v1
  metadata:
    - name: brokers
      value: "kafka:9092"
    - name: consumerGroup
      value: "todo-consumer-group"
    - name: authType
      value: "none"
    - name: disableTls
      value: "true"
scopes:
  - backend
  - notification-service
  - recurring-service
  - audit-service
```

### State Store Component

Create `dapr-components/statestore.yaml`:

```yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
  namespace: todo-app
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: "redis:6379"
    - name: redisPassword
      value: ""
    - name: actorStateStore
      value: "true"
scopes:
  - backend
```

### Secrets Component

Create `dapr-components/secrets.yaml`:

```yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: kubernetes-secrets
  namespace: todo-app
spec:
  type: secretstores.kubernetes
  version: v1
  metadata: []
```

## Python SDK Integration

### Installation

```bash
uv add dapr dapr-ext-fastapi
```

### Pub/Sub Publisher

```python
from dapr.clients import DaprClient

async def publish_task_event(event_type: str, task_data: dict):
    """Publish task event to Kafka via Dapr."""
    with DaprClient() as client:
        client.publish_event(
            pubsub_name="taskpubsub",
            topic_name="task-events",
            data=json.dumps({
                "event_type": event_type,
                "task": task_data,
                "timestamp": datetime.utcnow().isoformat()
            }),
            data_content_type="application/json"
        )
```

### Pub/Sub Subscriber (FastAPI)

```python
from dapr.ext.fastapi import DaprApp
from fastapi import FastAPI

app = FastAPI()
dapr_app = DaprApp(app)

@dapr_app.subscribe(pubsub="taskpubsub", topic="task-events")
async def handle_task_event(event: dict):
    """Handle incoming task events."""
    event_type = event.get("event_type")
    task_data = event.get("task")

    if event_type == "task.created":
        await process_new_task(task_data)
    elif event_type == "task.completed":
        await process_completed_task(task_data)
```

### State Management

```python
from dapr.clients import DaprClient

async def save_state(key: str, value: dict):
    """Save state to Dapr state store."""
    with DaprClient() as client:
        client.save_state(
            store_name="statestore",
            key=key,
            value=json.dumps(value)
        )

async def get_state(key: str) -> dict | None:
    """Get state from Dapr state store."""
    with DaprClient() as client:
        state = client.get_state(store_name="statestore", key=key)
        return json.loads(state.data) if state.data else None
```

### Service Invocation

```python
from dapr.clients import DaprClient

async def invoke_notification_service(user_id: str, message: str):
    """Invoke notification service via Dapr."""
    with DaprClient() as client:
        response = client.invoke_method(
            app_id="notification-service",
            method_name="send",
            data=json.dumps({
                "user_id": user_id,
                "message": message
            }),
            http_verb="POST"
        )
        return response.json()
```

### Jobs API (Scheduled Tasks)

```python
from dapr.clients import DaprClient

async def schedule_reminder(reminder_id: str, due_at: datetime):
    """Schedule a reminder using Dapr Jobs API."""
    with DaprClient() as client:
        # Create a scheduled job
        client.start_workflow(
            workflow_component="dapr",
            workflow_name="reminder-workflow",
            input={
                "reminder_id": reminder_id,
                "scheduled_time": due_at.isoformat()
            }
        )
```

## Kubernetes Deployment Annotations

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "backend"
        dapr.io/app-port: "8000"
        dapr.io/enable-api-logging: "true"
        dapr.io/log-level: "info"
        dapr.io/config: "dapr-config"
    spec:
      containers:
        - name: backend
          image: evolution-todo/backend:latest
```

## Local Development with Dapr

### Run with Dapr Sidecar

```bash
# Run backend with Dapr
dapr run --app-id backend \
         --app-port 8000 \
         --dapr-http-port 3500 \
         --components-path ./dapr-components \
         -- uv run uvicorn src.main:app --host 0.0.0.0 --port 8000

# Run notification service with Dapr
dapr run --app-id notification-service \
         --app-port 8002 \
         --dapr-http-port 3502 \
         --components-path ./dapr-components \
         -- uv run uvicorn services.notification.main:app --host 0.0.0.0 --port 8002
```

### Test Pub/Sub

```bash
# Publish test event
dapr publish --publish-app-id backend \
             --pubsub taskpubsub \
             --topic task-events \
             --data '{"event_type":"task.created","task":{"id":"123","title":"Test"}}'
```

## Verification Checklist

- [ ] Dapr CLI installed (`dapr --version`)
- [ ] Dapr initialized (`dapr init` or `dapr 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 |

## References

- [Dapr Documentation](https://docs.dapr.io/)
- [Dapr Python SDK](https://github.com/dapr/python-sdk)
- [Dapr Pub/Sub](https://docs.dapr.io/developing-applications/building-blocks/pubsub/)
- [Dapr State Management](https://docs.dapr.io/developing-applications/building-blocks/state-management/)
- [Phase 5 Constitution](../../../constitution-prompt-phase-5.md)

Quick Install

$npx ai-builder add skill maneeshanif/dapr-integration

Details

Type
skill
Slug
maneeshanif/dapr-integration
Created
6d ago