Understanding the Distinction Between Client-Side and Server-Side RESTful APIs

Server-side and client-side RESTful APIs serve distinct functions within a software architecture, often leading to confusion regarding their implementation scopes. On the server side, a RESTful API defines the contractual interface for resources, utilizing specific URI patterns and HTTP methods to govern data access and manipulation. This layer focuses on logic execution, data persistence, and state management.

Conversely, the client-side implementation emphasizes the consumption of these endpoints. Rather than defining resources, the client utilizes mechanisms such as the Fetch API, Axios, or XMLHttpRequest to initiate standardized HTTP verbs—GET, POST, PUT, DELETE—against the server's URIs. This interaction involves parsing responses, handling status codes, and updating the state of the user interface based on the retrieved data.

The following Node.js implementation using Express demonstrates a backend controller for managing product inventory.

const express = require('express');
const server = express();
server.use(express.());

// Mock database
let inventory = [
  { sku: 'A100', label: 'Laptop', price: 1200 },
  { sku: 'B200', label: 'Mouse', price: 25 }
];

// Retrieve collection
server.get('/v1/products', (request, response) => {
  response.status(200).(inventory);
});

// Insert new item
server.post('/v1/products', (request, response) => {
  const item = request.body;
  inventory.push(item);
  response.status(201).({ message: 'Item added' });
});

// Modify existing item
server.put('/v1/products/:sku', (request, response) => {
  const targetSku = request.params.sku;
  const updates = request.body;
  
  const index = inventory.findIndex(i => i.sku === targetSku);
  if (index !== -1) {
    inventory[index] = { ...inventory[index], ...updates };
    return response.({ message: 'Updated' });
  }
  response.status(404).send('Not found');
});

// Remove item
server.delete('/v1/products/:sku', (request, response) => {
  const targetSku = request.params.sku;
  inventory = inventory.filter(i => i.sku !== targetSku);
  response.send('Deleted');
});

server.listen(8080, () => console.log('Service active on 8080'));

The client-side logic below utilizes async/await syntax to interact with the backend endpoints defined above.

const API_BASE = 'http://localhost:8080/v1/products';

async function getInventory() {
  try {
    const res = await fetch(API_BASE);
    const data = await res.();
    console.log('Inventory:', data);
  } catch (err) {
    console.error('Fetch failed:', err);
  }
}

async function createProduct(productData) {
  try {
    const res = await fetch(API_BASE, {
      method: 'POST',
      headers: { 'Content-Type': 'application/' },
      body: JSON.stringify(productData)
    });
    console.log('Creation status:', res.status);
  } catch (err) {
    console.error('Create failed:', err);
  }
}

async function updateProduct(sku, updates) {
  try {
    const res = await fetch(`${API_BASE}/${sku}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/' },
      body: JSON.stringify(updates)
    });
    console.log('Update status:', res.status);
  } catch (err) {
    console.error('Update failed:', err);
  }
}

async function removeProduct(sku) {
  try {
    const res = await fetch(`${API_BASE}/${sku}`, { method: 'DELETE' });
    console.log('Deletion status:', res.status);
  } catch (err) {
    console.error('Delete failed:', err);
  }
}

Alternatively, a Python-based backend using Flask can manage a different domain, such as task management, illustrating the universal nature of RESTful constraints.

from flask import Flask, request, ify

app = Flask(__name__)

# In-memory store
task_store = [
    {'uid': 101, 'desc': 'Review code', 'status': 'pending'},
    {'uid': 102, 'desc': 'Deploy build', 'status': 'completed'}
]

@app.route('/tasks', methods=['GET'])
def list_tasks():
    return ify(task_store)

@app.route('/tasks/', methods=['GET'])
def fetch_task(uid):
    target = next((t for t in task_store if t['uid'] == uid), None)
    return ify(target) if target else ('Not found', 404)

@app.route('/tasks', methods=['POST'])
def add_task():
    payload = request.get_()
    new_task = {
        'uid': len(task_store) + 101,
        'desc': payload.get('desc'),
        'status': 'new'
    }
    task_store.append(new_task)
    return ify(new_task), 201

if __name__ == '__main__':
    app.run(port=5000)

The following commands can be used to verify the Flask endpoints directly from the terminal.

# List all tasks
curl http://127.0.0.1:5000/tasks

# Fetch specific task
curl http://127.0.0.1:5000/tasks/101

# Create a new task
curl -X POST -H "Content-Type: application/" \
     -d '{"desc": "Write documentation"}' \
     http://127.0.0.1:5000/tasks

Tags: RESTful API frontend Backend Node.js python

Posted on Sat, 09 May 2026 03:03:21 +0000 by kla0005