4bnode
Version:
4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.
120 lines (102 loc) • 3.15 kB
JavaScript
#!/usr/bin/env node
import path from "path";
import { select } from "@inquirer/prompts";
import { toPascalCase } from "./lib/names.js";
import { listRoutes, listModels, getModelSchema } from "./lib/project.js";
import {
getRouteFilePath,
addImportToRoute,
insertCodeIntoRoute,
} from "./lib/routeFile.js";
import {
showHeader,
showTaskDone,
showError,
showFileAction,
} from "./lib/ui.js";
async function main() {
showHeader("add-mongo-read", "Add data read route");
const routeFiles = listRoutes();
if (routeFiles.length === 0) {
showError("No route files found in src/routes.");
process.exit(1);
}
const modelFiles = listModels();
if (modelFiles.length === 0) {
showError("No model files found in src/models.");
process.exit(1);
}
const selectedRoute = await select({
message: "Select route file to modify:",
choices: routeFiles.map((f) => ({ name: f, value: f })),
});
const selectedModelFile = await select({
message: "Select model file to use:",
choices: modelFiles.map((f) => ({ name: f, value: f })),
});
const modelName = path.basename(selectedModelFile, ".js");
const pascalName = toPascalCase(modelName);
const fields = getModelSchema(modelName, { includeId: true });
const readType = await select({
message: "How to fetch data?",
choices: [
{ name: "Fetch all data", value: "all" },
{ name: "Fetch by specific field", value: "field" },
],
});
console.log();
const routeFilePath = getRouteFilePath(selectedRoute);
addImportToRoute(
routeFilePath,
`import ${pascalName} from '../models/${modelName}.js';`
);
let readCode;
let routeDetail;
if (readType === "all") {
readCode = `
router.get('/', async (req, res) => {
try {
const documents = await ${pascalName}.find();
res.json({ data: documents });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Server error' });
}
});`;
routeDetail = "GET / (fetch all)";
} else {
if (fields.length === 0) {
showError("No fields found in the selected model.");
process.exit(1);
}
const selectedField = await select({
message: "Select field to filter by:",
choices: fields.map((f) => ({ name: f, value: f })),
});
readCode = `
router.get('/', async (req, res) => {
try {
const documents = await ${pascalName}.find({ ${selectedField}: req.query.${selectedField} });
if (documents.length === 0) {
return res.status(404).json({ message: 'No matching documents found' });
}
res.json({ data: documents });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Server error' });
}
});`;
routeDetail = `GET /?${selectedField}=... (filter by ${selectedField})`;
}
insertCodeIntoRoute(routeFilePath, readCode);
showFileAction("updated", `src/routes/${selectedRoute}`);
showTaskDone("Read route added", [
`Route: ${routeDetail} in ${selectedRoute}`,
`Model: ${modelName}`,
]);
}
main().catch((err) => {
if (err.name === "ExitPromptError") process.exit(0);
showError(err.message);
process.exit(1);
});