Mobile apps need a backend — and in 2020, Node.js with Express has become the go-to choice for teams that want JavaScript end-to-end. Its non-blocking I/O model handles thousands of concurrent connections efficiently, npm has every library you will ever need, and deploying to AWS, GCP, or Heroku is a five-minute operation. Here’s the architecture of a production-ready mobile API.
Project Structure
src/
├── routes/ # Express route handlers
├── controllers/ # Business logic
├── models/ # Mongoose schemas
├── middleware/ # Auth, validation, error handling
├── config/ # DB connection, env variables
└── app.js # Express app setup
Authentication with JWT
Mobile apps cannot use session cookies easily — JSON Web Tokens are the standard:
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
// Login endpoint
router.post('/login', async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (!user || !await bcrypt.compare(req.body.password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
res.json({ token });
});
// Auth middleware
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ error: 'Unauthorized' });
}
};
Input Validation with express-validator
Never trust mobile app input. Validate every field server-side:
const { body, validationResult } = require('express-validator');
router.post('/users', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
// proceed
});
Rate Limiting
const rateLimit = require('express-rate-limit');
app.use('/api', rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
Error Handling
A centralised error handler prevents stack traces leaking to clients. Catch all async errors with a wrapper or use express-async-errors to avoid try-catch in every route.
Environment Variables
Use dotenv for local development and set environment variables directly in your deployment platform. Never commit a .env file.
A well-structured Node.js API is fast to develop, easy to scale horizontally, and fits naturally into a mobile-first product team. With TypeScript (which the community is rapidly adopting), you also gain static typing across your entire stack.