Skip to main content

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

ItemConventionExample
Type file{domain}.types.tsfeature.types.ts
Validator file{domain}.validators.tsfeature.validators.ts
Interface namesPascalCaseFeature, CreateFeatureInput
Schema namescamelCase + Schema suffixcreateFeatureSchema
Enum schemascamelCase + Enum suffixfeatureStatusEnum
Inferred typesSame as interface nameCreateFeatureInput

Checklist

  • Type file created in packages/shared/src/types/
  • Validator file created in packages/shared/src/validators/
  • Exported from types/index.ts and validators/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