DEV Community

Cover image for Structuring Your Backend for Growth: A Guide to Organizing API Calls
Janelle Phalon
Janelle Phalon

Posted on • Edited on

Structuring Your Backend for Growth: A Guide to Organizing API Calls

Introduction

In my previous blog, we took a leap into setting up our very first API calls for our secure message center with DataMotion's APIs. But as we add more features and capabilities, things can quickly get tangled. Now that we've got our feet wet with our initial API calls, it's time to restructure the backend to ensure scalability using best practices.

DataMotion's APIs offer a secure and compliant method for messaging, ideal for applications needing reliable data exchange.

Note: This blog was updated based on valuable feedback from readers. Your input is always appreciated and helps ensure the quality and relevance of my content. Thank you for your continued support!

Why Organize by Feature?

Using a feature-based backend organization:

  • 🧐 Focus: All related tasks for a feature in one spot.
  • 🔄 Modularity: Encapsulated features ensure clean separation.
  • 🚀 Easy Expansion: Introduce new features with new directories.
  • 🐛 Quick Debugging: Issues? Know exactly where to look.

Let's get started!

Backend Directory Structure

Your project should follow a clean structure to ensure clarity. Here's a suggested setup:

server/
│
│── authentication/
│    ├── authController.js
│    ├── authRoutes.js
│    └── getToken.js
│
│── messages/
│    ├── messageController.js
│    └── messageRoutes.js
│
│── .env       
│── server.js   
│── package.json
└── package-lock.json
Enter fullscreen mode Exit fullscreen mode

Let's dive into each section.

1. Authentication (authentication/)

Authentication is crucial for our app's security. This module ensures users are who they say they are and manages the tokens for DataMotion API interaction. Grouping all authentication tasks in one place streamlines any security enhancements or updates.

authController.js

const axios = require('axios');
const getTokenUtil = require('./getToken');

// Function to get a token from the DataMotion API
exports.getToken = async (req, res) => {
    try {
        const token = await getTokenUtil();
        res.json(token);
    } catch (error) {
        res.status(500).json({ message: "Error fetching token", error: error.response.data });
    }
};
Enter fullscreen mode Exit fullscreen mode

authRoutes.js

const express = require('express');
const router = express.Router();

const { getToken } = require('./authController');

// Endpoint to get token
router.get('/token', getToken);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

getToken.js

const axios = require('axios');

// Utility function to fetch the authentication token from the DataMotion API
const getTokenUtil = async () => {
    try {
        const response = await axios.post('https://api.datamotion.com/SMC/Messaging/v3/token', {
            grant_type: "client_credentials",
            client_id: process.env.CLIENT_ID,
            client_secret: process.env.CLIENT_SECRET
        });
        return response.data;
    } catch (error) {
        throw error;
    }
};

module.exports = getTokenUtil;
Enter fullscreen mode Exit fullscreen mode

2. Messages (messages/)

Our app's core function is handling secure messages. By clustering all related tasks in the messages/ directory, we streamline the addition of new messaging features and keep the focus on message operations.

messagesController.js

const axios = require('axios');
const getTokenUtil = require('../authentication/getToken');

// Function to get message summaries from the DataMotion API
exports.getMessageSummaries = async (req, res) => {
    try {
        const token = await getTokenUtil();
        const messagesResponse = await axios.get('https://api.datamotion.com/SMC/Messaging/v3/content/messages/?folderId=1&pageSize=10&pageNumber=1&sortDirection=DESC&metadata=true', {
            headers: {
                Authorization: `Bearer ${token.access_token}`
            }
        });
        res.json(messagesResponse.data);
    } catch (error) {
        res.status(500).json({ message: "Error fetching messages", error: error.response.data });
    }
};
Enter fullscreen mode Exit fullscreen mode

messageRoutes.js

const express = require('express');
const router = express.Router();

const { getMessageSummaries } = require('./messageController');

// Endpoint to get message summaries
router.get('/', getMessageSummaries);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

The Heart of It All: server.js

With our organized backend, let's take a look at the updated server.js:

const express = require('express');
const cors = require('cors');
require('dotenv').config();

const authRoutes = require('./authentication/authRoutes');
const messageRoutes = require('./messages/messageRoutes');

const app = express();
const PORT = 5000;

// Middlewares
app.use(cors());
app.use(express.json()); 

// Route middlewares
app.use('/auth', authRoutes);
app.use('/messages', messageRoutes);

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

With a feature-based backend, you're primed for smooth expansion and clear management. New features? Just slot in new modules.


By now, your message center's backend should be neatly structured and ready for growth. In our next posts, we'll add more features to our secure message center and watch our structured backend pay dividends. Stay tuned and happy coding!

Have you tackled restructuring the backend of your application recently? Share your experiences and tips in the comments below!

Top comments (3)

Collapse
 
theiaz profile image
Julian Schäfer

Its best not to structure your project by layer (controller, routes, ...) but by feature (auth, messages, ...)

Collapse
 
janellephalon profile image
Janelle Phalon

Thanks for the feedback! I understand the advantages of a feature-based organization, especially as projects scale. For this tutorial, the layered approach was chosen for its foundational clarity, but I recognize the benefits of a feature-centric structure in larger applications. I'll definitely consider this in future projects and writings. Thanks again for highlighting this!

Collapse
 
_ammurray profile image
anne marie

Great post! Thank you!