My boss thought it strange that we didn't already have a "mock server" so that we could keep doing UI work in case the endpoints went down.
He suggested using json-server but that wasn't made to emulate a rich set of endpoints, just one level deep, either GETing all the entries or just one at a time by id. Luckily that kind of server is the easy part of the assignment.
The first part was to make a file "urls.txt", with just the relevant part of the endpoint...
then this script load-db.js hits each of those entries and writes the content to a file in mocks/
const express = require("express");
const fs = require("fs");
const path = require("path");
const app = express();
// Ensure the mocks directory exists
const mocksDir = path.join(__dirname, "mocks");
if (!fs.existsSync(mocksDir)) {
fs.mkdirSync(mocksDir);
}
// Read the command line argument for the prefix
const prefix = process.argv[2];
// Read the URLs from urls.txt
const urlsFilePath = path.join(__dirname, "urls.txt");
const urls = fs.readFileSync(urlsFilePath, "utf8").split("\n");
// Function to make a safe filename from a URL path and append .json
const makeSafeFilename = (urlPath) => encodeURIComponent(urlPath.replace(/^\//, "").replace(/\//g, "_")) + ".json";
// Fetch content and save it as JSON
urls.forEach(async (urlPath) => {
if (!urlPath.trim()) return; // Skip empty lines
const content = await fetch(`${prefix}${urlPath}`).then((res) => res.json());
const safeFilename = makeSafeFilename(urlPath);
fs.writeFileSync(path.join(mocksDir, safeFilename), JSON.stringify(content, null, 2));
console.log(`Saved content from ${urlPath} to ${safeFilename}`);
});
Then the server.js just looks like this:
const express = require("express");
const fs = require("fs");
const path = require("path");
const cors = require("cors");
const app = express();
app.use(cors());
const mocksDir = path.join(__dirname, "mocks");
app.get("/", (req, res) => {
fs.readdir(mocksDir, (err, files) => {
if (err) {
console.error(err);
return res.status(500).send("Server error");
}
const links = files
.filter((file) => file.endsWith(".geojson") || file.endsWith(".json"))
.map((file) => {
const decodedFilename = file.replace(/\..+$/, "").replace(/_/g, "/");
const encodedPath = decodedFilename
.split("/")
.map((part) => encodeURIComponent(part))
.join("/");
return `<li><a href="/${encodedPath}">${file}</a></li>`;
})
.join("");
res.send(`<ul>${links}</ul>`);
});
});
app.get("*", (req, res) => {
const safeFilename = encodeURIComponent(req.path.replace(/^\//, "").replace(/\//g, "_"));
const filePath = path.join(mocksDir, safeFilename);
if (fs.existsSync(`${filePath}.geojson`) || fs.existsSync(`${filePath}.json`)) {
res.type("application/json");
res.sendFile(fs.existsSync(`${filePath}.geojson`) ? `${filePath}.geojson` : `${filePath}.json`);
console.log(req.path);
} else {
res.status(404).send("Not Found");
console.error(`404 ${req.path}`);
}
});
// Use environment variable for port or a default value
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Mock server listening at http://localhost:${port}`);
});
package.json is
{
"name": "mock-server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.3",
"node-fetch": "^3.3.2"
}
}
(Sigh, I suppose if I was feeling ambitious I should make my a little
project out of this and put it on github, but I'm sure someone has, and
better.)
UPDATE: this version now lists the endpoint/files it knows about if you go to the root, and listens to process.env.PORT for instructions on port