API Access & Documentation

RESTful API, MVC Pattern, Data Structures, Algorithms, State Management, and Debugging

By the Domain India teamPublished 8 min read
Knowledge base article
Contents (6 sections)

Building a MERN application (MongoDB, Express, React, Node.js) is easier once five ideas click: how a REST API is shaped, how MVC splits your server code, which data structure to reach for, where state lives in React, and how to handle errors and debug. This guide explains each one with short, current code you can reuse.

Key takeaways

Design your API around resources (/api/users/42) and HTTP methods, with correct status codes. Split Express code into models, controllers and routes, and keep React as the view. Use a Map or object for lookups instead of looping through arrays. Keep React state local with hooks, use a data-fetching library for server data, and add a global store only when you need one. Handle errors in one Express middleware, and debug with the browser DevTools and the Node.js inspector.

1. REST API principles

REST (Representational State Transfer) is a style for web APIs described by Roy Fielding in 2000. A RESTful API treats things on the server as resources with URLs, and uses HTTP methods to act on them.

ActionMethod and URLSuccess status
List usersGET /api/users200 OK
Get one userGET /api/users/42200 OK, or 404 if missing
Create a userPOST /api/users201 Created
Replace or updatePUT or PATCH /api/users/42200 OK
DeleteDELETE /api/users/42204 No Content

The principles that matter day to day:

  • Client and server are separate. React and the API can change independently as long as the API contract holds.
  • Stateless requests. Each request carries what the server needs, such as an auth token. The server does not rely on memory of earlier requests.
  • Cacheable responses. GET responses can be cached; say how long with Cache-Control.
  • A uniform interface. Nouns in URLs, verbs as HTTP methods: POST /api/users, not /api/createUser.

Use status codes honestly: 400 for invalid input, 401 when the user is not logged in, 403 when they are logged in but not allowed, 404 when the resource does not exist, 500 only for real server faults.

2. The MVC pattern in an Express app

Model-View-Controller splits code by responsibility:

  • Model: the data and its rules (a Mongoose schema).
  • View: what the user sees. In a MERN app this is the React front end; the API returns JSON.
  • Controller: receives the request, calls the model, and sends the response.

A small, correct example:

js
// models/User.js
import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  email: { type: String, required: true, unique: true, lowercase: true },
}, { timestamps: true });

export default mongoose.model('User', userSchema);
js
// controllers/userController.js
import User from '../models/User.js';

export async function getUser(req, res) {
  const user = await User.findById(req.params.id).lean();
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
}

export async function createUser(req, res) {
  const user = await User.create({ name: req.body.name, email: req.body.email });
  res.status(201).json(user);
}
js
// routes/users.js
import { Router } from 'express';
import { getUser, createUser } from '../controllers/userController.js';

const router = Router();
router.get('/:id', getUser);
router.post('/', createUser);
export default router;

Two things older tutorials get wrong: Mongoose queries return promises, so you must await them; and a controller should pick the fields it accepts (name, email) rather than saving req.body whole, or a user can set fields you never meant to expose.

Express 5 passes errors from async handlers to your error middleware automatically. On Express 4 you need a wrapper or a try/catch in every async handler.

3. Data structures and algorithms that matter in web apps

You rarely write sorting algorithms in a web app, but choosing the right structure changes how fast your code runs as data grows.

Finding a user in an array is a linear search, O(n): each lookup checks every item.

js
const users = [{ id: 1, name: 'Asha' }, { id: 2, name: 'Ravi' }];
const found = users.find(u => u.id === 2); // O(n)

If you look up many times, build a Map once and each lookup becomes O(1) on average:

js
const usersById = new Map(users.map(u => [u.id, u]));
usersById.get(2); // { id: 2, name: 'Ravi' }
Array
Ordered lists you show or loop through
Map
Fast lookup by key, any key type
Set
Unique values, fast "have I seen this?" checks
Database index
The same idea on the server: index fields you filter or sort on in MongoDB

The database is usually where performance is won or lost. Filter and paginate in the query (find().limit(20)) instead of loading everything into Node.js and filtering there.

4. State management in React

React today is written with function components and hooks. Local state with useState:

jsx
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

Choose the lightest tool that fits:

  • Local state (useState, useReducer) for anything one component or a small subtree uses.
  • Context for values many components read but rarely change, such as the logged-in user or theme.
  • A data-fetching library such as TanStack Query for server data. It handles loading states, caching and refetching, which is most of what apps once used Redux for.
  • A global store (Redux Toolkit or Zustand) when many distant components change the same client-side data. If you use Redux, use Redux Toolkit; hand-written reducers and action types are the old style.

5. Error handling and debugging

In JavaScript, try/catch/finally handles errors in synchronous code and in await:

js
try {
  const data = JSON.parse(input);
} catch (err) {
  console.error('Invalid JSON:', err.message);
}

In Express, handle errors in one place with an error middleware registered after your routes:

js
// app.js
app.use('/api/users', usersRouter);

app.use((err, req, res, next) => {
  console.error(err);
  const status = err.status || 500;
  res.status(status).json({ error: status === 500 ? 'Server error' : err.message });
});

Never send stack traces or database error text to the browser in production; log them on the server instead.

Debugging tools worth learning:

  • Browser DevTools: the Network tab shows each API request, its status and response; the Sources tab lets you set breakpoints in React code; React Developer Tools shows component state.
  • Node.js inspector: start the server with node --inspect app.js and attach Chrome DevTools or VS Code to step through Express code.
  • Structured logs: log with context (route, user ID, error) so a problem in production can be traced.

6. Running this on Domain India

Once your API works locally, there are three places to run it:

  • Shared hosting (cPanel or DirectAdmin): Node.js apps run through the panel's Node.js app tool. On cPanel, Node.js 20, 22 and 24 are available. You cannot run a MongoDB server there, so the database has to live elsewhere. See Deploy a Node.js app on shared hosting.
  • App Platform: Node.js apps are detected and built automatically; your app must listen on the PORT environment variable. Each plan includes PostgreSQL rather than MongoDB, so use a hosted MongoDB service or switch to PostgreSQL. See Getting started with App Platform.
  • VPS: self-managed with full root access, so you can run MongoDB yourself. See Running MERN on a clean VPS.

To put these ideas into practice, build the MERN to-do list app, or browse the MERN stack index.

App Starter
₹100/mo + GST
  • 512 MB RAM per app
  • 1 vCPU
  • 5 GB NVMe SSD
  • PostgreSQL Database
See plan details
What makes an API RESTful?

A RESTful API exposes resources at URLs, acts on them with HTTP methods such as GET, POST, PUT, PATCH and DELETE, keeps each request stateless, and returns meaningful HTTP status codes such as 201 for created and 404 for not found.

Is React the View in MVC?

In a MERN app, yes, loosely. Express holds the models and controllers and returns JSON, and the React front end renders that data for the user.

Do I still need Redux in a React app?

Often not. Local state with hooks, Context for rarely changing shared values, and a data-fetching library for server data cover most apps. Use Redux Toolkit or Zustand when many distant components change the same client-side state.

Why does my Mongoose query return a Query object instead of data?

Mongoose queries are asynchronous. Use await, as in const users = await User.find(), inside an async function; without await you get a pending query, not the documents.

How should an Express API handle errors?

Register one error-handling middleware after your routes, send a clean message and status code to the client, and log the full error on the server. Express 5 forwards errors from async handlers to it automatically.

Can I host a MERN app on Domain India shared hosting?

The Node.js part can run through the Node.js app tool in cPanel or DirectAdmin. You cannot run a MongoDB server on shared hosting, so use a hosted MongoDB service (ask support to confirm your server can reach it), or run everything on a VPS where you control the server.

Ready to deploy your API? Compare App Platform plans and VPS plans, or read Deploy a Node.js app on shared hosting.

Not sure where your app should run?

Tell us your stack and expected traffic, and we will suggest shared hosting, the App Platform or a VPS.

Ask our team

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app
REST, MVC, State & Debugging for MERN Developers