Complete CRUD
Let's implement a complete CRUD with pagination, filters and sorting.
Product Service
typescript
// src/services/product.service.tsimport { prisma } from '../config/database';import { AppError } from '../errors/AppError';import { Prisma } from '@prisma/client'; interface FindAllParams { page?: number; limit?: number; search?: string; categoryId?: string; sortBy?: string; order?: 'asc' | 'desc';} export class ProductService { async findAll(params: FindAllParams) { const { page = 1, limit = 20, search, categoryId, sortBy = 'createdAt', order = 'desc' } = params; const where: Prisma.ProductWhereInput = {}; if (search) { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, { description: { contains: search, mode: 'insensitive' } }, ]; } if (categoryId) { where.categoryId = categoryId; } const skip = (page - 1) * limit; const [products, total] = await Promise.all([ prisma.product.findMany({ where, skip, take: limit, orderBy: { [sortBy]: order }, include: { category: true }, }), prisma.product.count({ where }), ]); return { data: products, pagination: { page, limit, total, totalPages: Math.ceil(total / limit), }, }; } async findById(id: string) { const product = await prisma.product.findUnique({ where: { id }, include: { category: true }, }); if (!product) { throw new AppError('Product not found', 404); } return product; } async create(data: CreateProductDTO) { const slug = data.name.toLowerCase().replace(/\s+/g, '-'); const exists = await prisma.product.findUnique({ where: { slug } }); if (exists) { throw new AppError('Product already exists', 409); } return prisma.product.create({ data: { ...data, slug }, include: { category: true }, }); } async update(id: string, data: UpdateProductDTO) { await this.findById(id); return prisma.product.update({ where: { id }, data, include: { category: true }, }); } async delete(id: string) { await this.findById(id); return prisma.product.delete({ where: { id } }); }}Controller
typescript
// src/controllers/product.controller.tsexport class ProductController { async index(req: Request, res: Response) { const { page, limit, search, categoryId, sortBy, order } = req.query; const result = await productService.findAll({ page: Number(page) || 1, limit: Number(limit) || 20, search: search as string, categoryId: categoryId as string, sortBy: sortBy as string, order: order as 'asc' | 'desc', }); return res.json(result); } async show(req: Request, res: Response) { const product = await productService.findById(req.params.id); return res.json(product); } async store(req: Request, res: Response) { const product = await productService.create(req.body); return res.status(201).json(product); } async update(req: Request, res: Response) { const product = await productService.update(req.params.id, req.body); return res.json(product); } async destroy(req: Request, res: Response) { await productService.delete(req.params.id); return res.status(204).send(); }}Testing Endpoints
bash
# List with paginationGET /api/products?page=1&limit=10 # SearchGET /api/products?search=iphone # Filter by categoryGET /api/products?categoryId=abc-123 # SortGET /api/products?sortBy=price&order=ascSummary
- ✅ CRUD with Prisma
- ✅ Pagination
- ✅ Text search
- ✅ Filters and sorting
Next lesson: Database Relationships! 🚀