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
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 });
}
};
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;
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;
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 });
}
};
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;
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}`);
});
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)
Its best not to structure your project by layer (controller, routes, ...) but by feature (auth, messages, ...)
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!
Great post! Thank you!