Adding New Shared Types
When building a new feature, you need to create shared types and validators that both the frontend and backend will use. Follow this step-by-step guide.
Step-by-Step Process
Step 1: Create the Type File
// packages/shared/src/types/feature.types.ts
export interface Feature {
id: string;
workspaceId: string;
name: string;
description: string;
status: FeatureStatus;
createdAt: string;
updatedAt: string;
}
export type FeatureStatus = 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
export interface CreateFeatureInput {
name: string;
description?: string;
}
export interface UpdateFeatureInput {
name?: string;
description?: string;
status?: FeatureStatus;
}
export interface FeatureListResponse {
items: Feature[];
total: number;
page: number;
pageSize: number;
}
Step 2: Create the Validator File
// packages/shared/src/validators/feature.validators.ts
import { z } from 'zod';
export const featureStatusEnum = z.enum(['DRAFT', 'ACTIVE', 'ARCHIVED']);
export const createFeatureSchema = z.object({
name: z.string().min(1, 'Name is required').max(255),
description: z.string().max(1000).optional(),
});
export const updateFeatureSchema = z.object({
name: z.string().min(1).max(255).optional(),
description: z.string().max(1000).optional(),
status: featureStatusEnum.optional(),
});
// Infer types from schemas (optional -- you can use the interface types instead)
export type CreateFeatureInput = z.infer<typeof createFeatureSchema>;
export type UpdateFeatureInput = z.infer<typeof updateFeatureSchema>;
Step 3: Export from Index Files
Add exports to the package's barrel files:
// packages/shared/src/types/index.ts
export * from './feature.types';
// ... existing exports
// packages/shared/src/validators/index.ts
export * from './feature.validators';
// ... existing exports
// packages/shared/src/index.ts (main entry)
export * from './types';
export * from './validators';
Step 4: Rebuild the Shared Package
npm run build --filter=shared
The types and validators are now available in both apps/api and apps/web.
Must Rebuild After Changes
The shared package must be rebuilt for changes to be picked up by the API and web apps. During npm run dev, the shared package is watched for changes and rebuilt automatically via Turborepo.
Step 5: Use in API and Web
Backend (API route):
// apps/api/src/routes/feature.routes.ts
import { createFeatureSchema, updateFeatureSchema } from '@unipulse/shared';
import { validate } from '../middleware/validate';
router.post('/',
authenticate,
requireWorkspace('EDITOR'),
validate(createFeatureSchema),
featureController.create
);
router.patch('/:id',
authenticate,
requireWorkspace('EDITOR'),
validate(updateFeatureSchema),
featureController.update
);
Frontend (React form):
// apps/web/src/pages/features/CreateFeatureForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { createFeatureSchema, type CreateFeatureInput } from '@unipulse/shared';
function CreateFeatureForm() {
const form = useForm<CreateFeatureInput>({
resolver: zodResolver(createFeatureSchema),
});
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
<input {...form.register('name')} />
{form.formState.errors.name && (
<span>{form.formState.errors.name.message}</span>
)}
</form>
);
}
Naming Conventions
| Item | Convention | Example |
|---|---|---|
| Type file | {domain}.types.ts | feature.types.ts |
| Validator file | {domain}.validators.ts | feature.validators.ts |
| Interface names | PascalCase | Feature, CreateFeatureInput |
| Schema names | camelCase + Schema suffix | createFeatureSchema |
| Enum schemas | camelCase + Enum suffix | featureStatusEnum |
| Inferred types | Same as interface name | CreateFeatureInput |
Checklist
- Type file created in
packages/shared/src/types/ - Validator file created in
packages/shared/src/validators/ - Exported from
types/index.tsandvalidators/index.ts - Shared package rebuilt (
npm run build --filter=shared) - Used in backend route with
validate()middleware - Used in frontend form with
zodResolver() - Both EN and AR validation messages added (if custom messages)
Cross-Reference
- Types -- existing type file reference
- Validators -- existing validator file reference
- New Feature Checklist -- full end-to-end feature guide
- Middleware -- validate.ts middleware