dto-sync-patterns
Patterns for synchronizing DTOs between frontend and backend. Covers shared types, code generation, and validation sync. USE WHEN: user asks about "DTO sync", "shared types", "frontend backend types", "type consistency", "API models" DO NOT USE FOR: type generation tools - use `type-generation` skill, validation rules - use validation skills
What this skill does
# DTO Sync Patterns - Quick Reference
## When NOT to Use This Skill
- **Type generation setup** - Use `type-generation` skill
- **Validation implementation** - Use language-specific validation skills
- **API contract validation** - Use `openapi-contract` skill
## Sync Strategy Overview
```
┌─────────────────────────────────────────────────────────────────────┐
│ DTO SYNC STRATEGIES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Schema-First (Recommended) │
│ ┌──────────────┐ │
│ │ OpenAPI Spec │───→ Generate Backend DTOs │
│ │ (Source) │───→ Generate Frontend Types │
│ └──────────────┘ │
│ │
│ 2. Backend-First │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Backend DTOs │───→ │ OpenAPI Spec │───→ Frontend Types │
│ │ (Source) │ │ (Generated) │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ 3. Shared Package (Monorepo) │
│ ┌──────────────┐ │
│ │ @shared/types│───→ Backend imports │
│ │ (TypeScript) │───→ Frontend imports │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
## Pattern 1: Schema-First
### OpenAPI as Source of Truth
```yaml
# openapi.yaml - Single source of truth
components:
schemas:
CreateUserRequest:
type: object
required:
- email
- name
properties:
email:
type: string
format: email
maxLength: 255
name:
type: string
minLength: 2
maxLength: 100
age:
type: integer
minimum: 0
maximum: 150
User:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
name:
type: string
age:
type: integer
createdAt:
type: string
format: date-time
```
### Generate for Backend (Java)
```bash
# Generate Java DTOs from OpenAPI
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g spring \
-o generated/java \
--additional-properties=useJakartaEe=true
```
```java
// Generated: CreateUserRequest.java
@Generated
public class CreateUserRequest {
@NotNull
@Email
@Size(max = 255)
private String email;
@NotNull
@Size(min = 2, max = 100)
private String name;
@Min(0)
@Max(150)
private Integer age;
// getters, setters...
}
```
### Generate for Frontend (TypeScript)
```bash
# Generate TypeScript types from OpenAPI
npx openapi-typescript openapi.yaml -o src/api/types.ts
```
```typescript
// Generated: types.ts
export interface components {
schemas: {
CreateUserRequest: {
email: string;
name: string;
age?: number;
};
User: {
id?: string;
email?: string;
name?: string;
age?: number;
createdAt?: string;
};
};
}
```
## Pattern 2: Backend-First
### Backend Generates OpenAPI
```java
// Spring Boot with springdoc-openapi
@Schema(description = "Request to create a new user")
public record CreateUserRequest(
@Schema(description = "User email", example = "[email protected]")
@NotNull
@Email
@Size(max = 255)
String email,
@Schema(description = "User name", example = "John Doe")
@NotNull
@Size(min = 2, max = 100)
String name,
@Schema(description = "User age", minimum = "0", maximum = "150")
@Min(0)
@Max(150)
Integer age
) {}
```
```bash
# Export OpenAPI spec from running backend
curl http://localhost:8080/v3/api-docs > openapi.json
# Generate frontend types
npx openapi-typescript openapi.json -o src/api/types.ts
```
### NestJS with Swagger
```typescript
// NestJS DTO with decorators
@Schema({ description: 'Request to create a new user' })
export class CreateUserDto {
@ApiProperty({ example: '[email protected]' })
@IsEmail()
@MaxLength(255)
email: string;
@ApiProperty({ example: 'John Doe' })
@IsString()
@Length(2, 100)
name: string;
@ApiPropertyOptional({ minimum: 0, maximum: 150 })
@IsOptional()
@IsInt()
@Min(0)
@Max(150)
age?: number;
}
```
```bash
# Export from NestJS
# (requires @nestjs/swagger setup)
curl http://localhost:3000/api-json > openapi.json
```
## Pattern 3: Shared Package (Monorepo)
### Project Structure
```
monorepo/
├── packages/
│ ├── shared/
│ │ ├── package.json
│ │ └── src/
│ │ ├── types/
│ │ │ ├── user.ts
│ │ │ └── index.ts
│ │ └── validation/
│ │ ├── user.ts
│ │ └── index.ts
│ ├── frontend/
│ │ └── package.json # depends on @shared
│ └── backend/
│ └── package.json # depends on @shared
└── package.json
```
### Shared Types
```typescript
// packages/shared/src/types/user.ts
export interface User {
id: string;
email: string;
name: string;
age?: number;
createdAt: Date;
}
export interface CreateUserRequest {
email: string;
name: string;
age?: number;
}
export interface UpdateUserRequest {
name?: string;
age?: number;
}
// Type guards
export function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'email' in obj
);
}
```
### Shared Validation (Zod)
```typescript
// packages/shared/src/validation/user.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(2).max(100),
age: z.number().int().min(0).max(150).optional(),
});
export const UpdateUserSchema = z.object({
name: z.string().min(2).max(100).optional(),
age: z.number().int().min(0).max(150).optional(),
});
// Infer types from schemas
export type CreateUserRequest = z.infer<typeof CreateUserSchema>;
export type UpdateUserRequest = z.infer<typeof UpdateUserSchema>;
```
### Frontend Usage
```typescript
// packages/frontend/src/api/users.ts
import type { User, CreateUserRequest } from '@shared/types';
import { CreateUserSchema } from '@shared/validation';
async function createUser(data: CreateUserRequest): Promise<User> {
// Validate before sending
const validated = CreateUserSchema.parse(data);
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(validated),
});
return response.json();
}
```
### Backend Usage (Node.js)
```typescript
// packages/backend/src/routes/users.ts
import type { CreateUserRequest } from '@shared/types';
import { CreateUserSchema } from '@shared/validation';
app.post('/api/users', async (req, res) => {
// Same validation as frontend
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
code: 'VALIDATION_ERROR',
details: result.error.issues,
});
}
const user = await userService.create(result.data);
res.json(user);
});
```
## Validation Sync
### Zod (TypeScript Both Ends)
```typescript
// Shared schema
const UserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
});
// Frontend: Form validation
const form = useForm({
resolver: zodResolver(UserSchema),
});
// BackeRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.