smithery/feraudet

api-conventions

Standards pour la conception d'API REST. Use when "api", "endpoint", "route", "rest api", "http".

Installation

$ npx skills add smithery/feraudet --skill api-conventions

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/feraudet.

npx skills add smithery/feraudet

Browse all from smithery/feraudet

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsRead, Grep, Glob

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,626 B
  • docs SUMMARY.md 120 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

API Conventions

Purpose

Standards pour développer les API REST dans le projet consultant-manager.

Principes REST

Ressources

  • Consultant: /api/consultants
  • Mission: /api/missions
  • Dashboard: /api/dashboard

Méthodes HTTP

  • GET - Récupérer des données (lecture)
  • POST - Créer une nouvelle ressource
  • PUT - Modifier une ressource existante (remplacer)
  • PATCH - Modifier partiellement une ressource
  • DELETE - Supprimer une ressource

URLs

GET    /api/consultants           # Liste
GET    /api/consultants/:id       # Détail
POST   /api/consultants           # Créer
PUT    /api/consultants/:id       # Modifier
DELETE /api/consultants/:id       # Supprimer

GET    /api/missions
GET    /api/missions/:id
POST   /api/missions
PUT    /api/missions/:id
DELETE /api/missions/:id
GET    /api/missions/timeline     # Endpoint spécial

GET    /api/dashboard/stats       # Endpoint agrégé

Status Codes HTTP

Success

  • 200 OK - GET, PUT réussis
  • 201 Created - POST réussi
  • 204 No Content - DELETE réussi

Client Errors

  • 400 Bad Request - Données invalides
  • 401 Unauthorized - Non authentifié
  • 403 Forbidden - Non autorisé
  • 404 Not Found - Ressource introuvable
  • 409 Conflict - Conflit (ex: email déjà utilisé)
  • 422 Unprocessable Entity - Validation échouée

Server Errors

  • 500 Internal Server Error - Erreur serveur

Format de Réponse

Success Response

// GET /api/consultants/:id
{
  "id": "uuid",
  "nom": "Dupont",
  "prenom": "Jean",
  "email": "[email protected]",
  "telephone": "+33612345678",
  "competences": ["React", "TypeScript"],
  "tjm": 500,
  "statut": "EN_MISSION",
  "dateCreation": "2026-01-15T10:00:00.000Z",
  "dateModification": "2026-01-15T10:00:00.000Z"
}

Error Response

{
  "error": "Message d'erreur lisible",
  "details": [
    {
      "field": "email",
      "message": "Email invalide"
    }
  ]
}

Liste Paginée

{
  "data": [...],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "total": 150,
    "totalPages": 8
  }
}

Query Parameters

Filtres

GET /api/consultants?statut=DISPONIBLE
GET /api/consultants?search=dupont
GET /api/missions?consultantId=uuid
GET /api/missions?dateDebut=2026-01-01&dateFin=2026-12-31

Tri

GET /api/consultants?sortBy=nom&order=asc
GET /api/missions?sortBy=dateDebut&order=desc

Pagination

GET /api/consultants?page=2&pageSize=20

Include Relations

GET /api/consultants/:id?include=missions

Validation

Backend avec Zod

import { z } from 'zod';

const consultantSchema = z.object({
  nom: z.string().min(1, 'Nom requis'),
  prenom: z.string().min(1, 'Prénom requis'),
  email: z.string().email('Email invalide'),
  telephone: z.string().optional(),
  competences: z.array(z.string()).min(1, 'Au moins une compétence'),
  tjm: z.number().positive('TJM doit être positif'),
  statut: z.enum(['DISPONIBLE', 'EN_MISSION', 'EN_CONGES', 'INDISPONIBLE']).optional()
});

export const createConsultant = async (req: Request, res: Response) => {
  try {
    const validatedData = consultantSchema.parse(req.body);
    const consultant = await prisma.consultant.create({
      data: {
        ...validatedData,
        competences: JSON.stringify(validatedData.competences)
      }
    });
    res.status(201).json(consultant);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({
        error: 'Validation error',
        details: error.errors
      });
    }
    res.status(500).json({ error: 'Internal server error' });
  }
};

Gestion d'Erreurs

Error Handler Middleware

// Express error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);

  // Prisma errors
  if (err.code === 'P2002') {
    return res.status(409).json({
      error: 'Unique constraint violation',
      field: err.meta?.target
    });
  }

  if (err.code === 'P2025') {
    return res.status(404).json({
      error: 'Resource not found'
    });
  }

  // Zod validation errors
  if (err instanceof z.ZodError) {
    return res.status(400).json({
      error: 'Validation error',
      details: err.errors
    });
  }

  // Generic error
  res.status(500).json({
    error: process.env.NODE_ENV === 'production'
      ? 'Internal server error'
      : err.message
  });
});

Try-Catch Pattern

export const getConsultant = async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    const consultant = await prisma.consultant.findUnique({
      where: { id },
      include: { missions: true }
    });

    if (!consultant) {
      return res.status(404).json({ error: 'Consultant not found' });
    }

    res.json(consultant);
  } catch (error) {
    console.error('Error fetching consultant:', error);
    res.status(500).json({ error: 'Failed to fetch consultant' });
  }
};

Dates

Format ISO 8601

{
  "dateDebut": "2026-01-15T00:00:00.000Z",
  "dateFin": "2026-06-30T23:59:59.999Z"
}

Parsing

// Backend: convertir string en Date
const dateDebut = new Date(req.body.dateDebut);

// Valider avec Zod
const missionSchema = z.object({
  dateDebut: z.string().datetime(),
  dateFin: z.string().datetime()
}).refine(data => {
  return new Date(data.dateFin) > new Date(data.dateDebut);
}, {
  message: 'dateFin must be after dateDebut'
});

CORS

Configuration

import cors from 'cors';

// Development: permissif
app.use(cors());

// Production: restrictif
app.use(cors({
  origin: process.env.FRONTEND_URL || 'http://localhost:5173',
  credentials: true
}));

Sécurité

Validation Stricte

// ✅ Valider toutes les entrées
const validatedData = schema.parse(req.body);

// ❌ Utiliser directement req.body
await prisma.consultant.create({ data: req.body });

Rate Limiting

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use('/api/', limiter);

Helmet (Headers de sécurité)

import helmet from 'helmet';

app.use(helmet());

Documentation

JSDoc sur Routes

/**
 * GET /api/consultants
 *
 * Liste tous les consultants avec filtres optionnels
 *
 * Query params:
 * - statut (optional): DISPONIBLE | EN_MISSION | EN_CONGES | INDISPONIBLE
 * - search (optional): Recherche par nom, prénom, email
 *
 * Returns: Consultant[]
 */
export const getAllConsultants = async (req: Request, res: Response) => {
  // ...
};

Swagger/OpenAPI (Futur)

paths:
  /api/consultants:
    get:
      summary: Liste des consultants
      parameters:
        - name: statut
          in: query
          schema:
            type: string
            enum: [DISPONIBLE, EN_MISSION, EN_CONGES, INDISPONIBLE]
      responses:
        200:
          description: Liste des consultants
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Consultant'

Patterns Spécifiques au Projet

Calculs Côté Backend

// ✅ Calculs métier dans l'API
export const getMission = async (req: Request, res: Response) => {
  const mission = await prisma.mission.findUnique({ where: { id } });

  res.json({
    ...mission,
    revenusGeneres: calculateRevenue(mission.tjm, mission.dateDebut, mission.dateFin),
    dureeJours: calculateDuration(mission.dateDebut, mission.dateFin)
  });
};

// ❌ Laisser le calcul au frontend
// Frontend ne devrait PAS calculer les revenus

JSON Fields

// Consultant.competences est stocké en JSON string
export const getConsultant = async (req: Request, res: Response) => {
  const consultant = await prisma.consultant.findUnique({ where: { id } });

  res.json({
    ...consultant,
    competences: JSON.parse(consultant.competences) // Convertir en array
  });
};

Statut Calculé

// Calculer le statut réel basé sur missions actives
export const getAllConsultants = async (req: Request, res: Response) => {
  const consultants = await prisma.consultant.findMany({
    include: { missions: true }
  });

  const consultantsWithStatus = consultants.map(consultant => ({
    ...consultant,
    statut: calculateConsultantStatus(consultant) // Fonction utilitaire
  }));

  res.json(consultantsWithStatus);
};

Testing API

Avec curl

# GET
curl http://localhost:3000/api/consultants

# POST
curl -X POST http://localhost:3000/api/consultants \
  -H "Content-Type: application/json" \
  -d '{
    "nom": "Dupont",
    "prenom": "Jean",
    "email": "[email protected]",
    "competences": ["React"],
    "tjm": 500
  }'

# DELETE
curl -X DELETE http://localhost:3000/api/consultants/uuid

Avec Postman/Insomnia

Créer une collection avec tous les endpoints du projet.

Tests d'Intégration

import request from 'supertest';
import { app } from '../src/index';

describe('Consultants API', () => {
  it('should create a consultant', async () => {
    const response = await request(app)
      .post('/api/consultants')
      .send({
        nom: 'Test',
        prenom: 'User',
        email: '[email protected]',
        competences: ['React'],
        tjm: 500
      })
      .expect(201);

    expect(response.body).toHaveProperty('id');
    expect(response.body.email).toBe('[email protected]');
  });
});

Checklist API

Avant de commiter une nouvelle API:

  • Routes RESTful (GET, POST, PUT, DELETE)
  • Validation Zod sur toutes les entrées
  • Status codes HTTP appropriés
  • Gestion d'erreurs avec try-catch
  • Réponses JSON consistantes
  • CORS configuré
  • Dates en ISO 8601
  • Calculs métier côté backend
  • Documentation JSDoc
  • Tests d'intégration
  • Pas de données sensibles dans les logs

Ressources