Learn the key architectural decisions that can make or break your SaaS application as it scales from 100 to 100,000+ users.
sql
-- Always include tenant_id in your indexes
CREATE INDEX idx_orders_tenant_created ON orders(tenant_id, created_at);
CREATE INDEX idx_users_tenant_email ON users(tenant_id, email);
javascript
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
app.use('/api/', limiter);
javascript
app.get('/api/users', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = Math.min(parseInt(req.query.limit) || 10, 100);
const offset = (page - 1) * limit;
const users = await User.findAndCountAll({
where: { tenant_id: req.user.tenant_id },
limit,
offset,
order: [['created_at', 'DESC']]
});
res.json({
data: users.rows,
pagination: {
page,
limit,
total: users.count,
pages: Math.ceil(users.count / limit)
}
});
});
javascript
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const client = redis.createClient();
app.use(session({
store: new RedisStore({ client: client }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: { secure: false, maxAge: 86400000 } // 24 hours
}));
javascript
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10 minutes
app.get('/api/dashboard-stats', async (req, res) => {
const cacheKey =
dashboard-${req.user.tenant_id};
let stats = cache.get(cacheKey);
if (!stats) {
stats = await calculateDashboardStats(req.user.tenant_id);
cache.set(cacheKey, stats);
}
res.json(stats);
});
## Monitoring and Observability
### Health Checks
javascript
app.get('/health', async (req, res) => {
try {
// Check database connection
await sequelize.authenticate();
// Check Redis connection
await client.ping();
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
} catch (error) {
res.status(503).json({ status: 'unhealthy', error: error.message });
}
});
### Error Tracking
Use tools like Sentry for error tracking:
javascript
const Sentry = require('@sentry/node');
Sentry.init({ dsn: process.env.SENTRY_DSN });
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.errorHandler());
## Deployment and Infrastructure
### Docker Configuration
dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["npm", "start"]
### Environment Variables
Never hardcode configuration:
javascript
const config = {
port: process.env.PORT || 3000,
database: {
host: process.env.DB_HOST,
port: process.env.DB_PORT || 5432,
name: process.env.DB_NAME,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD
},
redis: {
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT || 6379
}
};
## Security Considerations
### Input Validation
javascript
const Joi = require('joi');
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).required(),
name: Joi.string().min(2).max(50).required()
});
app.post('/api/users', async (req, res) => {
const { error, value } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
// Process validated data
});
### SQL Injection Prevention
Always use parameterized queries:
javascript
// Bad
const query =
SELECT * FROM users WHERE email = '${email}';
// Good
const query = 'SELECT * FROM users WHERE email = $1';
const result = await client.query(query, [email]);
## Performance Optimization
### Database Query Optimization
javascript
// Use includes to avoid N+1 queries
const users = await User.findAll({
include: [{
model: Profile,
attributes: ['avatar', 'bio']
}],
where: { tenant_id: tenantId }
});
### Frontend Optimization
javascript
// Implement virtual scrolling for large lists
import { FixedSizeList as List } from 'react-window';
const VirtualizedList = ({ items }) => (
height={600}
itemCount={items.length}
itemSize={50}
itemData={items}
>
{({ index, style, data }) => (
{data[index].name}
)}
);
``Full Stack Developer
Passionate full-stack developer and tech consultant helping businesses build scalable solutions and accelerate growth.
A comprehensive guide to the modern full-stack development landscape, including the best tools, frameworks, and practices for 2024.
Read MoreIf you enjoyed this article and want to discuss your project or get personalized advice, I'd love to hear from you.