|
| 1 | +# Backend Architecture |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The GraphQL Analyzer backend follows a layered architecture with clear separation of concerns: |
| 6 | + |
| 7 | +``` |
| 8 | +┌──────────────────────────────────────┐ |
| 9 | +│ API Layer │ ← External interface |
| 10 | +├──────────────────────────────────────┤ |
| 11 | +│ Services Layer │ ← Business logic |
| 12 | +├──────────────────────────────────────┤ |
| 13 | +│ Models │ Validation │ ← Domain objects & schemas |
| 14 | +└──────────────────────────────────────┘ |
| 15 | +``` |
| 16 | + |
| 17 | +## Directory Structure |
| 18 | + |
| 19 | +``` |
| 20 | +packages/backend/src/ |
| 21 | +├── api/ # API handlers (thin layer) |
| 22 | +│ ├── attacks.ts # Attack execution APIs |
| 23 | +│ └── graphql.ts # GraphQL endpoint APIs |
| 24 | +├── models/ # Domain models |
| 25 | +│ └── AttackSession.ts # Discriminated union state types |
| 26 | +├── services/ # Business logic |
| 27 | +│ ├── attacks/ # Attack modules |
| 28 | +│ │ ├── AttackService.ts # Main orchestrator |
| 29 | +│ │ ├── IntrospectionAttack.ts # Schema introspection |
| 30 | +│ │ ├── DepthAttack.ts # Query depth limits |
| 31 | +│ │ ├── ComplexityAttack.ts # Query complexity |
| 32 | +│ │ ├── BatchAttack.ts # Batch queries |
| 33 | +│ │ ├── FieldSuggestionAttack.ts # Field suggestion disclosure |
| 34 | +│ │ ├── headerUtils.ts # Header utilities (SDK getHeaders) |
| 35 | +│ │ ├── sessionManager.ts # Attack session state |
| 36 | +│ │ └── types.ts # Shared attack types |
| 37 | +│ └── graphql/ |
| 38 | +│ └── GraphQLService.ts # GraphQL introspection service |
| 39 | +├── validation/ # Zod schemas |
| 40 | +│ └── schemas.ts # JSON response validation |
| 41 | +├── index.ts # Main entry point |
| 42 | +├── sdk.ts # SDK singleton |
| 43 | +└── types.ts # Backend event types |
| 44 | +``` |
| 45 | + |
| 46 | +## Key Design Patterns |
| 47 | + |
| 48 | +### Discriminated Unions |
| 49 | + |
| 50 | +Attack session states use discriminated unions for type safety: |
| 51 | + |
| 52 | +```typescript |
| 53 | +type AttackSessionState = |
| 54 | + | AttackSessionRunning // status: "running" |
| 55 | + | AttackSessionCompleted // status: "completed" |
| 56 | + | AttackSessionFailed // status: "failed" |
| 57 | + | AttackSessionCancelled; // status: "cancelled" |
| 58 | +``` |
| 59 | + |
| 60 | +Benefits: |
| 61 | + |
| 62 | +- No optional fields |
| 63 | +- Type narrowing on status |
| 64 | +- Cleaner state transitions |
| 65 | + |
| 66 | +### Zod Validation |
| 67 | + |
| 68 | +JSON responses are validated using Zod schemas instead of type casting: |
| 69 | + |
| 70 | +```typescript |
| 71 | +const parsed = parseGraphQLResponse(responseBody); |
| 72 | +if (parsed.kind === "Ok" && parsed.value.data) { |
| 73 | + // Type-safe access to validated data |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +### SDK Headers API |
| 78 | + |
| 79 | +Uses the SDK's `getHeaders()` method instead of manually parsing raw requests: |
| 80 | + |
| 81 | +```typescript |
| 82 | +const rawHeaders = result.request.getHeaders(); |
| 83 | +for (const [name, values] of Object.entries(rawHeaders)) { |
| 84 | + headers[name] = values[0] ?? ""; |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +## Data Flow |
| 89 | + |
| 90 | +``` |
| 91 | +1. API Request (index.ts) |
| 92 | + ↓ |
| 93 | +2. API Handler (api/attacks.ts) |
| 94 | + ↓ |
| 95 | +3. Attack Service (services/attacks/AttackService.ts) |
| 96 | + ↓ |
| 97 | +4. Individual Attack Module (e.g., IntrospectionAttack.ts) |
| 98 | + ↓ |
| 99 | +5. Response Validation (validation/schemas.ts) |
| 100 | + ↓ |
| 101 | +6. Result returned |
| 102 | +``` |
0 commit comments