Setup Completo - Backend
Guia completo para criar um projeto backend TypeScript + Fastify + Prisma do zero
Setup Completo - Backend IngenioLab
Guia completo para criar um projeto backend TypeScript + Fastify + Prisma do zero, seguindo os padrões da IngenioLab.
Índice
- Configuração Inicial do Projeto
- Configuração do TypeScript
- Setup do Servidor Fastify
- Configuração do Banco com Prisma
- Migrações do Banco
- Seeding do Banco
- Plugins do Fastify
- Sistema de Autenticação
- Sistema de Rotas
- Rotas de Autenticação
- Configuração de Testes
- Testando a API
1. Configuração Inicial do Projeto
1.1 Criar Diretório e Inicializar npm
mkdir meu-projeto-backendcd meu-projeto-backendnpm init -y
1.2 Instalar Dependências
# Dependências principaisnpm install fastifynpm install @fastify/cors @fastify/helmet @fastify/jwt @fastify/multipart @fastify/rate-limit @fastify/static @fastify/swaggernpm install @scalar/fastify-api-referencenpm install fastify-plugin fastify-type-provider-zodnpm install @prisma/client prismanpm install bcrypt dayjs dotenv uuid zodnpm install pino pino-pretty# Dependências de desenvolvimentonpm install -D typescript @types/node tsxnpm install -D @types/bcrypt @types/uuidnpm install -D vitest @types/supertest supertestnpm install -D @biomejs/biome
1.3 Criar Estrutura de Pastas
mkdir -p src/{routes,middlewares,plugins,services,utils,types,schemas}mkdir -p tests/{helpers,utils}mkdir -p prisma public scripts docs
2. Configuração do TypeScript
2.1 Criar tsconfig.json
{"compilerOptions": {"module": "ESNext","moduleResolution": "bundler","target": "ES2022","esModuleInterop": true,"allowSyntheticDefaultImports": true,"moduleDetection": "force","resolveJsonModule": true,"outDir": "./dist","rootDir": ".","declaration": true,"declarationMap": true,"sourceMap": true,"removeComments": true,"strict": true,"noUncheckedIndexedAccess": true,"exactOptionalPropertyTypes": true,"noImplicitReturns": true,"noFallthroughCasesInSwitch": true,"noImplicitOverride": true,"noPropertyAccessFromIndexSignature": true,"isolatedModules": true,"forceConsistentCasingInFileNames": true,"incremental": true,"skipLibCheck": true,"baseUrl": ".","paths": {"@/*": ["src/*"],"@/config/*": ["src/config/*"],"@/schemas/*": ["src/schemas/*"],"@/middlewares/*": ["src/middlewares/*"],"@/routes/*": ["src/routes/*"],"@/services/*": ["src/services/*"],"@/types/*": ["src/types/*"],"@/utils/*": ["src/utils/*"],"@/plugins/*": ["src/plugins/*"],"@/generated/*": ["prisma/generated/*"],"@/prisma/*": ["prisma/*"]}},"include": ["src/**/*", "prisma/**/*", "tests"],"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]}
2.2 Criar Tipagens de Environment
Criar src/types/env.d.ts:
declare namespace NodeJS {interface ProcessEnv {NODE_ENV: 'development' | 'production' | 'test';PORT: string;DATABASE_URL: string;JWT_SECRET: string;JWT_EXPIRES_IN: string;CORS_ORIGIN: string;ENABLE_TRACING: string;}}
2.3 Criar Extensões do Fastify
Criar src/types/fastify.d.ts:
import type { PrismaClient } from '@/prisma/generated/prisma';declare module 'fastify' {interface FastifyInstance {prisma: PrismaClient;authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;authorize: (roles: string[]) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;}interface FastifyRequest {startTime?: [number, number];user?: {id: string;email: string;role: string;};}}
3. Setup do Servidor Fastify
3.1 Criar Estrutura Básica da App
Criar src/app.ts:
import Fastify, { type FastifyInstance } from 'fastify';import {jsonSchemaTransform,serializerCompiler,validatorCompiler,} from 'fastify-type-provider-zod';export async function initializeApp(): Promise<FastifyInstance> {const app = Fastify({logger: {level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',transport: {target: 'pino-pretty',options: {colorize: true,translateTime: 'SYS:standard',ignore: 'pid,hostname',},},},});app.log.info('Iniciando aplicação...');app.log.info(`Ambiente: ${process.env.NODE_ENV || 'development'}`);// Configurar validação Zodapp.setValidatorCompiler(validatorCompiler);app.setSerializerCompiler(serializerCompiler);// Handler de erro básicoapp.setErrorHandler(async (error, request, reply) => {const statusCode = error.statusCode ?? 500;app.log.error({error: error.message,stack: error.stack,url: request.url,method: request.method,});return reply.status(statusCode).send({success: false,message: error.message || 'Erro interno do servidor',statusCode,...(process.env.NODE_ENV === 'development' && { stack: error.stack }),});});// Handler de rota não encontradaapp.setNotFoundHandler(async (_, reply) => {return reply.status(404).send({success: false,message: 'Rota não encontrada',statusCode: 404,});});return app;}
3.2 Criar Ponto de Entrada do Servidor
Criar src/server.ts:
import { initializeApp } from '@/app';async function main() {const host = process.env.HOST || '0.0.0.0';const port = Number.parseInt(process.env.PORT || '3333', 10);const app = await initializeApp();try {await app.listen({host,port,});app.log.info(`Servidor rodando em http://${host}:${port}`);} catch (error) {app.log.error('Falha ao iniciar servidor:', error);process.exit(1);}}const gracefulShutdown = async (signal: string) => {console.log(`\n🔄 Recebido ${signal}, encerrando graciosamente...`);process.exit(0);};process.on('SIGINT', () => gracefulShutdown('SIGINT'));process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));main();
3.3 Adicionar Scripts ao package.json
{"scripts": {"dev": "tsx watch src/server.ts","build": "tsc","start": "node dist/server.js","test": "vitest"}}
4. Configuração do Banco com Prisma
4.1 Inicializar Prisma
npx prisma init
4.2 Criar Schema Básico do Prisma
Criar prisma/schema.prisma:
generator client {provider = "prisma-client-js"output = "./generated/prisma"}datasource db {provider = "postgresql"url = env("DATABASE_URL")}enum UserRole {ADMINAGENTUSER}model User {id String @id @default(cuid())email String @uniquepassword Stringname Stringphone String?role UserRole @default(AGENT)createdAt DateTime @default(now())updatedAt DateTime @updatedAt@@map("users")}
4.3 Criar Plugin do Prisma
Criar src/plugins/prisma.plugin.ts:
import type { FastifyPluginAsync } from 'fastify';import fp from 'fastify-plugin';import { PrismaClient } from '@/prisma/generated/prisma';declare module 'fastify' {interface FastifyInstance {prisma: PrismaClient;}}const prismaPlugin: FastifyPluginAsync = async (app) => {const prisma = new PrismaClient({log:process.env.NODE_ENV === 'development'? ['query', 'error', 'warn']: ['error'],errorFormat: 'pretty',});app.log.info('Conectando ao Prisma...');try {await prisma.$connect();app.log.info('Prisma conectado com sucesso');} catch (error) {app.log.error('Falha ao conectar ao Prisma:', error);throw error;}app.decorate('prisma', prisma);app.addHook('onClose', async (app) => {await app.prisma.$disconnect();app.log.info('Prisma desconectado com sucesso');});};export default fp(prismaPlugin, {name: 'prisma-plugin',fastify: '5.x',});
4.4 Configuração do Environment
Criar .env:
# DatabaseDATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"# JWTJWT_SECRET="sua-chave-secreta-jwt-super-segura"JWT_EXPIRES_IN="7d"# ServerPORT=3333NODE_ENV="development"# CORSCORS_ORIGIN="http://localhost:3000"
5. Migrações do Banco
5.1 Gerar Cliente Prisma
npx prisma generate
5.2 Criar Primeira Migração
npx prisma migrate dev --name initial_migration
5.3 Adicionar Scripts de Migração ao package.json
{"scripts": {"db:generate": "prisma generate","db:migrate": "prisma migrate dev","db:studio": "prisma studio","db:reset": "prisma migrate reset"}}
6. Seeding do Banco
6.1 Criar Arquivo de Seed
Criar prisma/seed.ts:
import { PrismaClient, UserRole } from './generated/prisma';import bcrypt from 'bcrypt';const prisma = new PrismaClient();async function main() {console.log('🌱 Iniciando seeding do banco...');// Criar usuário adminconst adminPassword = await bcrypt.hash('admin123', 10);const admin = await prisma.user.upsert({where: { email: 'admin@ingeniolab.com.br' },update: {},create: {email: 'admin@ingeniolab.com.br',password: adminPassword,name: 'Admin IngenioLab',role: UserRole.ADMIN,},});// Criar agente de testeconst agentPassword = await bcrypt.hash('agent123', 10);const agent = await prisma.user.upsert({where: { email: 'agent@ingeniolab.com.br' },update: {},create: {email: 'agent@ingeniolab.com.br',password: agentPassword,name: 'Agente Teste',role: UserRole.AGENT,},});console.log('✅ Banco populado com sucesso');console.log(`Admin: ${admin.email}`);console.log(`Agente: ${agent.email}`);}main().catch((e) => {console.error('❌ Falha no seeding:', e);process.exit(1);}).finally(async () => {await prisma.$disconnect();});
6.2 Configurar Script de Seed
Adicionar ao package.json:
{"scripts": {"db:seed": "tsx prisma/seed.ts"},"prisma": {"seed": "tsx prisma/seed.ts"}}
6.3 Executar Seeding
npm run db:seed
7. Plugins do Fastify
7.1 Adicionar Plugins Principais à App
Atualizar src/app.ts para incluir plugins:
import fastifyCors from '@fastify/cors';import fastifyHelmet from '@fastify/helmet';import fastifyJwt from '@fastify/jwt';import fastifyMultipart from '@fastify/multipart';import fastifyRateLimit from '@fastify/rate-limit';import fastifyStatic from '@fastify/static';import fastifySwagger from '@fastify/swagger';import scalarFastifyApiReference from '@scalar/fastify-api-reference';import prismaPlugin from '@/plugins/prisma.plugin';import path from 'node:path';export async function initializeApp(): Promise<FastifyInstance> {// ... código existente ...// Registrar CORSawait app.register(fastifyCors, {origin: process.env.CORS_ORIGIN ?? '*',methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],allowedHeaders: ['Content-Type', 'Authorization'],credentials: true,});// Registrar plugins de segurançaapp.register(fastifyHelmet, { contentSecurityPolicy: false });await app.register(fastifyRateLimit, {max: 1000,timeWindow: '1 minute',});// Registrar JWTawait app.register(fastifyJwt, {secret: process.env.JWT_SECRET,sign: {expiresIn: process.env.JWT_EXPIRES_IN,},});// Registrar multipart (upload de arquivos)await app.register(fastifyMultipart, {limits: {fileSize: 10 * 1024 * 1024, // 10 MB},});// Registrar servir arquivos estáticosawait app.register(fastifyStatic, {root: path.resolve(__dirname, '../public'),prefix: '/public/',});// Registrar documentação Swaggerawait app.register(fastifySwagger, {openapi: {info: {title: 'API IngenioLab',description: 'Documentação da API backend',version: '1.0.0',},servers: [{url: process.env.NODE_ENV === 'production'? 'https://api.ingeniolab.com.br': 'http://localhost:3333',},],components: {securitySchemes: {BearerAuth: {type: 'http',scheme: 'bearer',bearerFormat: 'JWT',},},},},transform: jsonSchemaTransform,});// Registrar UI da documentaçãoawait app.register(scalarFastifyApiReference, {routePrefix: '/docs',configuration: {theme: 'kepler',},});// Registrar plugin do Prismaawait app.register(prismaPlugin);return app;}
8. Sistema de Autenticação
8.1 Criar Utilitários de Autenticação
Criar src/utils/auth-helpers.ts:
export const authConfig = {jwt: {secret: process.env.JWT_SECRET,expiresIn: process.env.JWT_EXPIRES_IN || '7d',},bcrypt: {saltRounds: 10,},};export interface JWTPayload {sub: string; // user idemail: string;role: string;iat?: number;exp?: number;}
8.2 Criar Middleware de Autenticação
Criar src/middlewares/auth.middleware.ts:
import type { FastifyRequest, FastifyReply } from 'fastify';import type { JWTPayload } from '@/utils/auth-helpers';export async function authenticate(request: FastifyRequest,reply: FastifyReply): Promise<void> {try {const token = await request.jwtVerify<JWTPayload>();// Adicionar info do usuário ao requestrequest.user = {id: token.sub,email: token.email,role: token.role,};} catch (error) {return reply.status(401).send({success: false,message: 'Token inválido ou expirado',statusCode: 401,});}}export function authorize(allowedRoles: string[]) {return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {if (!request.user) {return reply.status(401).send({success: false,message: 'Autenticação necessária',statusCode: 401,});}if (!allowedRoles.includes(request.user.role)) {return reply.status(403).send({success: false,message: 'Permissões insuficientes',statusCode: 403,});}};}
8.3 Registrar Middleware de Autenticação na App
Atualizar src/app.ts:
import { authenticate, authorize } from '@/middlewares/auth.middleware';export async function initializeApp(): Promise<FastifyInstance> {// ... configuração existente ...// Registrar middleware de authapp.decorate('authenticate', authenticate);app.decorate('authorize', authorize);// ... resto da configuração ...}
9. Sistema de Rotas
9.1 Criar Schemas de Resposta
Criar src/schemas/response-schema.ts:
import { z } from 'zod';export const SuccessResponseSchema = z.object({success: z.literal(true),data: z.any(),message: z.string().optional(),});export const ErrorResponseSchema = z.object({success: z.literal(false),message: z.string(),statusCode: z.number(),stack: z.string().optional(),});export const PaginatedResponseSchema = z.object({success: z.literal(true),data: z.array(z.any()),pagination: z.object({page: z.number(),limit: z.number(),total: z.number(),totalPages: z.number(),}),});
9.2 Criar Rota de Health (Exemplo)
Criar src/routes/health.route.ts:
import type { FastifyPluginAsync } from 'fastify';import { z } from 'zod';const HealthResponseSchema = z.object({status: z.string(),timestamp: z.string(),uptime: z.number(),environment: z.string(),version: z.string(),});export const healthRoutes: FastifyPluginAsync = async (app) => {app.get('/health',{schema: {description: 'Endpoint de verificação de saúde',tags: ['Health'],response: {200: HealthResponseSchema,},},},async (_, reply) => {return reply.send({status: 'OK',timestamp: new Date().toISOString(),uptime: Math.floor(process.uptime()),environment: process.env.NODE_ENV || 'development',version: '1.0.0',});});};
10. Rotas de Autenticação
10.1 Criar Schemas de Autenticação
Criar src/schemas/auth-schema.ts:
import { z } from 'zod';export const RegisterSchema = z.object({name: z.string().min(2, 'Nome deve ter pelo menos 2 caracteres'),email: z.string().email('Formato de email inválido'),password: z.string().min(8, 'Senha deve ter pelo menos 8 caracteres'),phone: z.string().optional(),});export const LoginSchema = z.object({email: z.string().email('Formato de email inválido'),password: z.string().min(1, 'Senha é obrigatória'),});export const AuthResponseSchema = z.object({success: z.literal(true),data: z.object({user: z.object({id: z.string(),email: z.string(),name: z.string(),role: z.string(),}),token: z.string(),}),});
10.2 Criar Serviço de Autenticação
Criar src/services/auth.service.ts:
import bcrypt from 'bcrypt';import type { PrismaClient, User } from '@/prisma/generated/prisma';import { authConfig } from '@/utils/auth-helpers';export class AuthService {constructor(private prisma: PrismaClient) {}async register(data: {name: string;email: string;password: string;phone?: string;}): Promise<User> {// Verificar se usuário já existeconst existingUser = await this.prisma.user.findUnique({where: { email: data.email },});if (existingUser) {throw new Error('Usuário já existe com este email');}// Hash da senhaconst hashedPassword = await bcrypt.hash(data.password,authConfig.bcrypt.saltRounds);// Criar usuárioreturn await this.prisma.user.create({data: {name: data.name,email: data.email,password: hashedPassword,phone: data.phone,},});}async login(email: string, password: string): Promise<User | null> {// Encontrar usuárioconst user = await this.prisma.user.findUnique({where: { email },});if (!user) {return null;}// Verificar senhaconst isValidPassword = await bcrypt.compare(password, user.password);if (!isValidPassword) {return null;}return user;}}
10.3 Criar Rotas de Autenticação
Criar src/routes/auth.route.ts:
import type { FastifyPluginAsync } from 'fastify';import { AuthService } from '@/services/auth.service';import {RegisterSchema,LoginSchema,AuthResponseSchema,} from '@/schemas/auth-schema';import { ErrorResponseSchema } from '@/schemas/response-schema';import { z } from 'zod';export const authRoutes: FastifyPluginAsync = async (app) => {const authService = new AuthService(app.prisma);// Endpoint de registroapp.post('/auth/register',{schema: {description: 'Registrar novo usuário',tags: ['Authentication'],body: RegisterSchema,response: {201: AuthResponseSchema,400: ErrorResponseSchema,409: ErrorResponseSchema,},},},async (request, reply) => {try {const userData = request.body;const user = await authService.register(userData);// Gerar token JWTconst token = await reply.jwtSign({sub: user.id,email: user.email,role: user.role,});return reply.status(201).send({success: true,data: {user: {id: user.id,email: user.email,name: user.name,role: user.role,},token,},});} catch (error) {const message = error instanceof Error ? error.message : 'Falha no registro';const statusCode = message.includes('já existe') ? 409 : 400;return reply.status(statusCode).send({success: false,message,statusCode,});}});// Endpoint de loginapp.post('/auth/login',{schema: {description: 'Login com email e senha',tags: ['Authentication'],body: LoginSchema,response: {200: AuthResponseSchema,401: ErrorResponseSchema,},},},async (request, reply) => {try {const { email, password } = request.body;const user = await authService.login(email, password);if (!user) {return reply.status(401).send({success: false,message: 'Email ou senha inválidos',statusCode: 401,});}// Gerar token JWTconst token = await reply.jwtSign({sub: user.id,email: user.email,role: user.role,});return reply.send({success: true,data: {user: {id: user.id,email: user.email,name: user.name,role: user.role,},token,},});} catch (error) {app.log.error('Erro no login:', error);return reply.status(500).send({success: false,message: 'Falha no login',statusCode: 500,});}});// Exemplo de rota protegidaapp.get('/auth/profile',{preHandler: [app.authenticate],schema: {description: 'Obter perfil do usuário atual',tags: ['Authentication'],security: [{ BearerAuth: [] }],response: {200: z.object({success: z.literal(true),data: z.object({id: z.string(),email: z.string(),name: z.string(),role: z.string(),}),}),401: ErrorResponseSchema,},},},async (request, reply) => {const user = await app.prisma.user.findUnique({where: { id: request.user!.id },select: {id: true,email: true,name: true,role: true,},});return reply.send({success: true,data: user,});});};
10.4 Registrar Rotas de Autenticação
Atualizar src/app.ts:
import { authRoutes } from '@/routes/auth.route';import { healthRoutes } from '@/routes/health.route';export async function initializeApp(): Promise<FastifyInstance> {// ... configuração existente ...// Registrar rotasawait app.register(authRoutes, { prefix: '/api' });await app.register(healthRoutes, { prefix: '/api' });return app;}
11. Configuração de Testes
11.1 Criar Configuração do Vitest
Criar vitest.config.ts:
/// <reference types="vitest" />import { resolve } from 'path';import { defineConfig } from 'vitest/config';export default defineConfig({test: {globals: true,environment: 'node',setupFiles: ['./tests/setup.ts'],include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],exclude: ['node_modules', 'dist', '.idea', '.git', '.cache', 'coverage'],testTimeout: 30000,pool: 'threads',poolOptions: {threads: {singleThread: true,},},},resolve: {alias: {'@': resolve(__dirname, './src'),},},});
11.2 Criar Setup de Teste
Criar tests/setup.ts:
import { beforeAll, afterAll } from 'vitest';import { exec } from 'child_process';import { promisify } from 'util';const execAsync = promisify(exec);beforeAll(async () => {// Configurar ambiente de testeprocess.env.NODE_ENV = 'test';process.env.DATABASE_URL = 'postgresql://user:password@localhost:5432/test_db?schema=public';// Executar migraçõesawait execAsync('npx prisma migrate reset --force --skip-seed');await execAsync('npx prisma migrate deploy');});afterAll(async () => {// Limpeza pode ser adicionada aqui se necessário});
11.3 Criar Helper de Teste
Criar tests/helpers/test-app.ts:
import { initializeApp } from '@/app';import type { FastifyInstance } from 'fastify';let app: FastifyInstance;export async function createTestApp(): Promise<FastifyInstance> {if (app) {return app;}app = await initializeApp();await app.ready();return app;}export async function closeTestApp(): Promise<void> {if (app) {await app.close();}}
11.4 Criar Teste de Exemplo
Criar src/routes/auth.route.test.ts:
import { describe, it, expect, beforeAll, afterAll } from 'vitest';import { createTestApp, closeTestApp } from '@/../tests/helpers/test-app';import type { FastifyInstance } from 'fastify';describe('Rotas de Autenticação', () => {let app: FastifyInstance;beforeAll(async () => {app = await createTestApp();});afterAll(async () => {await closeTestApp();});describe('POST /api/auth/register', () => {it('deve registrar um novo usuário com sucesso', async () => {const response = await app.inject({method: 'POST',url: '/api/auth/register',payload: {name: 'Usuário Teste',email: 'teste@ingeniolab.com.br',password: 'senha123456',},});expect(response.statusCode).toBe(201);const body = JSON.parse(response.body);expect(body.success).toBe(true);expect(body.data.user.email).toBe('teste@ingeniolab.com.br');expect(body.data.token).toBeDefined();});it('deve retornar erro para email duplicado', async () => {// Primeiro registroawait app.inject({method: 'POST',url: '/api/auth/register',payload: {name: 'Usuário Teste',email: 'duplicado@ingeniolab.com.br',password: 'senha123456',},});// Segundo registro com mesmo emailconst response = await app.inject({method: 'POST',url: '/api/auth/register',payload: {name: 'Usuário Teste 2',email: 'duplicado@ingeniolab.com.br',password: 'senha123456',},});expect(response.statusCode).toBe(409);const body = JSON.parse(response.body);expect(body.success).toBe(false);expect(body.message).toContain('já existe');});});describe('POST /api/auth/login', () => {beforeAll(async () => {// Criar usuário de testeawait app.inject({method: 'POST',url: '/api/auth/register',payload: {name: 'Usuário Login',email: 'login@ingeniolab.com.br',password: 'senha123456',},});});it('deve fazer login com credenciais válidas', async () => {const response = await app.inject({method: 'POST',url: '/api/auth/login',payload: {email: 'login@ingeniolab.com.br',password: 'senha123456',},});expect(response.statusCode).toBe(200);const body = JSON.parse(response.body);expect(body.success).toBe(true);expect(body.data.user.email).toBe('login@ingeniolab.com.br');expect(body.data.token).toBeDefined();});it('deve retornar erro para credenciais inválidas', async () => {const response = await app.inject({method: 'POST',url: '/api/auth/login',payload: {email: 'login@ingeniolab.com.br',password: 'senhaerrada',},});expect(response.statusCode).toBe(401);const body = JSON.parse(response.body);expect(body.success).toBe(false);expect(body.message).toBe('Email ou senha inválidos');});});});
12. Testando a API
12.1 Atualizar package.json com Todos os Scripts
{"scripts": {"dev": "tsx watch src/server.ts","build": "tsc","start": "node dist/server.js","db:generate": "prisma generate","db:migrate": "prisma migrate dev","db:studio": "prisma studio","db:seed": "tsx prisma/seed.ts","test": "vitest","test:watch": "vitest --watch","test:coverage": "vitest --coverage"}}
12.2 Executar Setup Completo
# Instalar dependênciasnpm install# Gerar cliente Prismanpm run db:generate# Executar primeira migraçãonpm run db:migrate# Popular banco de dadosnpm run db:seed# Iniciar servidor de desenvolvimentonpm run dev
12.3 Testar Endpoints
# Verificação de saúdecurl http://localhost:3333/api/health# Registrar usuáriocurl -X POST http://localhost:3333/api/auth/register \-H "Content-Type: application/json" \-d '{"name":"Teste IngenioLab","email":"teste@ingeniolab.com.br","password":"senha123456"}'# Fazer logincurl -X POST http://localhost:3333/api/auth/login \-H "Content-Type: application/json" \-d '{"email":"teste@ingeniolab.com.br","password":"senha123456"}'# Testar rota protegida (substitua SEU_TOKEN pelo token recebido no login)curl http://localhost:3333/api/auth/profile \-H "Authorization: Bearer SEU_TOKEN"
12.4 Executar Testes
# Executar todos os testesnpm run test# Executar testes em modo watchnpm run test:watch# Executar testes com coberturanpm run test:coverage
12.5 Visualizar Documentação
Abra http://localhost:3333/docs para ver a documentação interativa da API.
12.6 Visualizar Banco de Dados
# Abrir Prisma Studionpm run db:studio
Próximos Passos
Com este setup completo, você tem uma base sólida para desenvolver APIs backend seguindo os padrões da IngenioLab. Você pode expandir esta base adicionando:
- Mais rotas e serviços
- Validações mais complexas
- Upload de arquivos
- Integração com serviços externos
- Caching com Redis
- Filas de background jobs
- Monitoramento e métricas
Estrutura Final do Projeto
meu-projeto-backend/├── prisma/│ ├── schema.prisma│ ├── seed.ts│ └── generated/├── src/│ ├── app.ts│ ├── server.ts│ ├── middlewares/│ │ └── auth.middleware.ts│ ├── plugins/│ │ └── prisma.plugin.ts│ ├── routes/│ │ ├── auth.route.ts│ │ ├── auth.route.test.ts│ │ └── health.route.ts│ ├── schemas/│ │ ├── auth-schema.ts│ │ └── response-schema.ts│ ├── services/│ │ └── auth.service.ts│ ├── types/│ │ ├── env.d.ts│ │ └── fastify.d.ts│ └── utils/│ └── auth-helpers.ts├── tests/│ ├── setup.ts│ └── helpers/│ └── test-app.ts├── .env├── package.json├── tsconfig.json└── vitest.config.ts
Esta estrutura fornece uma base robusta e escalável para qualquer projeto backend da IngenioLab.