Full Stack JS

Chapter 14
0%

Chapter 14: REST API with Sequelize

Bring it all together — build a professional REST API with Express routes, Sequelize models, pagination, filtering, and proper error handling.

CRUD Review
Controllers
Services
Pagination & Filtering
Error Handling
Quiz

CRUD Review

Remember our restaurant? Now we're building the entire kitchen system. The controller is the waiter (takes orders, delivers food). The service is the chef (does the actual cooking/logic). The model is the recipe book (defines what each dish looks like). They work together to handle every customer request.

Here's how CRUD maps to HTTP methods and Sequelize operations:

OperationHTTPRouteSequelizeRestaurant
CreatePOST/api/tasksTask.create()Place a new order
Read AllGET/api/tasksTask.findAll()See the full menu
Read OneGET/api/tasks/:idTask.findByPk()Ask about a specific dish
UpdatePUT/api/tasks/:idtask.update()Change your order
DeleteDELETE/api/tasks/:idtask.destroy()Cancel your order

Project Structure

Project — Backend API Structure

Controllers

The controller is the waiter. It takes the customer's request (req), calls the kitchen (service), and delivers the response (res). The waiter doesn't cook — they just coordinate between the customer and the kitchen.
controllers/taskController.js

Routes — Connecting URLs to Controllers

routes/taskRoutes.js
Notice the clean separation: Routes just map URLs to controller functions. Controllers extract data from requests and call services. This makes each piece easy to test and modify independently.

Services

The service is the chef. It contains the actual business logic — the "how to cook the dish." The service talks to the database (model) and returns the result to the controller (waiter). The chef doesn't care about the customer (req/res) — they only care about cooking (data operations).
services/taskService.js
Always filter by userId! Notice every query includes where: { id, userId }. This ensures users can only access their OWN tasks. Without this check, any user could read/edit/delete anyone's data — a major security hole.

Pagination & Filtering

Imagine a library with 10,000 books. You wouldn't dump all 10,000 on the floor — you'd show them page by page (pagination) and let users search by genre or author (filtering). The same principle applies to APIs — don't send all records at once, let clients request specific pages and filters.

How Pagination Works

Pagination — How It Works

Filtering and Sorting

Sequelize — Filtering & Sorting

Sequelize Operators Reference

OperatorMeaningExample
Op.eqEqual{ age: { [Op.eq]: 25 } }
Op.neNot equal{ status: { [Op.ne]: 'deleted' } }
Op.gtGreater than{ age: { [Op.gt]: 18 } }
Op.gteGreater than or equal{ price: { [Op.gte]: 10 } }
Op.ltLess than{ age: { [Op.lt]: 65 } }
Op.likePattern match{ name: { [Op.like]: '%raj%' } }
Op.iLikeCase-insensitive match{ name: { [Op.iLike]: '%raj%' } }
Op.inIn a list{ id: { [Op.in]: [1, 2, 3] } }
Op.orOR condition{ [Op.or]: [{...}, {...}] }

Error Handling

Good error handling is like a good hospital. When something goes wrong, you want clear diagnosis (error type), helpful treatment (error message), and proper records (logging). Bad error handling is like a doctor saying "something is wrong" with no further details — useless!

Custom Error Class

utils/AppError.js — Custom Error Class

Centralized Error Handler

middleware/errorHandler.js
Error handling best practices:
1. Use custom error classes (AppError) for expected errors
2. Handle Sequelize-specific errors (validation, unique constraint)
3. Never expose stack traces in production
4. Always log errors server-side for debugging
5. Return helpful, specific messages to the client

📝 Chapter 14 Quiz

1. What's the difference between a controller and a service?

Controllers are for GET, services are for POST
Controllers handle req/res, services handle business logic
There is no difference
Services are for frontend, controllers for backend

2. For page 3 with 10 items per page, what is the offset?

3
10
20
30

3. Which operator performs case-insensitive search in Sequelize?

Op.iLike
Op.search
Op.contains
Op.match

4. Why should service methods always filter by userId?

To make queries faster
To sort the results
It's not necessary, it's optional
To ensure users only access their own data (security)

5. What does Task.findAndCountAll() return?

Just the tasks array
Both the tasks (rows) and total count
Only the count of matching tasks
The first matching task
← Chapter 13: Sequelize ORM Chapter 15: Authentication →