REST vs MVC & Framework Patterns

How REST APIs and MVC relate, and how modern frameworks implement these patterns

REST and MVC: Complementary, Not Competing

A common misconception is that REST and MVC are competing patterns. They are not — they operate at different layers. REST defines how clients talk to your server (the API interface). MVC defines how your server is organized internally (the code structure).

REST is the Interface, MVC is the Organization:

  Client                     REST API                    MVC Backend              Database
+----------+            +--------------+           +------------------+      +-----------+
|          |            |              |           |                  |      |           |
| Browser  |  HTTP      |  /api/users  |           |   Controller     |      |           |
| Mobile   | ---------> |  /api/items  | --------> |       |          |      | PostgreSQL|
| Another  |            |  /api/orders |           |       v          |      | MongoDB   |
| Service  | <--------- |              | <-------- |   Model --> View | <--> | MySQL     |
|          |  JSON/HTML |  Defines:    |           |                  |      |           |
+----------+            |  - URLs      |           |   Organizes:     |      +-----------+
                        |  - Methods   |           |   - Data logic   |
                        |  - Status    |           |   - Presentation |
                        |    codes     |           |   - Flow control |
                        +--------------+           +------------------+

Think of it this way:

  • REST is like the menu at a restaurant — it defines what you can order and how to ask for it
  • MVC is the kitchen organization — how the chefs, prep cooks, and plating stations are arranged to fulfill those orders

You can have REST without MVC (a single-file API script), MVC without REST (a server-rendered app with no API), or both together (which is the most common real-world pattern).

MVC in Popular Frameworks

Every major web framework organizes code using MVC (or something very close to it). The concepts are the same; only the file names and conventions differ:

Framework Routes Controller Model View / Template
Express (Node.js) routes/users.js controllers/userController.js models/User.js views/users/index.ejs
Laravel (PHP) routes/web.php app/Http/Controllers/UserController.php app/Models/User.php resources/views/users/index.blade.php
Django (Python) urls.py views.py models.py templates/users/index.html
Rails (Ruby) config/routes.rb app/controllers/users_controller.rb app/models/user.rb app/views/users/index.html.erb

The Route-Handler-Model Pattern

Regardless of the framework, the flow is always the same: a route maps a URL to a handler (controller), which calls the model for data, then returns a response. Here is the pattern in each major framework:

Express (Node.js)

// routes/users.js router.get('/', (req, res) => controller.index(req, res)); router.post('/', (req, res) => controller.store(req, res)); // controllers/userController.js async index(req, res) { const users = await this.UserModel.findAll(); res.render('users/index', { users }); } // models/User.js async findAll() { const result = await this.pool.query('SELECT * FROM users'); return result.rows; }

Laravel (PHP)

// routes/web.php Route::get('/users', [UserController::class, 'index']); Route::post('/users', [UserController::class, 'store']); // app/Http/Controllers/UserController.php public function index() { $users = User::all(); return view('users.index', compact('users')); } // app/Models/User.php // Eloquent ORM - User::all() generates SELECT * FROM users

Django (Python)

# urls.py urlpatterns = [ path('users/', views.user_list, name='user_list'), path('users/create/', views.user_create, name='user_create'), ] # views.py (this is the "controller" in Django) def user_list(request): users = User.objects.all() return render(request, 'users/index.html', {'users': users}) # models.py class User(models.Model): name = models.CharField(max_length=100) email = models.EmailField()

Rails (Ruby)

# config/routes.rb resources :users # app/controllers/users_controller.rb def index @users = User.all end # app/models/user.rb class User < ApplicationRecord validates :name, presence: true end

The file names and syntax differ, but the architecture is identical: Route → Controller → Model → View.

Choosing Your Architecture

With a clear understanding of REST and MVC as separate, complementary layers, here is a summary of how to choose your approach:

Concept Key Points
MVC A pattern that separates an application into Model (data), View (presentation), and Controller (logic/flow). Not tied to any framework or language.
Model Manages data and business rules. Knows how to talk to the database. Has no knowledge of how data is displayed.
View Renders output (HTML, JSON, etc.). Takes data from the Model and formats it for the client. Has no knowledge of data storage.
Controller Receives user input, coordinates the Model and View. The "traffic cop" that routes requests to the right code.
Server-Side MVC Server renders complete HTML pages. Browser just displays them. Best for content sites, SEO, and progressive enhancement. Frameworks: Express+EJS, Laravel, Django, Rails.
Client-Side MVC Server sends JSON, client JavaScript renders the UI. Best for highly interactive apps. Frameworks: React, Vue, Angular. Requires JS to function.
Adaptable MVC Same controller serves HTML or JSON based on the Accept header. Progressive enhancement: works without JS, enhanced with it.
REST vs MVC REST defines the external API interface (URLs, methods, status codes). MVC organizes internal server code. They are complementary, not competing.
ORMs Object-Relational Mappers let Models interact with the database using objects instead of raw SQL. Examples: Sequelize (Node.js), Eloquent (Laravel), Django ORM, ActiveRecord (Rails).