When you inherit a legacy project, the first thing you want to do is get it running locally. Clone the repo, install dependencies, maybe spin up some Docker containers, and you’re good to go. But legacy systems have their own ideas about cooperation.
I recently had to work on improving a process that integrated with an old internal system. This wasn’t just “a few versions behind” old - we’re talking Windows Server 2012, SQL Server 2008, and a an app that I never heard before. The system communicated via HTTP, which should’ve made things simple. Instead, I spent two days trying to get the environment working on my Mac. Between incompatible SQL Server drivers, Windows-specific dependencies, framework versions that refused to run outside their natural habitat, and my laptop’s very limited RAM struggling with the resource-hungry legacy stack, I realized I was fighting a losing battle.
json-server is a full fake REST API with zero coding required. It’s perfect when you need to mock an external service quickly without dealing with complex infrastructure. In my case, instead of wrestling with outdated dependencies and database versions, I could simulate the legacy API’s responses and continue development.
The benefits for legacy system integration:
First, install json-server as a development dependency:
npm install --save-dev json-server
Create a db.json
file with your mock data structure:
{
"users": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"status": "active"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"status": "inactive"
}
],
"transactions": [
{
"id": 1,
"userId": 1,
"amount": 150.00,
"status": "completed",
"createdAt": "2024-01-15T10:30:00Z"
}
]
}
Add a script to your package.json
:
{
"scripts": {
"mock-server": "json-server --watch db.json --port 3001"
}
}
Now you can run:
npm run mock-server
This gives you a fully functional REST API at http://localhost:3001
with GET, POST, PUT, PATCH, and DELETE operations on your resources.
The basic setup works great for simple cases, but legacy systems often have quirky endpoints and custom logic. Here’s how to create a custom server with dynamic routing:
Create a server.js
file:
// server.js
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
// Add custom middleware
server.use(middlewares);
// Parse JSON bodies
server.use(jsonServer.bodyParser);
// Custom routes before json-server router
server.get('/api/v1/users/:id/full-profile', (req, res) => {
const userId = parseInt(req.params.id);
const db = router.db; // Access lowdb instance
const user = db.get('users')
.find({ id: userId })
.value();
const transactions = db.get('transactions')
.filter({ userId: userId })
.value();
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({
user: user,
transactions: transactions,
transactionCount: transactions.length,
totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0)
});
});
// Simulate legacy system's weird authentication endpoint
server.post('/legacy/auth/validate', (req, res) => {
const { token } = req.body;
// Mock validation logic
if (token && token.startsWith('valid_')) {
res.json({
success: true,
userId: parseInt(token.split('_')[1]) || 1,
permissions: ['read', 'write']
});
} else {
res.status(401).json({
success: false,
error: 'Invalid token'
});
}
});
// Add delay to simulate network latency
server.use((req, res, next) => {
setTimeout(next, 500); // 500ms delay
});
// Custom error responses to match legacy system
server.use((req, res, next) => {
if (req.method === 'POST') {
req.body.createdAt = new Date().toISOString();
}
next();
});
// Use default router
server.use('/api', router);
// Custom 404 handler
server.use((req, res) => {
res.status(404).json({
error: 'Endpoint not found',
path: req.path,
timestamp: new Date().toISOString()
});
});
const PORT = process.env.MOCK_SERVER_PORT || 3001;
server.listen(PORT, () => {
console.log(`JSON Server is running on port ${PORT}`);
console.log(`http://localhost:${PORT}`);
});
For more complex scenarios with middleware chains:
// middleware.js
module.exports = (req, res, next) => {
// Add custom headers like the legacy system
res.header('X-Legacy-System', 'v1.0');
res.header('X-Response-Time', Date.now());
// Log all requests
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
// Validate API key for certain endpoints
if (req.path.startsWith('/api/secured')) {
const apiKey = req.headers['x-api-key'];
if (!apiKey || apiKey !== 'test-key-123') {
return res.status(403).json({
error: 'API key required',
code: 'MISSING_API_KEY'
});
}
}
next();
};
Update your server.js
to use the middleware:
const customMiddleware = require('./middleware');
server.use(customMiddleware);
Create a routes.json
file for URL rewriting to match your legacy system’s patterns:
{
"/old-api/get-user/:id": "/users/:id",
"/legacy/transactions/user/:userId": "/transactions?userId=:userId",
"/v1/users\\?id=:id": "/users/:id"
}
Update your package.json with different configurations:
{
"scripts": {
"mock:basic": "json-server --watch db.json --port 3001",
"mock:custom": "node server.js",
"mock:routes": "json-server --watch db.json --routes routes.json --port 3001",
"mock:dev": "nodemon server.js"
}
}
Test your endpoints using curl or httpie:
# Test basic CRUD
curl http://localhost:3001/api/users
# Test custom endpoint
curl http://localhost:3001/api/v1/users/1/full-profile
# Test with custom headers
curl -H "X-API-Key: test-key-123" \
http://localhost:3001/api/secured/users
# Test POST with authentication
curl -X POST \
-H "Content-Type: application/json" \
-d '{"token":"valid_123"}' \
http://localhost:3001/legacy/auth/validate
json-server transformed what could have been days of environment setup into a 30-minute solution. Instead of fighting with legacy dependencies, you can focus on building your integration. The mock server is also invaluable for:
You might also want to explore WireMock if you need more sophisticated request matching. But for quickly mocking REST APIs, especially legacy systems, json-server hits the sweet spot between simplicity and functionality.
When dealing with legacy systems, sometimes the best integration is the one that doesn’t require actually integrating at all 😄