UNPKG

apialize

Version:

Turn a database model into a production ready REST(ish) CRUD API in a few lines.

202 lines (177 loc) 5.32 kB
const { express, apializeContext, ensureFn, asyncHandler, filterMiddlewareFns, buildHandlers, getIdFromInstance, extractOption, extractBooleanOption, extractMiddleware, mergeReqOptionsIntoModelOptions, convertInstanceToPlainObject, } = require('./utils'); const { validateData } = require('./validationMiddleware'); const { withTransactionAndHooks, optionsWithTransaction, } = require('./operationUtils'); function validateBulkCreateRequest(rawBody, allow_bulk_create, context) { if (Array.isArray(rawBody) && !allow_bulk_create) { context.res .status(400) .json({ success: false, error: 'Cannot insert multiple records.' }); return false; } return true; } function applyIdMapping(outputArray, id_mapping) { if (id_mapping && id_mapping !== 'id') { for (let i = 0; i < outputArray.length; i++) { const row = outputArray[i]; if ( row && Object.prototype.hasOwnProperty.call(row, id_mapping) && typeof row[id_mapping] !== 'undefined' ) { row.id = row[id_mapping]; } } } } function handleBulkCreate(model, rawBody, createOptions, context, id_mapping) { ensureFn(model, 'bulkCreate'); const bulkOptions = Object.assign({}, createOptions, { returning: true, validate: true, individualHooks: true, }); return model.bulkCreate(rawBody, bulkOptions).then((createdArray) => { const outputArray = []; for (let i = 0; i < createdArray.length; i++) { const instance = createdArray[i]; const plainObject = convertInstanceToPlainObject(instance); outputArray.push(plainObject); } applyIdMapping(outputArray, id_mapping); context.created = createdArray; context.payload = outputArray; return context.payload; }); } function handleSingleCreate(model, req, createOptions, context, id_mapping) { const values = req && req.apialize ? req.apialize.values : undefined; return model.create(values, createOptions).then((created) => { context.created = created; const idValue = getIdFromInstance(created, id_mapping); context.payload = { success: true, id: idValue }; return context.payload; }); } function create(model, options, modelOptions) { if (!options) { options = {}; } if (!modelOptions) { modelOptions = {}; } ensureFn(model, 'create'); const middleware = extractMiddleware(options); const allow_bulk_create = extractBooleanOption( options, 'allow_bulk_create', false ); const validate = extractBooleanOption(options, 'validate', true); const id_mapping = extractOption(options, 'id_mapping', 'id'); const pre = extractOption(options, 'pre', null); const post = extractOption(options, 'post', null); const inline = filterMiddlewareFns(middleware); const router = express.Router({ mergeParams: true }); const handlers = buildHandlers(inline, async (req, res) => { const effectiveOptions = Object.assign({}, options, { pre: pre, post: post, }); const payload = await withTransactionAndHooks( { model: model, options: effectiveOptions, req: req, res: res, modelOptions: modelOptions, idMapping: id_mapping, }, async function (context) { const rawBody = req && req.body; const isValidRequest = validateBulkCreateRequest( rawBody, allow_bulk_create, context ); if (!isValidRequest) { return; } // Run validation if enabled (after middleware and pre-hooks have run) if (validate) { try { // For bulk operations, validate the original array data // For single operations, use the processed values from middleware let dataToValidate; if (Array.isArray(rawBody)) { dataToValidate = rawBody; } else { dataToValidate = req && req.apialize && req.apialize.values ? req.apialize.values : rawBody; } await validateData(model, dataToValidate, { isPartial: false }); } catch (error) { if (error.name === 'ValidationError') { context.res.status(400).json({ success: false, error: error.message, details: error.details, }); return; } throw error; } } const mergedCreateOptions = mergeReqOptionsIntoModelOptions( req, modelOptions ); const createOptions = optionsWithTransaction( mergedCreateOptions, context.transaction ); if (Array.isArray(rawBody)) { return await handleBulkCreate( model, rawBody, createOptions, context, id_mapping ); } return await handleSingleCreate( model, req, createOptions, context, id_mapping ); } ); if (!res.headersSent) { res.status(201).json(payload); } }); router.post('/', handlers); router.apialize = {}; return router; } module.exports = create;