ultimate-crud
Version:
Ultimate dynamic CRUD API generator with REST, GraphQL, OpenAPI support and association handling for Node.js/Express/Sequelize
109 lines (101 loc) • 4.97 kB
JavaScript
/**
* REST utilities for Ultimate CRUD
*
* @license MIT
* @copyright 2025 cnos-dev
* @author Harish Kashyap (CNOS Dev)
*/
// utils/rest.js
const { defineModelFromTable } = require('./dynamicModel');
const { createCrudRouter, createGetRouter } = require('./dynamicRouter');
const { handleDatabaseError, asyncErrorHandler } = require('./errorHandler');
async function setupRest(app, sequelize, entities) {
for (const entity of entities) {
if (entity.type === "table") {
const model = await defineModelFromTable(sequelize, entity.name, entity.schema, entity.associations || []);
const pkAttr = Object.keys(model.rawAttributes).find(attr => model.rawAttributes[attr].primaryKey);
app.get(`${entity.route}/:${pkAttr}`, asyncErrorHandler(async (req, res, next) => {
try {
const item = await model.findByPk(req.params[pkAttr]);
if (item) res.status(200).json({ message: entity.responseMessages?.[200] || "Success", data: item });
else next({ status: 404, message: entity.responseMessages?.[404] || "Not found" });
} catch (err) {
const mappedError = handleDatabaseError(err, 'read', entity.name);
const message = entity.responseMessages?.[mappedError.status] || mappedError.message;
res.status(mappedError.status).json({
error: message,
details: mappedError.details
});
}
}));
app.put(`${entity.route}/:${pkAttr}`, asyncErrorHandler(async (req, res, next) => {
try {
const item = await model.findByPk(req.params[pkAttr]);
if (!item) return res.status(404).json({ error: entity.responseMessages?.[404] || "Not found" });
await item.update(req.body);
res.status(200).json({ message: entity.responseMessages?.[200] || "Success", data: item });
} catch (err) {
const mappedError = handleDatabaseError(err, 'update', entity.name);
const message = entity.responseMessages?.[mappedError.status] || mappedError.message;
res.status(mappedError.status).json({
error: message,
details: mappedError.details
});
}
}));
app.delete(`${entity.route}/:${pkAttr}`, asyncErrorHandler(async (req, res, next) => {
try {
const item = await model.findByPk(req.params[pkAttr]);
if (!item) return res.status(404).json({ error: entity.responseMessages?.[404] || "Not found" });
await item.destroy();
res.status(200).json({ message: entity.responseMessages?.[200] || "Deleted" });
} catch (err) {
const mappedError = handleDatabaseError(err, 'delete', entity.name);
const message = entity.responseMessages?.[mappedError.status] || mappedError.message;
res.status(mappedError.status).json({
error: message,
details: mappedError.details
});
}
}));
app.use(entity.route, createCrudRouter(model, { responseMessages: entity.responseMessages }));
} else if (entity.type === "view") {
const model = await defineModelFromTable(sequelize, entity.name, entity.schema, entity.associations || []);
app.use(entity.route, createGetRouter(model, { responseMessages: entity.responseMessages }));
} else if (entity.type === "query") {
app.get(entity.route, asyncErrorHandler(async (req, res, next) => {
try {
const [results] = await sequelize.query(entity.sql);
const msg = entity.responseMessages?.[200] || "Success";
res.status(200).json({ message: msg, data: results });
} catch (err) {
const mappedError = handleDatabaseError(err, 'read', entity.name);
const msg = entity.responseMessages?.[mappedError.status] || mappedError.message;
res.status(mappedError.status).json({
error: msg,
details: mappedError.details
});
}
}));
} else if (entity.type === "procedure") {
app.post(entity.route, asyncErrorHandler(async (req, res, next) => {
try {
const params = req.body ? Object.values(req.body) : [];
const placeholders = params.map(() => '?').join(',');
const sql = `CALL ${entity.procedure}(${placeholders})`;
const results = await sequelize.query(sql, { replacements: params });
const msg = entity.responseMessages?.[200] || "Procedure executed successfully";
res.status(200).json({ message: msg, data: results });
} catch (err) {
const mappedError = handleDatabaseError(err, 'create', entity.name);
const msg = entity.responseMessages?.[mappedError.status] || mappedError.message;
res.status(mappedError.status).json({
error: msg,
details: mappedError.details
});
}
}));
}
}
}
module.exports = { setupRest };