Proyecto E-commerce - Parte 1
Vamos a construir la API completa del e-commerce. Esta primera parte cubre la estructura inicial y módulo de autenticación.
Estructura del Proyecto
ecommerce-api/
├── prisma/
│ └── schema.prisma
├── src/
│ ├── @types/
│ │ └── express.d.ts
│ ├── config/
│ │ ├── auth.ts
│ │ ├── database.ts
│ │ ├── redis.ts
│ │ └── upload.ts
│ ├── controllers/
│ │ ├── auth.controller.ts
│ │ ├── product.controller.ts
│ │ ├── category.controller.ts
│ │ ├── order.controller.ts
│ │ └── user.controller.ts
│ ├── errors/
│ │ └── AppError.ts
│ ├── middlewares/
│ │ ├── auth.middleware.ts
│ │ ├── authorize.middleware.ts
│ │ ├── error.middleware.ts
│ │ └── validate.middleware.ts
│ ├── routes/
│ │ ├── index.ts
│ │ ├── auth.routes.ts
│ │ ├── product.routes.ts
│ │ ├── category.routes.ts
│ │ ├── order.routes.ts
│ │ └── user.routes.ts
│ ├── schemas/
│ │ ├── auth.schema.ts
│ │ ├── product.schema.ts
│ │ └── order.schema.ts
│ ├── services/
│ │ ├── auth.service.ts
│ │ ├── product.service.ts
│ │ ├── category.service.ts
│ │ ├── order.service.ts
│ │ └── mail.service.ts
│ ├── utils/
│ │ └── async-handler.ts
│ ├── app.ts
│ └── server.ts
├── .env
├── .env.example
├── docker-compose.yml
├── package.json
└── tsconfig.json
Inicialización
bash
mkdir ecommerce-api && cd ecommerce-apinpm init -ynpm install express cors helmet compression dotenvnpm install @prisma/client ioredis jsonwebtoken bcryptjs zodnpm install -D typescript ts-node-dev @types/node @types/expressnpm install -D @types/cors @types/jsonwebtoken @types/bcryptjsnpm install -D prismanpx tsc --initnpx prisma initApp Principal
typescript
// src/app.tsimport express from 'express';import cors from 'cors';import helmet from 'helmet';import compression from 'compression';import { routes } from './routes';import { errorHandler } from './middlewares/error.middleware'; const app = express(); // Middlewaresapp.use(helmet());app.use(cors());app.use(compression());app.use(express.json()); // Rutasapp.use('/api', routes); // Error handlerapp.use(errorHandler); export { app };Servidor
typescript
// src/server.tsimport 'dotenv/config';import { app } from './app';import { prisma } from './config/database'; const PORT = process.env.PORT || 3000; async function bootstrap() { await prisma.$connect(); console.log('📦 Base de datos conectada'); app.listen(PORT, () => { console.log(`🚀 Servidor en http://localhost:${PORT}`); });} bootstrap().catch(console.error);Módulo de Auth
typescript
// src/services/auth.service.tsimport bcrypt from 'bcryptjs';import jwt from 'jsonwebtoken';import { prisma } from '../config/database';import { AppError } from '../errors/AppError'; export class AuthService { async register(data: RegisterDTO) { const exists = await prisma.user.findUnique({ where: { email: data.email }, }); if (exists) { throw new AppError('Email ya registrado', 409); } const user = await prisma.user.create({ data: { ...data, password: await bcrypt.hash(data.password, 10), }, select: { id: true, name: true, email: true, role: true }, }); return { user, token: this.generateToken(user.id), }; } async login(email: string, password: string) { const user = await prisma.user.findUnique({ where: { email } }); if (!user || !(await bcrypt.compare(password, user.password))) { throw new AppError('Credenciales inválidas', 401); } return { user: { id: user.id, name: user.name, email: user.email, role: user.role }, token: this.generateToken(user.id), }; } private generateToken(userId: string) { return jwt.sign({ sub: userId }, process.env.JWT_SECRET!, { expiresIn: '7d', }); }}Controller de Auth
typescript
// src/controllers/auth.controller.tsimport { Request, Response } from 'express';import { AuthService } from '../services/auth.service'; const authService = new AuthService(); export class AuthController { async register(req: Request, res: Response) { const result = await authService.register(req.body); return res.status(201).json(result); } async login(req: Request, res: Response) { const { email, password } = req.body; const result = await authService.login(email, password); return res.json(result); } async me(req: Request, res: Response) { return res.json(req.user); }}Schemas de Validación
typescript
// src/schemas/auth.schema.tsimport { z } from 'zod'; export const registerSchema = z.object({ body: z.object({ name: z.string().min(2).max(100), email: z.string().email(), password: z.string().min(6), }),}); export const loginSchema = z.object({ body: z.object({ email: z.string().email(), password: z.string().min(1), }),});Rutas de Auth
typescript
// src/routes/auth.routes.tsimport { Router } from 'express';import { AuthController } from '../controllers/auth.controller';import { validate } from '../middlewares/validate.middleware';import { authenticate } from '../middlewares/auth.middleware';import { registerSchema, loginSchema } from '../schemas/auth.schema'; const router = Router();const controller = new AuthController(); router.post('/register', validate(registerSchema), controller.register);router.post('/login', validate(loginSchema), controller.login);router.get('/me', authenticate, controller.me); export { router as authRoutes };Resumen Parte 1
- ✅ Estructura del proyecto
- ✅ Configuración inicial
- ✅ Módulo de autenticación completo
- ✅ Validación con Zod
Próxima parte: Módulo de Productos y CategorÃas! 🚀