Skip to main content

Code Standards & Best Practices

Standards and conventions for Pet Service Management System across backend (NestJS) and frontend (Next.js/React).
TypeScript Strict Mode: All files use strict mode with no implicit any types and null/undefined checking enforced.

TypeScript Conventions

Naming Conventions

  • Use kebab-case for file names: auth.guard.ts, booking.service.ts
  • Feature directories in lowercase: auth/, bookings/, services/
  • Components in PascalCase: DashboardHeader.tsx, LanguageSwitcher.tsx

Type Annotations

const userId: string = user.id;
const booking: Booking = await bookingService.getById(id);
function processBooking(id: string): Promise<Booking> { }

Comments & Documentation

/**
 * Retrieves a booking by ID with all related data
 * @param id - The booking identifier
 * @returns The booking or undefined if not found
 * @throws NotFoundException if booking doesn't exist
 */
async getBookingById(id: string): Promise<Booking> {
  // Fetch booking with relations
  return this.prisma.booking.findUnique({
    where: { id },
    include: {
      pets: true,
      services: true,
      assignedStaff: true,
    },
  });
}

Common Type Patterns

export class CreateBookingDto {
  customerId: string;
  petIds: string[];
  serviceIds: string[];
  startDate: Date;
  notes?: string;
}

Backend (NestJS) Standards

Module Organization

src/bookings/
├── dto/
│   ├── create-booking.dto.ts
│   ├── update-booking.dto.ts
│   └── booking-filters.dto.ts
├── bookings.controller.spec.ts
├── bookings.controller.ts
├── bookings.module.ts
├── bookings.service.spec.ts
└── bookings.service.ts

Controllers

import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../auth/guards/auth.guard';
import { BookingsService } from './bookings.service';
import { CreateBookingDto } from './dto/create-booking.dto';

@Controller('bookings')
@UseGuards(AuthGuard) // Protect entire controller
export class BookingsController {
  constructor(private readonly bookingsService: BookingsService) {}

  @Get()
  async getBookings() {
    return this.bookingsService.findAll();
  }

  @Get(':id')
  async getBookingById(@Param('id') id: string) {
    return this.bookingsService.findById(id);
  }

  @Post()
  async createBooking(@Body() createDto: CreateBookingDto) {
    return this.bookingsService.create(createDto);
  }
}
Controller Patterns:
  • One controller per module
  • Use decorators: @Controller, @Get, @Post, @Put, @Delete, @Patch
  • Parameters: @Param, @Body, @Query
  • Guards for authentication/authorization
  • Error handling with appropriate HTTP status codes

Services

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateBookingDto } from './dto/create-booking.dto';
import { Booking } from '@prisma/client';

@Injectable()
export class BookingsService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll(): Promise<Booking[]> {
    return this.prisma.booking.findMany({
      include: {
        pets: true,
        services: true,
        assignedStaff: true,
      },
    });
  }

  async findById(id: string): Promise<Booking> {
    const booking = await this.prisma.booking.findUnique({
      where: { id },
      include: {
        pets: true,
        services: true,
      },
    });
    if (!booking) {
      throw new NotFoundException(`Booking ${id} not found`);
    }
    return booking;
  }

  async create(createDto: CreateBookingDto): Promise<Booking> {
    // Validation
    if (createDto.petIds.length === 0) {
      throw new BadRequestException('At least one pet is required');
    }

    // Business logic
    return this.prisma.booking.create({
      data: {
        customerId: createDto.customerId,
        startDate: createDto.startDate,
        status: 'PENDING',
        pets: {
          connect: createDto.petIds.map(id => ({ id })),
        },
        services: {
          connect: createDto.serviceIds.map(id => ({ id })),
        },
      },
      include: {
        pets: true,
        services: true,
      },
    });
  }
}
Service Patterns:
  • Inject PrismaService for database access
  • One responsibility per method
  • Throw appropriate exceptions
  • Return typed responses
  • Use async/await
  • Include relations in queries
  • Add business logic validation

Error Handling

import {
  BadRequestException,
  NotFoundException,
  UnauthorizedException,
  ConflictException,
} from '@nestjs/common';

// Use appropriate exceptions
if (!booking) {
  throw new NotFoundException('Booking not found');
}

if (booking.status === 'COMPLETED') {
  throw new BadRequestException('Cannot update completed booking');
}

if (!hasPermission(user, 'UPDATE_BOOKING')) {
  throw new UnauthorizedException('Permission denied');
}

if (emailExists) {
  throw new ConflictException('Email already registered');
}

Frontend (React/Next.js) Standards

Component Structure

// components/DashboardHeader.tsx
import React, { FC, ReactNode } from 'react';

interface DashboardHeaderProps {
  title: string;
  subtitle?: string;
  actions?: ReactNode;
}

/**
 * Dashboard header component with title and optional actions
 */
export const DashboardHeader: FC<DashboardHeaderProps> = ({
  title,
  subtitle,
  actions,
}) => {
  return (
    <div className="flex justify-between items-center p-6 bg-white border-b">
      <div>
        <h1 className="text-2xl font-bold">{title}</h1>
        {subtitle && <p className="text-gray-600">{subtitle}</p>}
      </div>
      {actions && <div>{actions}</div>}
    </div>
  );
};

DashboardHeader.displayName = 'DashboardHeader';

Page Components

// app/[locale]/dashboard/booking/page.tsx
import { ReactNode } from 'react';
import { useTranslations } from 'next-intl';
import { DashboardHeader } from '@/components/DashboardHeader';

interface BookingPageProps {
  params: Promise<{
    locale: string;
  }>;
}

export const metadata = {
  title: 'Bookings Management',
};

export default async function BookingPage({ params }: BookingPageProps) {
  const { locale } = await params;
  const t = useTranslations('dashboard.booking');

  return (
    <div>
      <DashboardHeader
        title={t('title')}
        subtitle={t('subtitle')}
      />
      {/* Content */}
    </div>
  );
}

API Clients

// lib/api/bookings.ts
import { API_URL } from '@/lib/config';
import { Booking, CreateBookingDto } from '@/types/booking';

const BASE_URL = `${API_URL}/bookings`;

export async function getBookings(): Promise<Booking[]> {
  const response = await fetch(BASE_URL, {
    headers: {
      'Authorization': `Bearer ${getToken()}`,
    },
  });

  if (!response.ok) {
    throw new Error(`Failed to fetch bookings: ${response.statusText}`);
  }

  return response.json();
}

export async function getBookingById(id: string): Promise<Booking> {
  const response = await fetch(`${BASE_URL}/${id}`, {
    headers: {
      'Authorization': `Bearer ${getToken()}`,
    },
  });

  if (!response.ok) {
    throw new Error('Booking not found');
  }

  return response.json();
}

export async function createBooking(data: CreateBookingDto): Promise<Booking> {
  const response = await fetch(BASE_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`,
    },
    body: JSON.stringify(data),
  },);

  if (!response.ok) {
    throw new Error('Failed to create booking');
  }

  return response.json();
}

Styling Guidelines

Design System Overview

Framework: Tailwind CSS 4 with custom design tokens

Color Palette

'#FF6B6B'  // Primary brand color - buttons, links, accents
'#FF5A5A'  // Hover state
'#E24D4D'  // Active/pressed state

Component Patterns

// Primary button
className="bg-[#FF6B6B] text-white px-4 md:px-6 py-2 rounded-md
           hover:bg-[#FF5A5A] active:bg-[#E24D4D] transition-colors"

// Secondary button
className="border border-[#EADFD6] text-[#475569] px-4 md:px-6 py-2 rounded-md
           hover:bg-[#FFFBF5] active:bg-[#FFE5E5] transition-colors"

Git & Version Control

Commit Conventions

Format: <type>: <subject> Types:
  • feat: New feature
  • fix: Bug fix
  • refactor: Code refactoring
  • style: Formatting changes
  • test: Testing additions
  • docs: Documentation updates
  • chore: Build or dependency updates
  • perf: Performance improvements
Examples:
feat: Add booking status update endpoint
fix: Resolve duplicate booking creation issue
refactor: Extract booking validation logic into separate module
docs: Update README with setup instructions
test: Add unit tests for BookingService
chore: Upgrade NestJS to v11.0.1

Branch Naming

Format: <type>/<short-description> Examples:
feat/booking-status-workflow
fix/duplicate-pet-creation
refactor/auth-guards
docs/api-documentation

Testing Standards

Unit Tests

Coverage Target: 80% minimum
describe('BookingsService', () => {
  describe('create', () => {
    it('should create a booking with valid data', async () => {
      // Arrange
      const createDto: CreateBookingDto = { /* ... */ };

      // Act
      const result = await service.create(createDto);

      // Assert
      expect(result).toBeDefined();
      expect(result.id).toBeDefined();
      expect(result.status).toBe('PENDING');
    });

    it('should throw error if no pets provided', async () => {
      // Arrange
      const createDto = { ...validDto, petIds: [] };

      // Act & Assert
      await expect(service.create(createDto)).rejects.toThrow(
        BadRequestException
      );
    });
  });
});

Performance Standards

Backend

  • API response time: < 300ms (p95)
  • Database queries with proper indexing
  • Implement pagination for list endpoints
  • Use select to avoid unnecessary fields
  • Connection pooling configured

Frontend

  • Lazy load components and routes
  • Image optimization with Next.js Image component
  • CSS-in-JS: Minimal runtime overhead
  • Code splitting by route
  • Minimize bundle size

Security Standards

Backend

  • HTTPS/TLS enforced in production
  • CORS configured for specific origins
  • Rate limiting on auth endpoints
  • SQL injection prevention via ORM
  • Password hashing with bcrypt
  • Secure session management

Frontend

  • CSP headers
  • XSS prevention through React escaping
  • CSRF tokens for state-changing operations
  • No sensitive data in localStorage
  • Secure cookie attributes (HttpOnly, Secure)

Accessibility Standards

WCAG 2.1 AA Compliance

  • Semantic HTML elements
  • ARIA labels for interactive components
  • Color contrast ratios 4.5:1 minimum
  • Keyboard navigation support
  • Form labels and error messages
  • Screen reader compatibility