├── dist ├── all.d.ts ├── files │ ├── utils.d.ts │ ├── interface.crud.js │ ├── interface.crud.js.map │ ├── utils.js.map │ ├── utils.js │ ├── error.handler.d.ts │ ├── responseMessage.js.map │ ├── error.handler.js.map │ ├── responseMessage.d.ts │ ├── responseMessage.js │ ├── query.js.map │ ├── error.handler.js │ ├── interface.crud.d.ts │ ├── query.d.ts │ ├── crud.model.js.map │ ├── query.js │ ├── crud.model.d.ts │ ├── crud.controller.js.map │ ├── crud.model.js │ ├── crud.service.js.map │ ├── crud.service.d.ts │ ├── crud.controller.d.ts │ ├── crud.controller.js │ └── crud.service.js ├── test │ ├── crud.model.test.d.ts │ ├── crud.query.test.d.ts │ ├── crud.service.test.d.ts │ ├── error.handler.test.d.ts │ ├── crud.controller.test.d.ts │ ├── model.js │ ├── model.js.map │ ├── crud.model.test.js │ ├── crud.model.test.js.map │ ├── crud.query.test.js.map │ ├── crud.query.test.js │ ├── error.handler.test.js.map │ ├── error.handler.test.js │ ├── crud.service.test.js.map │ ├── model.d.ts │ ├── crud.controller.test.js.map │ ├── crud.service.test.js │ └── crud.controller.test.js ├── index.d.ts ├── index.js.map ├── index.js ├── all.js.map └── all.js ├── .gitignore ├── LICENSE ├── src ├── test │ ├── model.ts │ ├── crud.model.test.ts │ ├── crud.query.test.ts │ ├── error.handler.test.ts │ ├── crud.service.test.ts │ └── crud.controller.test.ts ├── index.ts └── files │ ├── utils.ts │ ├── interface.crud.ts │ ├── responseMessage.ts │ ├── error.handler.ts │ ├── query.ts │ ├── crud.model.ts │ ├── crud.controller.ts │ └── crud.service.ts ├── typedoc.json ├── package.json ├── README.md └── tsconfig.json /dist/all.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs -------------------------------------------------------------------------------- /dist/files/utils.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/test/crud.model.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/test/crud.query.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/test/crud.service.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/test/error.handler.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/test/crud.controller.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT licenced 2 | Copyright (c) 2024 Derek Ogagarue -------------------------------------------------------------------------------- /dist/files/interface.crud.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=interface.crud.js.map -------------------------------------------------------------------------------- /dist/files/interface.crud.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"interface.crud.js","sourceRoot":"","sources":["../../src/files/interface.crud.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/test/model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const modelSchema = new mongoose.Schema({ 3 | name: String, 4 | age: Number, 5 | }); 6 | export const MyModel = mongoose.model("MyModel", modelSchema); 7 | //# sourceMappingURL=model.js.map -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import CrudController from "./files/crud.controller"; 2 | import generateDynamicSchema from "./files/crud.model"; 3 | import CrudService from "./files/crud.service"; 4 | import { errorCenter } from "./files/error.handler"; 5 | export { CrudController, CrudService, errorCenter, generateDynamicSchema }; 6 | -------------------------------------------------------------------------------- /src/test/model.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | const modelSchema = new mongoose.Schema({ 3 | name: String, 4 | age: Number, 5 | }); 6 | 7 | export const MyModel = mongoose.model("MyModel", modelSchema); 8 | 9 | export interface MyModelInterface { 10 | name: string; 11 | age: number; 12 | } 13 | -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,yBAAyB,CAAC;AACrD,OAAO,qBAAqB,MAAM,oBAAoB,CAAC;AACvD,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC"} -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import CrudController from "./files/crud.controller"; 2 | import generateDynamicSchema from "./files/crud.model"; 3 | import CrudService from "./files/crud.service"; 4 | import { errorCenter } from "./files/error.handler"; 5 | 6 | export { CrudController, CrudService, errorCenter, generateDynamicSchema }; 7 | -------------------------------------------------------------------------------- /dist/test/model.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/test/model.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,MAAM,WAAW,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;CACZ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC"} -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | import CrudController from "./files/crud.controller"; 2 | import generateDynamicSchema from "./files/crud.model"; 3 | import CrudService from "./files/crud.service"; 4 | import { errorCenter } from "./files/error.handler"; 5 | export { CrudController, CrudService, errorCenter, generateDynamicSchema }; 6 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/test/crud.model.test.js: -------------------------------------------------------------------------------- 1 | import { generateDynamicSchema } from ".."; 2 | import mongoose from "mongoose"; 3 | const table = generateDynamicSchema({ 4 | modelName: "TABLE", 5 | fields: { 6 | name: { 7 | type: String 8 | } 9 | }, model: mongoose.model, 10 | }); 11 | //# sourceMappingURL=crud.model.test.js.map -------------------------------------------------------------------------------- /dist/files/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/files/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU;AACd;mFACmF;AACnF,CAAC,EAAO,EAAE,EAAE,CAAC,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC/D,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC,CAAC"} -------------------------------------------------------------------------------- /dist/files/utils.js: -------------------------------------------------------------------------------- 1 | const catchAsync = 2 | /* This code defines a higher-order function named `catchAsync`. This function takes another function 3 | `fn` as a parameter and returns a new function that handles asynchronous errors. */ 4 | (fn) => (req, res, next) => { 5 | Promise.resolve(fn(req, res, next)).catch((err) => next(err)); 6 | }; 7 | export {}; 8 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /dist/test/crud.model.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.model.test.js","sourceRoot":"","sources":["../../src/test/crud.model.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,QAAQ,MAAM,UAAU,CAAC;AAShC,MAAM,KAAK,GAAG,qBAAqB,CAAY;IAC3C,SAAS,EAAE,OAAO;IAClB,MAAM,EAAE;QACJ,IAAI,EAAE;YACF,IAAI,EAAE,MAAM;SACf;KACJ,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK;CAE3B,CAAC,CAAA"} -------------------------------------------------------------------------------- /src/test/crud.model.test.ts: -------------------------------------------------------------------------------- 1 | import { generateDynamicSchema } from ".."; 2 | import mongoose from "mongoose"; 3 | 4 | interface TableI { 5 | name: string 6 | } 7 | 8 | interface e { 9 | 10 | } 11 | const table = generateDynamicSchema({ 12 | modelName: "TABLE", 13 | fields: { 14 | name: { 15 | type: String 16 | } 17 | }, model: mongoose.model, 18 | 19 | }) -------------------------------------------------------------------------------- /src/files/utils.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | const catchAsync = 4 | /* This code defines a higher-order function named `catchAsync`. This function takes another function 5 | `fn` as a parameter and returns a new function that handles asynchronous errors. */ 6 | (fn: any) => (req: Request, res: Response, next: NextFunction) => { 7 | Promise.resolve(fn(req, res, next)).catch((err) => next(err)); 8 | }; 9 | -------------------------------------------------------------------------------- /dist/files/error.handler.d.ts: -------------------------------------------------------------------------------- 1 | import { Response } from "express"; 2 | declare class CustomError extends Error { 3 | status: number; 4 | isOperational: boolean; 5 | constructor(status: number, message: string, isOperational?: boolean); 6 | } 7 | export declare const errorCenter: ({ error, response, env, }: { 8 | error: any; 9 | response: Response; 10 | env: "production" | "development"; 11 | }) => void; 12 | export default CustomError; 13 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "src" 4 | ], 5 | "out": "./docs", 6 | "tsconfig": "./tsconfig.json", 7 | "cleanOutputDir": true, 8 | "watch": false, 9 | "entryPointStrategy": "expand", 10 | "includeVersion": true, 11 | "markedOptions": { 12 | "gfm": true, 13 | "tables": true, 14 | "breaks": false, 15 | "pedantic": false, 16 | "sanitize": false, 17 | "smartLists": true, 18 | "smartypants": false 19 | }, 20 | } -------------------------------------------------------------------------------- /dist/files/responseMessage.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"responseMessage.js","sourceRoot":"","sources":["../../src/files/responseMessage.ts"],"names":[],"mappings":"AACA;;;;GAIG;AACH;;;;;;;;;GASG;AACH,SAAS,eAAe,CACtB,GAAsB,EACtB,SAAuC,YAAY;IAEnD,QAAQ,GAAG,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,IAAI;YACP,OAAO;gBACL,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC;QAEJ,KAAK,KAAK;YACR,OAAO;gBACL,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,KAAK,EAAE,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;aACjD,CAAC;IACN,CAAC;AACH,CAAC;AACD,eAAe,eAAe,CAAC"} -------------------------------------------------------------------------------- /src/files/interface.crud.ts: -------------------------------------------------------------------------------- 1 | import { Document, Model, PopulateOptions } from "mongoose"; 2 | 3 | type PrefixedKeys = { [K in keyof T]: `-${string & keyof T}` }[keyof T]; 4 | 5 | export interface CrudModelI { 6 | Model: Model; 7 | select: ( 8 | | keyof (Pick & V) 9 | | PrefixedKeys & V> 10 | )[] /* ( 11 | | keyof Document 12 | | `-${keyof Document extends string ? string : never}` 13 | )[]; */; 14 | } 15 | 16 | export interface PopulateFieldI { 17 | path?: keyof V; 18 | fields?: string[]; 19 | second_layer_populate?: PopulateOptions | string; 20 | } 21 | export interface CustomMessageI { 22 | message: string; 23 | success: boolean; 24 | data?: V; 25 | stack?: any; 26 | error?: any; 27 | doc_length?: number; 28 | } 29 | -------------------------------------------------------------------------------- /dist/test/crud.query.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.query.test.js","sourceRoot":"","sources":["../../src/test/crud.query.test.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,gBAAgB,CAAC;AAErC,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,wIAAwI;IACxI,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,MAAM,aAAa,GAAG,EAAE,CAAC;QAEzB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAElD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,4GAA4G;IAC5G,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /src/test/crud.query.test.ts: -------------------------------------------------------------------------------- 1 | import Queries from "../files/query"; 2 | 3 | describe("Queries", () => { 4 | // The constructor function should correctly assign the model and request_query parameters to the corresponding properties of the class. 5 | it("should correctly assign the model and request_query parameters", () => { 6 | const model = {}; 7 | const request_query = {}; 8 | 9 | const queries = new Queries(model, request_query); 10 | 11 | expect(queries.model).toBe(model); 12 | expect(queries.request_query).toBe(request_query); 13 | }); 14 | 15 | // The constructor function should throw an error if the model or request_query parameters are not provided. 16 | it("should throw an error if the model or request_query parameters are not provided", () => { 17 | expect(() => new Queries(null, null)).toThrow(); 18 | expect(() => new Queries({}, {})).toThrow(); 19 | expect(() => new Queries(null, {})).toThrow(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /dist/test/crud.query.test.js: -------------------------------------------------------------------------------- 1 | import Queries from "../files/query"; 2 | describe("Queries", () => { 3 | // The constructor function should correctly assign the model and request_query parameters to the corresponding properties of the class. 4 | it("should correctly assign the model and request_query parameters", () => { 5 | const model = {}; 6 | const request_query = {}; 7 | const queries = new Queries(model, request_query); 8 | expect(queries.model).toBe(model); 9 | expect(queries.request_query).toBe(request_query); 10 | }); 11 | // The constructor function should throw an error if the model or request_query parameters are not provided. 12 | it("should throw an error if the model or request_query parameters are not provided", () => { 13 | expect(() => new Queries(null, null)).toThrow(); 14 | expect(() => new Queries({}, {})).toThrow(); 15 | expect(() => new Queries(null, {})).toThrow(); 16 | }); 17 | }); 18 | //# sourceMappingURL=crud.query.test.js.map -------------------------------------------------------------------------------- /dist/test/error.handler.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"error.handler.test.js","sourceRoot":"","sources":["../../src/test/error.handler.test.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAEjD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,qFAAqF;IACrF,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,MAAM,GAAG,GAAG,CAAC;QACnB,MAAM,OAAO,GAAG,WAAW,CAAC;QAC5B,MAAM,aAAa,GAAG,IAAI,CAAC;QAE3B,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAEpE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,wEAAwE;IACxE,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,MAAM,GAAG,GAAG,CAAC;QACnB,MAAM,OAAO,GAAG,uBAAuB,CAAC;QAExC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAErD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /src/test/error.handler.test.ts: -------------------------------------------------------------------------------- 1 | import CustomError from "../files/error.handler"; 2 | 3 | describe("CustomError", () => { 4 | // CustomError can be instantiated with a status code, message and isOperational flag 5 | it("should instantiate CustomError with status code, message, and isOperational flag", () => { 6 | const status = 404; 7 | const message = "Not Found"; 8 | const isOperational = true; 9 | 10 | const customError = new CustomError(status, message, isOperational); 11 | 12 | expect(customError.status).toBe(status); 13 | expect(customError.message).toBe(message); 14 | expect(customError.isOperational).toBe(isOperational); 15 | }); 16 | 17 | // CustomError can be instantiated without passing an isOperational flag 18 | it("should instantiate CustomError without passing an isOperational flag", () => { 19 | const status = 500; 20 | const message = "Internal Server Error"; 21 | 22 | const customError = new CustomError(status, message); 23 | 24 | expect(customError.status).toBe(status); 25 | expect(customError.message).toBe(message); 26 | expect(customError.isOperational).toBe(true); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /dist/test/error.handler.test.js: -------------------------------------------------------------------------------- 1 | import CustomError from "../files/error.handler"; 2 | describe("CustomError", () => { 3 | // CustomError can be instantiated with a status code, message and isOperational flag 4 | it("should instantiate CustomError with status code, message, and isOperational flag", () => { 5 | const status = 404; 6 | const message = "Not Found"; 7 | const isOperational = true; 8 | const customError = new CustomError(status, message, isOperational); 9 | expect(customError.status).toBe(status); 10 | expect(customError.message).toBe(message); 11 | expect(customError.isOperational).toBe(isOperational); 12 | }); 13 | // CustomError can be instantiated without passing an isOperational flag 14 | it("should instantiate CustomError without passing an isOperational flag", () => { 15 | const status = 500; 16 | const message = "Internal Server Error"; 17 | const customError = new CustomError(status, message); 18 | expect(customError.status).toBe(status); 19 | expect(customError.message).toBe(message); 20 | expect(customError.isOperational).toBe(true); 21 | }); 22 | }); 23 | //# sourceMappingURL=error.handler.test.js.map -------------------------------------------------------------------------------- /dist/files/error.handler.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"error.handler.js","sourceRoot":"","sources":["../../src/files/error.handler.ts"],"names":[],"mappings":"AACA,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAY,SAAQ,KAAK;IAI7B,YAAY,MAAc,EAAE,OAAe,EAAE,aAAa,GAAG,IAAI;QAC/D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;CACF;AAED,wDAAwD;AACxD,4CAA4C;AAE5C,SAAS;AACT,yDAAyD;AACzD,qDAAqD;AACrD,QAAQ;AACR,2DAA2D;AAC3D,yEAAyE;AACzE,gBAAgB;AAChB,yCAAyC;AACzC,uCAAuC;AACvC,QAAQ;AACR,mCAAmC;AACnC,sDAAsD;AACtD,sCAAsC;AACtC,SAAS;AACT,+DAA+D;AAC/D,aAAa;AACb,+BAA+B;AAC/B,oDAAoD;AACpD,MAAM;AAEN,yBAAyB;AACzB,KAAK;AAEL,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,GAAG,GAAG,aAAa,GAOpB,EAAE,EAAE;;IACH,MAAM,YAAY,GAAG,MAAA,KAAK,CAAC,UAAU,mCAAI,GAAG,CAAC;IAE7C,IAAI,aAAa,GAAW,KAAK,CAAC,OAAO,CAAC;IAE1C,eAAe;IACf,IAAI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,aAAa,GAAG,oCAAoC,CAAC;IACvD,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC;IAErC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAChC,eAAe,CACb;QACE,OAAO,EAAE,UAAU;QACnB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,EACD,GAAG,CACJ,CACF,CAAC;AACJ,CAAC,CAAC;AACF,eAAe,WAAW,CAAC"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expressbolt", 3 | "version": "1.0.22", 4 | "description": "Crud helper using mongoose to make creation of api fast", 5 | "author": "Derek Og ", 6 | "keywords": [ 7 | "mongoose", 8 | "crud", 9 | "api", 10 | "helper", 11 | "typescript", 12 | "express" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/derekzyl/expressbolt.git", 17 | "directory": "" 18 | }, 19 | "main": "./dist/index.js", 20 | "types": "./dist/index.d.ts", 21 | "dependencies": { 22 | "@types/express": "^4.17.21", 23 | "express": "^4.18.2", 24 | "http-status": "^1.7.3", 25 | "mongoose": "^8.1.1", 26 | "typescript": "^5.3.3" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/derekzyl/expressbolt/issues", 30 | "email": "derekzyl@gmail.com" 31 | }, 32 | "scripts": { 33 | "build": "tsc" 34 | }, 35 | "homepage": "https://github.com/derekzyl/expressbolt#readme", 36 | "license": "MIT", 37 | "devDependencies": { 38 | "@microsoft/api-extractor": "^7.43.0", 39 | "@types/jest": "^29.5.12", 40 | "@types/node": "^20.11.10", 41 | "jest": "^29.7.0", 42 | "ts-jest": "^29.1.2", 43 | "ts-node": "^10.9.2", 44 | "tsup": "^8.0.2" 45 | } 46 | } -------------------------------------------------------------------------------- /dist/files/responseMessage.d.ts: -------------------------------------------------------------------------------- 1 | import { CustomMessageI } from "./interface.crud"; 2 | /** 3 | * 4 | * @param {CustomMessageI} msg 5 | * @returns 6 | */ 7 | /** 8 | * The function `responseMessage` takes in a custom message object and a configuration option, and 9 | * returns a response object based on the success status of the message. 10 | * @param msg - The `msg` parameter is an object of type `CustomMessageI`. It contains the following 11 | * properties: 12 | * @param {"development" | "production"} [config=development] - The `config` parameter is a string that 13 | * specifies the environment in which the function is being executed. It can have two possible values: 14 | * "development" or "production". By default, it is set to "development". 15 | * @returns an object with different properties based on the value of `msg.success_status`. 16 | */ 17 | declare function responseMessage(msg: CustomMessageI, config?: "development" | "production"): { 18 | message: string; 19 | data: V | undefined; 20 | success: true; 21 | doc_length: number | undefined; 22 | error?: undefined; 23 | stack?: undefined; 24 | } | { 25 | message: string; 26 | error: any; 27 | success: false; 28 | stack: any; 29 | data?: undefined; 30 | doc_length?: undefined; 31 | }; 32 | export default responseMessage; 33 | -------------------------------------------------------------------------------- /dist/test/crud.service.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.service.test.js","sourceRoot":"","sources":["../../src/test/crud.service.test.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAoB,MAAM,SAAS,CAAC;AAEpD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,mDAAmD;IACnD,EAAE,CAAC,qFAAqF,EAAE,GAAS,EAAE;QACnG,UAAU;QACV,MAAM,SAAS,GAAG;YAChB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,EAAE;SACX,CAAC;QAEF,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,EAAE;SACR,CAAC;QAEF,MAAM,KAAK,GAAG;YACZ,IAAI,EAAE,UAAU;SACjB,CAAC;QAEF,MAAM;QACN,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAmB;YACxD,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE;YAC/C,IAAI;YACJ,KAAK;SACN,CAAC,CAAC;QAEH,SAAS;QACT,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,6CAA6C;QAC7C,oCAAoC;QACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtD,CAAC,CAAA,CAAC,CAAC;IAEH,8DAA8D;IAC9D,EAAE,CAAC,wDAAwD,EAAE,GAAS,EAAE;QACtE,UAAU;QACV,MAAM,SAAS,GAAG;YAChB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,CAAC,WAAW,CAAC;SACtB,CAAC;QAEF,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,EAAE;SACR,CAAC;QAEF,MAAM,KAAK,GAAG;YACZ,IAAI,EAAE,UAAU;SACjB,CAAC;QAEF,eAAe;QACf,MAAM,MAAM,CACV,WAAW,CAAC,MAAM,CAAC;YACjB,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE;YAC9C,IAAI;YACJ,KAAK;SACN,CAAC,CACH,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IACtC,CAAC,CAAA,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /src/files/responseMessage.ts: -------------------------------------------------------------------------------- 1 | import { CustomMessageI } from "./interface.crud"; 2 | /** 3 | * 4 | * @param {CustomMessageI} msg 5 | * @returns 6 | */ 7 | /** 8 | * The function `responseMessage` takes in a custom message object and a configuration option, and 9 | * returns a response object based on the success status of the message. 10 | * @param msg - The `msg` parameter is an object of type `CustomMessageI`. It contains the following 11 | * properties: 12 | * @param {"development" | "production"} [config=development] - The `config` parameter is a string that 13 | * specifies the environment in which the function is being executed. It can have two possible values: 14 | * "development" or "production". By default, it is set to "development". 15 | * @returns an object with different properties based on the value of `msg.success_status`. 16 | */ 17 | function responseMessage( 18 | msg: CustomMessageI, 19 | config: "development" | "production" = "production" 20 | ) { 21 | switch (msg.success) { 22 | case true: 23 | return { 24 | message: msg.message, 25 | data: msg.data, 26 | success: msg.success, 27 | doc_length: msg.doc_length, 28 | }; 29 | 30 | case false: 31 | return { 32 | message: msg.message, 33 | error: msg.error, 34 | success: msg.success, 35 | stack: config === "development" ? msg.stack : {}, 36 | }; 37 | } 38 | } 39 | export default responseMessage; 40 | -------------------------------------------------------------------------------- /dist/files/responseMessage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {CustomMessageI} msg 4 | * @returns 5 | */ 6 | /** 7 | * The function `responseMessage` takes in a custom message object and a configuration option, and 8 | * returns a response object based on the success status of the message. 9 | * @param msg - The `msg` parameter is an object of type `CustomMessageI`. It contains the following 10 | * properties: 11 | * @param {"development" | "production"} [config=development] - The `config` parameter is a string that 12 | * specifies the environment in which the function is being executed. It can have two possible values: 13 | * "development" or "production". By default, it is set to "development". 14 | * @returns an object with different properties based on the value of `msg.success_status`. 15 | */ 16 | function responseMessage(msg, config = "production") { 17 | switch (msg.success) { 18 | case true: 19 | return { 20 | message: msg.message, 21 | data: msg.data, 22 | success: msg.success, 23 | doc_length: msg.doc_length, 24 | }; 25 | case false: 26 | return { 27 | message: msg.message, 28 | error: msg.error, 29 | success: msg.success, 30 | stack: config === "development" ? msg.stack : {}, 31 | }; 32 | } 33 | } 34 | export default responseMessage; 35 | //# sourceMappingURL=responseMessage.js.map -------------------------------------------------------------------------------- /src/test/crud.service.test.ts: -------------------------------------------------------------------------------- 1 | import CrudService from "../files/crud.service"; 2 | import CustomError from "../files/error.handler"; 3 | import { MyModel, MyModelInterface } from "./model"; 4 | 5 | describe("CrudService", () => { 6 | // Successfully create a new document and return it 7 | it("should create a new document and return it when the document does not already exist", async () => { 8 | // Arrange 9 | const modelData = { 10 | Model: MyModel, 11 | select: [], 12 | }; 13 | 14 | const data = { 15 | name: "John Doe", 16 | age: 30, 17 | }; 18 | 19 | const check = { 20 | name: "John Doe", 21 | }; 22 | 23 | // Act 24 | const result = await CrudService.create({ 25 | modelData: { Model: MyModel, select: ["-age"] }, 26 | data, 27 | check, 28 | }); 29 | 30 | // Assert 31 | expect(result.success).toBe(true); 32 | // expect(result.data.name).toBe("John Doe"); 33 | // expect(result.data.age).toBe(30); 34 | expect(result.message).toBe("Successfully created"); 35 | }); 36 | 37 | // Throw an error if the document to be created already exists 38 | it("should throw an error when the document already exists", async () => { 39 | // Arrange 40 | const modelData = { 41 | Model: MyModel, 42 | select: ["-password"], 43 | }; 44 | 45 | const data = { 46 | name: "John Doe", 47 | age: 30, 48 | }; 49 | 50 | const check = { 51 | name: "John Doe", 52 | }; 53 | 54 | // Act & Assert 55 | await expect( 56 | CrudService.create({ 57 | modelData: { Model: MyModel, select: ["age"] }, 58 | data, 59 | check, 60 | }) 61 | ).rejects.toThrowError(CustomError); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /dist/files/query.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/files/query.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO;IAKX;;;;;;;;;OASG;IACH,YAAY,KAAU,EAAE,aAAkB;QACxC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,MAAM,QAAQ,qBAAQ,IAAI,CAAC,aAAa,CAAE,CAAC;QAC3C,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3D,cAAc,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAEpD,IAAI,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAE5E,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEnD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;QAClD,MAAM,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAEhC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEhD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED;;gEAEgE;AAChE,eAAe,OAAO,CAAC"} -------------------------------------------------------------------------------- /dist/files/error.handler.js: -------------------------------------------------------------------------------- 1 | import responseMessage from "./responseMessage"; 2 | class CustomError extends Error { 3 | constructor(status, message, isOperational = true) { 4 | super(message); 5 | this.status = status; 6 | this.isOperational = isOperational; 7 | // Set the prototype explicitly. 8 | Object.setPrototypeOf(this, CustomError.prototype); 9 | } 10 | } 11 | // const handleMongooseError = (error: any): string => { 12 | // let errorMessage = "An error occurred"; 13 | // if ( 14 | // error instanceof mongoose.Error.ValidationError && 15 | // (error.code === 11000 || error.code === 11001) 16 | // ) { 17 | // // Duplicate key error (unique constraint violation) 18 | // errorMessage = "Duplicate key error. This record already exists."; 19 | // } else if ( 20 | // error instanceof mongoose.Error && 21 | // error.name === "ValidationError" 22 | // ) { 23 | // // Mongoose validation error 24 | // const errors = Object.values(error.errors).map( 25 | // (error: any) => error.message 26 | // ); 27 | // errorMessage = `Validation error: ${errors.join(", ")}`; 28 | // } else { 29 | // // Other types of errors 30 | // errorMessage = error.message || errorMessage; 31 | // } 32 | // return errorMessage; 33 | // }; 34 | export const errorCenter = ({ error, response, env = "development", }) => { 35 | var _a; 36 | const error_status = (_a = error.statusCode) !== null && _a !== void 0 ? _a : 500; 37 | let error_message = error.message; 38 | //mongodb error 39 | if (error_message.includes("E11000")) { 40 | error_message = "data already exist in the database"; 41 | } 42 | const error_info = error.information; 43 | response.status(error_status).json(responseMessage({ 44 | message: error_info, 45 | success: false, 46 | data: error_message, 47 | stack: error.stack, 48 | }, env)); 49 | }; 50 | export default CustomError; 51 | //# sourceMappingURL=error.handler.js.map -------------------------------------------------------------------------------- /dist/files/interface.crud.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | import { Document, Model, PopulateOptions } from "mongoose"; 26 | type PrefixedKeys = { 27 | [K in keyof T]: `-${string & keyof T}`; 28 | }[keyof T]; 29 | export interface CrudModelI { 30 | Model: Model; 31 | select: (keyof (Pick & V) | PrefixedKeys & V>)[]; 32 | } 33 | export interface PopulateFieldI { 34 | path?: keyof V; 35 | fields?: string[]; 36 | second_layer_populate?: PopulateOptions | string; 37 | } 38 | export interface CustomMessageI { 39 | message: string; 40 | success: boolean; 41 | data?: V; 42 | stack?: any; 43 | error?: any; 44 | doc_length?: number; 45 | } 46 | export {}; 47 | -------------------------------------------------------------------------------- /dist/files/query.d.ts: -------------------------------------------------------------------------------- 1 | declare class Queries { 2 | model: any; 3 | request_query: any; 4 | /** 5 | * The constructor function takes in a model and request_query as parameters and assigns them to the 6 | * corresponding properties of the class. 7 | * @param {any} model - The `model` parameter is used to pass in the model object. This object 8 | * represents the data structure or schema of the entity that you are working with. It typically 9 | * includes properties and methods that define how the data is stored, retrieved, and manipulated. 10 | * @param {any} request_query - The `request_query` parameter is an object that contains the query 11 | * parameters from an HTTP request. These query parameters are typically used to filter or sort data 12 | * when querying a database or API. 13 | */ 14 | constructor(model: any, request_query: any); 15 | /** 16 | * The filter function removes excluded fields from the query object and converts it into a string 17 | * that can be used to filter the model. 18 | * @returns The filter() method is returning the updated instance of the object. 19 | */ 20 | filter(): this; 21 | /** 22 | * The sort() function sorts the model based on the provided sort query parameter or defaults to 23 | * sorting by the created_at field in descending order. 24 | * @returns the updated object with the sorted model. 25 | */ 26 | sort(): this; 27 | /** 28 | * The `limitFields` function selects specific fields from a model based on the `fields` query 29 | * parameter, or selects all fields except `__v` if no `fields` parameter is provided. 30 | * @returns the updated object. 31 | */ 32 | limitFields(): this; 33 | /** 34 | * The `paginate` function is used to set the page and limit for pagination in a TypeScript 35 | * application. 36 | * @returns The paginate() function returns the modified object itself (this) after applying the skip 37 | * and limit operations on the model. 38 | */ 39 | paginate(): this; 40 | } 41 | export default Queries; 42 | -------------------------------------------------------------------------------- /dist/files/crud.model.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.model.js","sourceRoot":"","sources":["../../src/files/crud.model.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/files/error.handler.ts: -------------------------------------------------------------------------------- 1 | import { Response } from "express"; 2 | import responseMessage from "./responseMessage"; 3 | 4 | class CustomError extends Error { 5 | status: number; 6 | isOperational: boolean; 7 | 8 | constructor(status: number, message: string, isOperational = true) { 9 | super(message); 10 | this.status = status; 11 | this.isOperational = isOperational; 12 | 13 | // Set the prototype explicitly. 14 | Object.setPrototypeOf(this, CustomError.prototype); 15 | } 16 | } 17 | 18 | // const handleMongooseError = (error: any): string => { 19 | // let errorMessage = "An error occurred"; 20 | 21 | // if ( 22 | // error instanceof mongoose.Error.ValidationError && 23 | // (error.code === 11000 || error.code === 11001) 24 | // ) { 25 | // // Duplicate key error (unique constraint violation) 26 | // errorMessage = "Duplicate key error. This record already exists."; 27 | // } else if ( 28 | // error instanceof mongoose.Error && 29 | // error.name === "ValidationError" 30 | // ) { 31 | // // Mongoose validation error 32 | // const errors = Object.values(error.errors).map( 33 | // (error: any) => error.message 34 | // ); 35 | // errorMessage = `Validation error: ${errors.join(", ")}`; 36 | // } else { 37 | // // Other types of errors 38 | // errorMessage = error.message || errorMessage; 39 | // } 40 | 41 | // return errorMessage; 42 | // }; 43 | 44 | export const errorCenter = ({ 45 | error, 46 | response, 47 | env = "development", 48 | }: { 49 | error: any; 50 | 51 | response: Response; 52 | 53 | env: "production" | "development"; 54 | }) => { 55 | const error_status = error.statusCode ?? 500; 56 | 57 | let error_message: string = error.message; 58 | 59 | //mongodb error 60 | if (error_message.includes("E11000")) { 61 | error_message = "data already exist in the database"; 62 | } 63 | 64 | const error_info = error.information; 65 | 66 | response.status(error_status).json( 67 | responseMessage( 68 | { 69 | message: error_info, 70 | success: false, 71 | data: error_message, 72 | stack: error.stack, 73 | }, 74 | env 75 | ) 76 | ); 77 | }; 78 | export default CustomError; 79 | -------------------------------------------------------------------------------- /dist/test/model.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | import mongoose from "mongoose"; 26 | export declare const MyModel: mongoose.Model<{ 27 | name?: string | null | undefined; 28 | age?: number | null | undefined; 29 | }, {}, {}, {}, mongoose.Document & { 33 | name?: string | null | undefined; 34 | age?: number | null | undefined; 35 | } & { 36 | _id: mongoose.Types.ObjectId; 37 | }, mongoose.Schema, {}, {}, {}, {}, mongoose.DefaultSchemaOptions, { 38 | name?: string | null | undefined; 39 | age?: number | null | undefined; 40 | }, mongoose.Document> & mongoose.FlatRecord<{ 44 | name?: string | null | undefined; 45 | age?: number | null | undefined; 46 | }> & { 47 | _id: mongoose.Types.ObjectId; 48 | }>>; 49 | export interface MyModelInterface { 50 | name: string; 51 | age: number; 52 | } 53 | -------------------------------------------------------------------------------- /dist/test/crud.controller.test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.controller.test.js","sourceRoot":"","sources":["../../src/test/crud.controller.test.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,cAAc,MAAM,0BAA0B,CAAC;AACtD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,WAAW,MAAM,wBAAwB,CAAC;AAGjD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,yFAAyF;IACzF,EAAE,CAAC,2EAA2E,EAAE,GAAS,EAAE;QACzF,iDAAiD;QACjD,MAAM,OAAO,GAAG,EAAa,CAAC;QAC9B,MAAM,QAAQ,GAAG;YACf,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,cAAc,EAAE;YAClC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;SACO,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAA6B,CAAC;QAElD,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;YACxC,OAAO;YACP,QAAQ;YACR,IAAI;YACJ,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,aAAa;SACnB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG;YAChB,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;SACzB,CAAC;QAEF,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,MAAM;YACZ,GAAG,EAAE,EAAE;SACR,CAAC;QAEF,MAAM,KAAK,GAAG;QACZ,+CAA+C;SAChD,CAAC;QAEF,qEAAqE;QACrE,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YAC/C,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;YACR,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,MAAM,cAAc,CAAC,MAAM,CAAmB;YAC5C,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE;YACtC,IAAI;YACJ,KAAK;SACN,CAAC,CAAC;QAEH,iGAAiG;QACjG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACjE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC;YACzC,cAAc,EAAE,IAAI;YACpB,IAAI,EAAE,EAAE;YACR,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;IACL,CAAC,CAAA,CAAC,CAAC;IAEH,kFAAkF;IAClF,EAAE,CAAC,iFAAiF,EAAE,GAAS,EAAE;QAC/F,iDAAiD;QACjD,MAAM,OAAO,GAAG,EAAa,CAAC;QAC9B,MAAM,QAAQ,GAAG;YACf,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,cAAc,EAAE;YAClC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;SACO,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,EAA6B,CAAC;QAElD,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;YACxC,OAAO;YACP,QAAQ;YACR,IAAI;YACJ,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,aAAa;SACnB,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG;YACX,GAAG,EAAE,EAAE;YACP,IAAI,EAAE,MAAM;SACb,CAAC;QAEF,MAAM,KAAK,GAAG;QACZ,+CAA+C;SAChD,CAAC;QAEF,uDAAuD;QACvD,WAAW,CAAC,MAAM,GAAG,IAAI;aACtB,EAAE,EAAE;aACJ,iBAAiB,CAChB,IAAI,WAAW,CAAC,UAAU,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAC/D,CAAC;QAEJ,2BAA2B;QAC3B,MAAM,cAAc,CAAC,MAAM,CAAmB;YAC5C,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;YACtD,IAAI;YACJ,KAAK;SACN,CAAC,CAAC;QAEH,iEAAiE;QACjE,MAAM,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAC/B,IAAI,WAAW,CAAC,UAAU,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAC/D,CAAC;IACJ,CAAC,CAAA,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /dist/test/crud.service.test.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | import CrudService from "../files/crud.service"; 11 | import CustomError from "../files/error.handler"; 12 | import { MyModel } from "./model"; 13 | describe("CrudService", () => { 14 | // Successfully create a new document and return it 15 | it("should create a new document and return it when the document does not already exist", () => __awaiter(void 0, void 0, void 0, function* () { 16 | // Arrange 17 | const modelData = { 18 | Model: MyModel, 19 | select: [], 20 | }; 21 | const data = { 22 | name: "John Doe", 23 | age: 30, 24 | }; 25 | const check = { 26 | name: "John Doe", 27 | }; 28 | // Act 29 | const result = yield CrudService.create({ 30 | modelData: { Model: MyModel, select: ["-age"] }, 31 | data, 32 | check, 33 | }); 34 | // Assert 35 | expect(result.success).toBe(true); 36 | // expect(result.data.name).toBe("John Doe"); 37 | // expect(result.data.age).toBe(30); 38 | expect(result.message).toBe("Successfully created"); 39 | })); 40 | // Throw an error if the document to be created already exists 41 | it("should throw an error when the document already exists", () => __awaiter(void 0, void 0, void 0, function* () { 42 | // Arrange 43 | const modelData = { 44 | Model: MyModel, 45 | select: ["-password"], 46 | }; 47 | const data = { 48 | name: "John Doe", 49 | age: 30, 50 | }; 51 | const check = { 52 | name: "John Doe", 53 | }; 54 | // Act & Assert 55 | yield expect(CrudService.create({ 56 | modelData: { Model: MyModel, select: ["age"] }, 57 | data, 58 | check, 59 | })).rejects.toThrowError(CustomError); 60 | })); 61 | }); 62 | //# sourceMappingURL=crud.service.test.js.map -------------------------------------------------------------------------------- /src/files/query.ts: -------------------------------------------------------------------------------- 1 | class Queries { 2 | model: any; 3 | 4 | request_query: any; 5 | 6 | /** 7 | * The constructor function takes in a model and request_query as parameters and assigns them to the 8 | * corresponding properties of the class. 9 | * @param {any} model - The `model` parameter is used to pass in the model object. This object 10 | * represents the data structure or schema of the entity that you are working with. It typically 11 | * includes properties and methods that define how the data is stored, retrieved, and manipulated. 12 | * @param {any} request_query - The `request_query` parameter is an object that contains the query 13 | * parameters from an HTTP request. These query parameters are typically used to filter or sort data 14 | * when querying a database or API. 15 | */ 16 | constructor(model: any, request_query: any) { 17 | this.model = model; 18 | this.request_query = request_query; 19 | } 20 | 21 | /** 22 | * The filter function removes excluded fields from the query object and converts it into a string 23 | * that can be used to filter the model. 24 | * @returns The filter() method is returning the updated instance of the object. 25 | */ 26 | filter() { 27 | const queryObj = { ...this.request_query }; 28 | const excludedFields = ["page", "sort", "limit", "fields"]; 29 | excludedFields.forEach((el) => delete queryObj[el]); 30 | 31 | let queryStr = JSON.stringify(queryObj); 32 | queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`); 33 | 34 | this.model = this.model.find(JSON.parse(queryStr)); 35 | 36 | return this; 37 | } 38 | 39 | /** 40 | * The sort() function sorts the model based on the provided sort query parameter or defaults to 41 | * sorting by the created_at field in descending order. 42 | * @returns the updated object with the sorted model. 43 | */ 44 | sort() { 45 | if (this.request_query.sort) { 46 | const sortBy = this.request_query.sort.split(",").join(" "); 47 | this.model = this.model.sort(sortBy); 48 | } else { 49 | this.model = this.model.sort("-createdAt"); 50 | } 51 | 52 | return this; 53 | } 54 | 55 | /** 56 | * The `limitFields` function selects specific fields from a model based on the `fields` query 57 | * parameter, or selects all fields except `__v` if no `fields` parameter is provided. 58 | * @returns the updated object. 59 | */ 60 | limitFields() { 61 | if (this.request_query.fields) { 62 | const fields = this.request_query.fields.split(",").join(" "); 63 | this.model = this.model.select(fields); 64 | } else { 65 | this.model = this.model.select("-__v"); 66 | } 67 | 68 | return this; 69 | } 70 | 71 | /** 72 | * The `paginate` function is used to set the page and limit for pagination in a TypeScript 73 | * application. 74 | * @returns The paginate() function returns the modified object itself (this) after applying the skip 75 | * and limit operations on the model. 76 | */ 77 | paginate() { 78 | const page = this.request_query.page * 1 || 1; 79 | const limit = this.request_query.limit * 1 || 100; 80 | const skip = (page - 1) * limit; 81 | 82 | this.model = this.model.skip(skip).limit(limit); 83 | 84 | return this; 85 | } 86 | } 87 | 88 | /* `export default Queries;` is exporting the `Queries` class as the default export of the module. This 89 | allows other modules to import the `Queries` class using the `import` statement. For example, in 90 | another module, you can import the `Queries` class like this: */ 91 | export default Queries; 92 | -------------------------------------------------------------------------------- /dist/files/query.js: -------------------------------------------------------------------------------- 1 | class Queries { 2 | /** 3 | * The constructor function takes in a model and request_query as parameters and assigns them to the 4 | * corresponding properties of the class. 5 | * @param {any} model - The `model` parameter is used to pass in the model object. This object 6 | * represents the data structure or schema of the entity that you are working with. It typically 7 | * includes properties and methods that define how the data is stored, retrieved, and manipulated. 8 | * @param {any} request_query - The `request_query` parameter is an object that contains the query 9 | * parameters from an HTTP request. These query parameters are typically used to filter or sort data 10 | * when querying a database or API. 11 | */ 12 | constructor(model, request_query) { 13 | this.model = model; 14 | this.request_query = request_query; 15 | } 16 | /** 17 | * The filter function removes excluded fields from the query object and converts it into a string 18 | * that can be used to filter the model. 19 | * @returns The filter() method is returning the updated instance of the object. 20 | */ 21 | filter() { 22 | const queryObj = Object.assign({}, this.request_query); 23 | const excludedFields = ["page", "sort", "limit", "fields"]; 24 | excludedFields.forEach((el) => delete queryObj[el]); 25 | let queryStr = JSON.stringify(queryObj); 26 | queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`); 27 | this.model = this.model.find(JSON.parse(queryStr)); 28 | return this; 29 | } 30 | /** 31 | * The sort() function sorts the model based on the provided sort query parameter or defaults to 32 | * sorting by the created_at field in descending order. 33 | * @returns the updated object with the sorted model. 34 | */ 35 | sort() { 36 | if (this.request_query.sort) { 37 | const sortBy = this.request_query.sort.split(",").join(" "); 38 | this.model = this.model.sort(sortBy); 39 | } 40 | else { 41 | this.model = this.model.sort("-createdAt"); 42 | } 43 | return this; 44 | } 45 | /** 46 | * The `limitFields` function selects specific fields from a model based on the `fields` query 47 | * parameter, or selects all fields except `__v` if no `fields` parameter is provided. 48 | * @returns the updated object. 49 | */ 50 | limitFields() { 51 | if (this.request_query.fields) { 52 | const fields = this.request_query.fields.split(",").join(" "); 53 | this.model = this.model.select(fields); 54 | } 55 | else { 56 | this.model = this.model.select("-__v"); 57 | } 58 | return this; 59 | } 60 | /** 61 | * The `paginate` function is used to set the page and limit for pagination in a TypeScript 62 | * application. 63 | * @returns The paginate() function returns the modified object itself (this) after applying the skip 64 | * and limit operations on the model. 65 | */ 66 | paginate() { 67 | const page = this.request_query.page * 1 || 1; 68 | const limit = this.request_query.limit * 1 || 100; 69 | const skip = (page - 1) * limit; 70 | this.model = this.model.skip(skip).limit(limit); 71 | return this; 72 | } 73 | } 74 | /* `export default Queries;` is exporting the `Queries` class as the default export of the module. This 75 | allows other modules to import the `Queries` class using the `import` statement. For example, in 76 | another module, you can import the `Queries` class like this: */ 77 | export default Queries; 78 | //# sourceMappingURL=query.js.map -------------------------------------------------------------------------------- /src/test/crud.controller.test.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import httpStatus from "http-status"; 3 | import { Model } from "mongoose"; 4 | import CrudController from "../files/crud.controller"; 5 | import CrudService from "../files/crud.service"; 6 | import CustomError from "../files/error.handler"; 7 | import { MyModelInterface } from "./model"; 8 | 9 | describe("CrudController", () => { 10 | // should successfully create a new document when create method is called with valid data 11 | it("should create a new document when create method is called with valid data", async () => { 12 | // Initialize the necessary variables and objects 13 | const request = {} as Request; 14 | const response = { 15 | status: jest.fn().mockReturnThis(), 16 | json: jest.fn(), 17 | } as unknown as Response; 18 | const next = jest.fn() as unknown as NextFunction; 19 | 20 | const crudController = new CrudController({ 21 | request, 22 | response, 23 | next, 24 | useNext: true, 25 | env: "development", 26 | }); 27 | 28 | const modelData = { 29 | Model: Model, 30 | select: ["-name", "age"], 31 | }; 32 | 33 | const data = { 34 | name: "john", 35 | age: 30, 36 | }; 37 | 38 | const check = { 39 | // filter query to check if data already exists 40 | }; 41 | 42 | // Mock the CrudService.create method to return a successful response 43 | CrudService.create = jest.fn().mockResolvedValue({ 44 | success_status: true, 45 | data: {}, 46 | message: "Successfully created", 47 | }); 48 | 49 | // Invoke the create method 50 | await crudController.create({ 51 | modelData: { Model, select: ["-age"] }, 52 | data, 53 | check, 54 | }); 55 | 56 | // Assert that the response status is 201 and the json method is called with the correct response 57 | expect(response.status).toHaveBeenCalledWith(httpStatus.CREATED); 58 | expect(response.json).toHaveBeenCalledWith({ 59 | success_status: true, 60 | data: {}, 61 | message: "Successfully created", 62 | }); 63 | }); 64 | 65 | // should return an error response when create method is called with existing data 66 | it("should return an error response when create method is called with existing data", async () => { 67 | // Initialize the necessary variables and objects 68 | const request = {} as Request; 69 | const response = { 70 | status: jest.fn().mockReturnThis(), 71 | json: jest.fn(), 72 | } as unknown as Response; 73 | const next = jest.fn() as unknown as NextFunction; 74 | 75 | const crudController = new CrudController({ 76 | request, 77 | response, 78 | next, 79 | useNext: true, 80 | env: "development", 81 | }); 82 | 83 | const data = { 84 | age: 34, 85 | name: "john", 86 | }; 87 | 88 | const check = { 89 | // filter query to check if data already exists 90 | }; 91 | 92 | // Mock the CrudService.create method to throw an error 93 | CrudService.create = jest 94 | .fn() 95 | .mockRejectedValue( 96 | new CustomError(httpStatus.BAD_REQUEST, "Data already exists") 97 | ); 98 | 99 | // Invoke the create method 100 | await crudController.create({ 101 | modelData: { Model, select: ["-__v", "-_id", "-age"] }, 102 | data, 103 | check, 104 | }); 105 | 106 | // Assert that the next function is called with the correct error 107 | expect(next).toHaveBeenCalledWith( 108 | new CustomError(httpStatus.BAD_REQUEST, "Data already exists") 109 | ); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /dist/files/crud.model.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | import mongoose, { CompileModelOptions, Document, Model, Schema, SchemaDefinitionProperty } from "mongoose"; 26 | type DynamicField = { 27 | [key in keyof T]: SchemaDefinitionProperty; 28 | }; 29 | type modelT = (name: string, schema?: Schema | Schema, collection?: string, options?: CompileModelOptions) => Model; 30 | /** 31 | * The function `generateDynamicSchema` creates a dynamic Mongoose schema based on the provided fields, 32 | * modelName, plugins, and schemaOptions. 33 | * @param - The `generateDynamicSchema` function takes in the following parameters: 34 | * @returns The `generateDynamicSchema` function returns an object with two properties: 35 | * 1. `model`: This is the model created using the `model` function from Mongoose, with the specified 36 | * `modelName` and the generated schema (`schemaDef`). 37 | * 2. `schema`: This is the generated schema object (`schemaDef`) that defines the structure of the 38 | * model. 39 | */ 40 | declare const generateDynamicSchema: ({ fields, modelName, plugins, schemaOptions, model }: { 41 | modelName: string; 42 | fields: DynamicField; 43 | plugins?: any[] | undefined; 44 | schemaOptions?: Record | undefined; 45 | model: modelT & U>; 46 | }) => { 47 | model: mongoose.Model, {}, {}, {}, mongoose.IfAny, any, mongoose.Document> & mongoose.Require_id>>, any>; 48 | schema: mongoose.Schema, mongoose.Model, {}, {}, {}, mongoose.IfAny, any, mongoose.Document> & mongoose.Require_id>>, any> & U, {}, {}, {}, {}, mongoose.DefaultSchemaOptions, mongoose.ObtainDocumentType, mongoose.ResolveSchemaOptions>, mongoose.IfAny, mongoose.ResolveSchemaOptions>>, any, mongoose.Document, mongoose.ResolveSchemaOptions>>> & mongoose.Require_id, mongoose.ResolveSchemaOptions>>>>>; 49 | }; 50 | export default generateDynamicSchema; 51 | -------------------------------------------------------------------------------- /dist/files/crud.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.controller.js","sourceRoot":"","sources":["../../src/files/crud.controller.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/files/crud.model.js: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema } from "mongoose"; 2 | /** 3 | * The function `generateDynamicSchema` creates a dynamic Mongoose schema based on the provided fields, 4 | * modelName, plugins, and schemaOptions. 5 | * @param - The `generateDynamicSchema` function takes in the following parameters: 6 | * @returns The `generateDynamicSchema` function returns an object with two properties: 7 | * 1. `model`: This is the model created using the `model` function from Mongoose, with the specified 8 | * `modelName` and the generated schema (`schemaDef`). 9 | * 2. `schema`: This is the generated schema object (`schemaDef`) that defines the structure of the 10 | * model. 11 | */ 12 | const generateDynamicSchema = ({ fields, modelName, plugins, schemaOptions, model }) => { 13 | const schemaDefinition = {}; 14 | // Iterate through the fields and define the schema 15 | // eslint-disable-next-line no-restricted-syntax 16 | for (const [keys, values] of Object.entries(fields)) { 17 | const fieldOptions = values; 18 | /* The `if (typeof fieldOptions === "object") {` condition in the code snippet is checking if the 19 | `fieldOptions` variable is of type "object". If the `fieldOptions` is an object, it means that 20 | the field definition for a specific key in the dynamic schema is an object with additional 21 | properties defining the field type and options. In this case, the code processes the field 22 | options accordingly to define the schema property for that key in the Mongoose schema being 23 | generated... */ 24 | if (typeof fieldOptions === "object") { 25 | schemaDefinition[keys] = fieldOptions; 26 | } 27 | else if (Array.isArray(fieldOptions)) { 28 | switch (typeof fieldOptions[0]) { 29 | case "string": 30 | schemaDefinition[keys] = { 31 | type: [String], 32 | }; 33 | break; 34 | case "number": 35 | schemaDefinition[keys] = { 36 | type: [Number], 37 | }; 38 | break; 39 | case "object": 40 | { 41 | Object.prototype.hasOwnProperty.call(fieldOptions[0], "ref") && 42 | Object.prototype.hasOwnProperty.call(fieldOptions[0], "type") && 43 | fieldOptions[0].type === mongoose.Schema.Types.ObjectId 44 | ? (schemaDefinition[keys] = fieldOptions) 45 | : (schemaDefinition[keys] = generateDynamicSchema({ 46 | modelName: "", 47 | fields: fieldOptions[0], 48 | schemaOptions: schemaOptions !== null && schemaOptions !== void 0 ? schemaOptions : {}, 49 | model 50 | }).schema); 51 | } 52 | break; 53 | case "bigint": 54 | schemaDefinition[keys] = { 55 | type: [BigInt], 56 | }; 57 | break; 58 | case "boolean": 59 | schemaDefinition[keys] = { 60 | type: [Boolean], 61 | }; 62 | break; 63 | case "symbol": 64 | schemaDefinition[keys] = { 65 | type: [String], 66 | }; 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | else { 73 | schemaDefinition[keys] = { 74 | type: fieldOptions, 75 | }; 76 | } 77 | } 78 | // Create and return the schema 79 | const schemaDef = new Schema(schemaDefinition, schemaOptions); 80 | plugins === null || plugins === void 0 ? void 0 : plugins.forEach((plugin) => schemaDef.plugin(plugin)); 81 | return { 82 | model: model(modelName, schemaDef), 83 | schema: schemaDef, 84 | }; 85 | }; 86 | export default generateDynamicSchema; 87 | //# sourceMappingURL=crud.model.js.map -------------------------------------------------------------------------------- /dist/test/crud.controller.test.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | import httpStatus from "http-status"; 11 | import { Model } from "mongoose"; 12 | import CrudController from "../files/crud.controller"; 13 | import CrudService from "../files/crud.service"; 14 | import CustomError from "../files/error.handler"; 15 | describe("CrudController", () => { 16 | // should successfully create a new document when create method is called with valid data 17 | it("should create a new document when create method is called with valid data", () => __awaiter(void 0, void 0, void 0, function* () { 18 | // Initialize the necessary variables and objects 19 | const request = {}; 20 | const response = { 21 | status: jest.fn().mockReturnThis(), 22 | json: jest.fn(), 23 | }; 24 | const next = jest.fn(); 25 | const crudController = new CrudController({ 26 | request, 27 | response, 28 | next, 29 | useNext: true, 30 | env: "development", 31 | }); 32 | const modelData = { 33 | Model: Model, 34 | select: ["-name", "age"], 35 | }; 36 | const data = { 37 | name: "john", 38 | age: 30, 39 | }; 40 | const check = { 41 | // filter query to check if data already exists 42 | }; 43 | // Mock the CrudService.create method to return a successful response 44 | CrudService.create = jest.fn().mockResolvedValue({ 45 | success_status: true, 46 | data: {}, 47 | message: "Successfully created", 48 | }); 49 | // Invoke the create method 50 | yield crudController.create({ 51 | modelData: { Model, select: ["-age"] }, 52 | data, 53 | check, 54 | }); 55 | // Assert that the response status is 201 and the json method is called with the correct response 56 | expect(response.status).toHaveBeenCalledWith(httpStatus.CREATED); 57 | expect(response.json).toHaveBeenCalledWith({ 58 | success_status: true, 59 | data: {}, 60 | message: "Successfully created", 61 | }); 62 | })); 63 | // should return an error response when create method is called with existing data 64 | it("should return an error response when create method is called with existing data", () => __awaiter(void 0, void 0, void 0, function* () { 65 | // Initialize the necessary variables and objects 66 | const request = {}; 67 | const response = { 68 | status: jest.fn().mockReturnThis(), 69 | json: jest.fn(), 70 | }; 71 | const next = jest.fn(); 72 | const crudController = new CrudController({ 73 | request, 74 | response, 75 | next, 76 | useNext: true, 77 | env: "development", 78 | }); 79 | const data = { 80 | age: 34, 81 | name: "john", 82 | }; 83 | const check = { 84 | // filter query to check if data already exists 85 | }; 86 | // Mock the CrudService.create method to throw an error 87 | CrudService.create = jest 88 | .fn() 89 | .mockRejectedValue(new CustomError(httpStatus.BAD_REQUEST, "Data already exists")); 90 | // Invoke the create method 91 | yield crudController.create({ 92 | modelData: { Model, select: ["-__v", "-_id", "-age"] }, 93 | data, 94 | check, 95 | }); 96 | // Assert that the next function is called with the correct error 97 | expect(next).toHaveBeenCalledWith(new CustomError(httpStatus.BAD_REQUEST, "Data already exists")); 98 | })); 99 | }); 100 | //# sourceMappingURL=crud.controller.test.js.map -------------------------------------------------------------------------------- /src/files/crud.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { 2 | CompileModelOptions, 3 | Document, 4 | Model, 5 | Schema, 6 | SchemaDefinition, 7 | SchemaDefinitionProperty 8 | } from "mongoose"; 9 | 10 | // Interface for dynamic field definition 11 | type DynamicField = { 12 | [key in keyof T]: SchemaDefinitionProperty; 13 | }; 14 | type modelT =( 15 | name: string, 16 | schema?: Schema | Schema, 17 | collection?: string, 18 | options?: CompileModelOptions 19 | )=> Model; 20 | 21 | 22 | /** 23 | * The function `generateDynamicSchema` creates a dynamic Mongoose schema based on the provided fields, 24 | * modelName, plugins, and schemaOptions. 25 | * @param - The `generateDynamicSchema` function takes in the following parameters: 26 | * @returns The `generateDynamicSchema` function returns an object with two properties: 27 | * 1. `model`: This is the model created using the `model` function from Mongoose, with the specified 28 | * `modelName` and the generated schema (`schemaDef`). 29 | * 2. `schema`: This is the generated schema object (`schemaDef`) that defines the structure of the 30 | * model. 31 | */ 32 | const generateDynamicSchema = ({ 33 | fields, 34 | modelName, 35 | plugins, 36 | schemaOptions, 37 | model 38 | 39 | }: { 40 | modelName: string; 41 | fields: DynamicField; 42 | plugins?: any[]; 43 | schemaOptions?: Record; 44 | model:modelT &U> 45 | 46 | }) => { 47 | type ITDoc = T & Document; 48 | type ITModel = Model & U; 49 | const schemaDefinition: SchemaDefinition = {}; 50 | 51 | // Iterate through the fields and define the schema 52 | // eslint-disable-next-line no-restricted-syntax 53 | for (const [keys, values] of Object.entries(fields)) { 54 | const fieldOptions = values; 55 | 56 | /* The `if (typeof fieldOptions === "object") {` condition in the code snippet is checking if the 57 | `fieldOptions` variable is of type "object". If the `fieldOptions` is an object, it means that 58 | the field definition for a specific key in the dynamic schema is an object with additional 59 | properties defining the field type and options. In this case, the code processes the field 60 | options accordingly to define the schema property for that key in the Mongoose schema being 61 | generated... */ 62 | if (typeof fieldOptions === "object") { 63 | schemaDefinition[keys] = fieldOptions as SchemaDefinitionProperty; 64 | } else if (Array.isArray(fieldOptions)) { 65 | switch (typeof fieldOptions[0]) { 66 | case "string": 67 | schemaDefinition[keys] = { 68 | type: [String], 69 | }; 70 | break; 71 | case "number": 72 | schemaDefinition[keys] = { 73 | type: [Number], 74 | }; 75 | break; 76 | case "object": 77 | { 78 | Object.prototype.hasOwnProperty.call(fieldOptions[0], "ref") && 79 | Object.prototype.hasOwnProperty.call(fieldOptions[0], "type") && 80 | fieldOptions[0].type === mongoose.Schema.Types.ObjectId 81 | ? (schemaDefinition[keys] = fieldOptions) 82 | : (schemaDefinition[keys] = generateDynamicSchema({ 83 | modelName: "", 84 | fields: fieldOptions[0], 85 | schemaOptions: schemaOptions ?? {}, 86 | model 87 | }).schema); 88 | } 89 | break; 90 | case "bigint": 91 | schemaDefinition[keys] = { 92 | type: [BigInt], 93 | }; 94 | break; 95 | case "boolean": 96 | schemaDefinition[keys] = { 97 | type: [Boolean], 98 | }; 99 | break; 100 | case "symbol": 101 | schemaDefinition[keys] = { 102 | type: [String], 103 | }; 104 | break; 105 | default: 106 | break; 107 | } 108 | } else { 109 | schemaDefinition[keys] = { 110 | type: fieldOptions, 111 | }; 112 | } 113 | } 114 | 115 | // Create and return the schema 116 | const schemaDef = new Schema(schemaDefinition, schemaOptions); 117 | plugins?.forEach((plugin) => schemaDef.plugin(plugin)); 118 | return { 119 | model: model(modelName, schemaDef), 120 | schema: schemaDef, 121 | }; 122 | }; 123 | 124 | export default generateDynamicSchema; 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /dist/files/crud.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"crud.service.js","sourceRoot":"","sources":["../../src/files/crud.service.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/all.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"all.js","sourceRoot":"","sources":["../src/all.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/files/crud.service.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | import { request } from "express"; 26 | import { Document, FilterQuery, UpdateQuery } from "mongoose"; 27 | import { CrudModelI, CustomMessageI, PopulateFieldI } from "./interface.crud"; 28 | declare class CrudService { 29 | /** 30 | * The `create` function creates a document in a MongoDB collection. 31 | * It takes in a `CrudModelI` object, the data to be created, and a filter to check for existing documents. 32 | * It returns a Promise that resolves to a response message containing the successfully created document. 33 | * 34 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 35 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the MongoDB collection. 36 | * @param {T} param.data - The data to be created. 37 | * @param {FilterQuery} param.check - A filter to check for existing documents. 38 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created document. 39 | */ 40 | static create({ check, modelData, data, }: { 41 | modelData: CrudModelI; 42 | data: T; 43 | check: FilterQuery; 44 | }): Promise>; 45 | /** 46 | * The function `createMany` creates multiple documents in a MongoDB collection based on an array of data. 47 | * It takes in a `CrudModelI` object, an array of data, and an array of filters to check for existing documents. 48 | * It returns a Promise that resolves to a response message containing the successfully created documents. 49 | * 50 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 51 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the model to be created. 52 | * @param {T[]} param.data - An array of data to be created. 53 | * @param {FilterQuery[]} param.check - An array of filters to check for existing documents. 54 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created documents. 55 | */ 56 | static createMany({ check, data, modelData, }: { 57 | modelData: CrudModelI; 58 | data: T[]; 59 | check: Partial>[]; 60 | }): Promise>; 61 | /** 62 | * The `update` static method is used to update a document in a database based on specified criteria. 63 | * 64 | * @param {Object} params - The `params` object contains the necessary parameters for the update operation. 65 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to update. 66 | * @param {UpdateQuery} params.data - The `data` parameter represents the update data. 67 | * @param {FilterQuery} params.filter - The `filter` parameter represents the criteria to 68 | * identify the document(s) to update. 69 | * @returns {Promise} The method returns a promise that resolves to a response message containing the 70 | * updated document and a success status. 71 | */ 72 | static update({ data, filter, modelData, }: { 73 | modelData: CrudModelI; 74 | data: UpdateQuery; 75 | filter: FilterQuery; 76 | }): Promise>; 77 | /** 78 | * This function retrieves multiple documents from a MongoDB collection based on the provided 79 | * filter, model, populate, and query parameters. 80 | * @param {Object} params - The `params` object contains the necessary parameters for the getMany 81 | * operation. 82 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to 83 | * retrieve documents from. 84 | * @param {FilterQuery | null} params.filter - The `filter` parameter represents the criteria to 85 | * identify the documents to retrieve. 86 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The `populate` parameter 87 | * represents the fields to populate in the retrieved documents. 88 | * @param {typeof request.query} params.query - The `query` parameter represents the query parameters 89 | * from the request. 90 | * @returns {Promise} The method returns a promise that resolves to a response message object 91 | * containing the retrieved documents, a success status, and other relevant information. 92 | */ 93 | static getMany({ filter, modelData, populate, query, }: { 94 | modelData: CrudModelI; 95 | query: typeof request.query; 96 | populate: PopulateFieldI | PopulateFieldI[]; 97 | filter: FilterQuery | null; 98 | }): Promise>; 99 | private static getModelLength; 100 | /** 101 | * The function `populateModel` populates a model with specified fields and nested fields based on 102 | * the provided criteria. 103 | * @param {any} modelFind - The `modelFind` parameter is the model object that you want to populate 104 | * with additional data. It could be a Mongoose model instance or any other object that supports 105 | * population of fields. 106 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The `populate` parameter in the 107 | * `populateModel` function is used to specify which fields in the model should be populated with 108 | * additional data. It can be a single `PopulateFieldI` object or an array of `PopulateFieldI` 109 | * objects. Each `PopulateFieldI` object 110 | * @returns The `populateModel` function returns the result of populating the specified fields in the 111 | * modelFind object based on the populate configuration provided. 112 | */ 113 | private static populateModel; 114 | /** 115 | * The function `delete` is an asynchronous static method in TypeScript that deletes data based on 116 | * the provided filter query and model data, handling errors and returning a success message. 117 | * @param - The `delete` method you provided is an asynchronous function that deletes data from a 118 | * database using the given `modelData` and `data` parameters. Here's a breakdown of the parameters: 119 | * @returns The `delete` method is returning a Promise that resolves to an object with the following 120 | * properties: 121 | * - `success`: a boolean value indicating the success status of the deletion operation 122 | * - `message`: a string message indicating that the deletion was successful 123 | * - `data`: a string value indicating that the data was deleted 124 | */ 125 | static delete({ data, modelData, }: { 126 | modelData: CrudModelI; 127 | data: FilterQuery; 128 | }): Promise>; 129 | /** 130 | * This TypeScript function deletes multiple documents based on a filter query using the deleteMany 131 | * method and returns a success message. 132 | * @param - The `deleteMany` function takes in two parameters: 133 | * @returns The `deleteMany` function returns a Promise that resolves to an object with the following 134 | * properties: 135 | * - `success`: a boolean indicating the success status of the deletion operation (true in 136 | * this case) 137 | * - `message`: a string message indicating that the deletion was successful ("Deleted successfully" 138 | * in this case) 139 | * - `data`: a string value indicating that the data was deleted ("deleted" in this case 140 | */ 141 | static deleteMany({ data, modelData, }: { 142 | modelData: CrudModelI; 143 | data: FilterQuery; 144 | }): Promise>; 145 | /** 146 | * Asynchronously retrieves a single document from a MongoDB collection. 147 | * 148 | * @param {Object} params - The parameters for retrieving the document. 149 | * @param {CrudModelI} params.modelData - The model data for the MongoDB collection. 150 | * @param {FilterQuery} params.data - The filter query for finding the document. 151 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The populate options for the document. 152 | * @returns {Promise} The response message containing the retrieved document. 153 | * @throws {CustomError} If the document is not found. 154 | */ 155 | static getOne({ modelData, data, populate, }: { 156 | modelData: CrudModelI; 157 | data: FilterQuery; 158 | populate: PopulateFieldI | PopulateFieldI[]; 159 | }): Promise>; 160 | } 161 | export default CrudService; 162 | -------------------------------------------------------------------------------- /dist/files/crud.controller.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | import { NextFunction, Request, Response } from "express"; 26 | import { FilterQuery, UpdateQuery } from "mongoose"; 27 | import { CrudModelI, PopulateFieldI } from "./interface.crud"; 28 | declare class CrudController { 29 | request: Request; 30 | response: Response; 31 | next: NextFunction; 32 | useNext: boolean; 33 | env: "development" | "production"; 34 | /** 35 | * Constructor for creating an instance of the class. 36 | * 37 | * @param {Object} options - The options object containing request, response, next, useNext, and env. 38 | * @param {Request} options.request - The request object. 39 | * @param {Response} options.response - The response object. 40 | * @param {NextFunction} options.next - The next function. 41 | * @param {boolean} [options.useNext=true] - Flag indicating whether to use the next function. 42 | * @param {"development" | "production"} [options.env="development"] - The environment setting. 43 | */ 44 | constructor({ request, response, next, useNext, env, }: { 45 | request: Request; 46 | response: Response; 47 | next: NextFunction; 48 | useNext?: boolean; 49 | env?: "development" | "production"; 50 | }); 51 | /** 52 | * A description of the entire function. 53 | * 54 | * @param {CrudModelI} modelData - description of parameter 55 | * @param {T} data - description of parameter 56 | * @param {FilterQuery} check - description of parameter 57 | * @return {Promise} description of return value 58 | */ 59 | create({ modelData, data, check, }: { 60 | modelData: CrudModelI; 61 | data: T; 62 | check: FilterQuery; 63 | }): Promise; 64 | /** 65 | * A function to create multiple records in the database. 66 | * 67 | * @param {Object} param0 - Object containing modelData, data, and check 68 | * @param {CrudModelI} param0.modelData - The model data for the records 69 | * @param {T[]} param0.data - The array of records to be created 70 | * @param {FilterQuery[]} param0.check - The filter query for checking existing records 71 | * @return {Promise} A promise that resolves to a response or next function, or void in case of error 72 | */ 73 | createMany({ check, modelData, data, }: { 74 | modelData: CrudModelI; 75 | data: T[]; 76 | check: FilterQuery[]; 77 | }): Promise; 78 | /** 79 | * The function updates a model using the provided data and filter, and returns the updated response. 80 | * @param {CrudModelI} modelData - A variable representing a CRUD model interface. It is used to 81 | * perform CRUD operations on a specific model. 82 | * @param data - The `data` parameter is an object that contains the fields and values to be updated 83 | * in the database. It represents the update query that will be executed on the `modelData` collection. 84 | * The type `T` represents the shape of the data object. 85 | * @param filter - The `filter` parameter is a query object used to filter the documents to be 86 | * updated in the database. It specifies the criteria that the documents must meet in order to be 87 | * updated. The `filter` parameter is of type `FilterQuery`, where `U` is the type of the filter 88 | * @returns a promise that resolves to a response object. 89 | */ 90 | update({ data, modelData, filter, }: { 91 | modelData: CrudModelI; 92 | data: UpdateQuery; 93 | filter: FilterQuery; 94 | }): Promise>>; 95 | /** 96 | * Fetches multiple documents from the database based on the provided query parameters. 97 | * Supports pagination, sorting, and field selection. 98 | * Can fetch data from multiple models if an array of models is provided. 99 | * 100 | * @param {CrudModelI } modelData - The model(s) to fetch data from. 101 | * @param {typeof this.request.query} query - The query parameters. 102 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The fields to populate. 103 | * @param {FilterQuery | null} [category=null] - The category filter. 104 | * @returns {Promise} - JSON response with the fetched data, success status, message, and the length of the fetched documents. 105 | * 106 | * @example 107 | * const crud = new Crud(request, response, next); 108 | * const modelData = { 109 | * Model: Model, 110 | * select: ['field1','field2'], 111 | * }; 112 | * const query = request.query; 113 | * const populate = { 114 | * model: 'relatedModel', 115 | * fields: ['field1', 'field2'], 116 | * second_layer_populate: 'nestedModel', 117 | * }; 118 | * const category = { category: 'category1' }; 119 | * 120 | * await crud.getMany(modelData, query, populate, category); 121 | * 122 | * This example initializes the `Crud` class object and calls the `getMany` method to fetch documents from the `Model` based on the provided query parameters. 123 | * It also specifies the fields to be populated and the category filter. 124 | */ 125 | getMany({ filter, modelData, populate, query, }: { 126 | modelData: CrudModelI; 127 | query: any; 128 | populate: PopulateFieldI | PopulateFieldI[]; 129 | filter: FilterQuery | null; 130 | }): Promise; 131 | /** 132 | * The function is an asynchronous method that deletes data from a model using a filter query and 133 | * returns the response. 134 | * @param {CrudModelI} modelData - The modelData parameter is an instance of a CRUD model. It represents 135 | * the model or schema that you want to perform the delete operation on. It should implement the 136 | * CrudModelI interface. 137 | * @param data - The `data` parameter is a filter query object used to specify the criteria for 138 | * deleting documents from the database. It is of type `FilterQuery`, where `T` represents the 139 | * type of the documents being deleted. The `FilterQuery` type is typically used in MongoDB queries 140 | * to filter documents 141 | * @example - The example below shows how to use the `delete` function to delete a document from the 142 | * `Model` using a filter query. 143 | * const crud = new Crud(request, response, next); 144 | * const modelData = { 145 | * Model: Model, 146 | * select: 'field1 field2', 147 | * } 148 | */ 149 | delete({ data, modelData, }: { 150 | modelData: CrudModelI; 151 | data: FilterQuery; 152 | }): Promise; 153 | /** 154 | * This TypeScript function deletes multiple documents based on a filter query using a CRUD service. 155 | * @param - The `deleteMany` function takes in two parameters: 156 | * @returns The `deleteMany` method is returning the response from the `CrudService.deleteMany` 157 | * function call if successful. If there is an error, it will either call `this.next(error)` if 158 | * `this.useNext` is true, or it will call `errorCenter` function with the provided parameters. 159 | */ 160 | deleteMany({ data, modelData, }: { 161 | modelData: CrudModelI; 162 | data: FilterQuery; 163 | }): Promise; 164 | /** 165 | * The function retrieves one document from a database using a given model, data filter, and optional 166 | * population fields, and returns the response as JSON. 167 | * @param {CrudModelI} modelData - The `modelData` parameter is the model that you want to perform the 168 | * operation on. It should be an instance of a CRUD model that implements the `CrudModelI` interface. 169 | * @param data - The `data` parameter is a filter query object used to specify the conditions for 170 | * finding a document in the database. It can be used to filter the documents based on certain 171 | * criteria such as equality, inequality, greater than, less than, etc. The type `T` represents the 172 | * type of the data 173 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The "populate" parameter is used to specify 174 | * which fields of the retrieved document(s) should be populated with their referenced documents. It 175 | * can be a single field or an array of fields to populate. 176 | * @example 177 | * ```ts 178 | * CrudModelI { 179 | * model: Model; 180 | *select: string; 181 | * } 182 | * populate: { model?: string | undefined; fields?: string | undefined } ``` 183 | * 184 | * @example 185 | * // returns a response 186 | * getOne< T the model >(modelData, category, populate: { model?: string | undefined; fields?: string | undefined)}) 187 | */ 188 | getOne({ data, modelData, populate, }: { 189 | modelData: CrudModelI; 190 | data: FilterQuery; 191 | populate: PopulateFieldI | PopulateFieldI[]; 192 | }): Promise; 193 | } 194 | export default CrudController; 195 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExpressBolt ⚡ 2 | 3 | [![npm version](https://badge.fury.io/js/expressbolt.svg)](https://badge.fury.io/js/expressbolt) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | [![GitHub stars](https://img.shields.io/github/stars/derekzyl/expressbolt.svg?style=social&label=Star)](https://github.com/derekzyl/expressbolt) 6 | 7 | > A powerful middleware layer for Express.js that simplifies CRUD operations with MongoDB and Mongoose. Build RESTful APIs faster with type-safe operations, automatic error handling, and advanced querying capabilities. 8 | 9 | ## ✨ Key Features 10 | 11 | - 🔥 **Simplified CRUD Operations** - Create, read, update, and delete with minimal boilerplate 12 | - 🛡️ **Type Safety** - Full TypeScript support with strict type checking 13 | - 📊 **Advanced Querying** - Built-in filtering, sorting, pagination, and population 14 | - 🎯 **Dynamic Schema Generation** - Generate Mongoose schemas from TypeScript interfaces 15 | - ⚡ **Error Handling** - Graceful error handling with environment-aware responses 16 | - 🔌 **Middleware Integration** - Seamless Express.js middleware compatibility 17 | 18 | ## 📦 Installation 19 | 20 | ```bash 21 | npm install expressbolt 22 | ``` 23 | 24 | ## 🚀 Quick Start 25 | 26 | ### 1. Define Your Interface 27 | 28 | ```typescript 29 | interface UserI { 30 | name: string; 31 | email: string; 32 | password: string; 33 | } 34 | 35 | interface UserDocI extends mongoose.Document, UserI {} 36 | 37 | interface UserModelI { 38 | isEmailTaken(email: string, excludeUserId?: mongoose.Types.ObjectId): Promise; 39 | paginate(filter: Record, options: Record): Promise; 40 | } 41 | ``` 42 | 43 | ### 2. Create Your Model 44 | 45 | ```typescript 46 | import { generateDynamicSchema } from 'expressbolt'; 47 | import { model } from 'mongoose'; 48 | 49 | const userModel = generateDynamicSchema({ 50 | modelName: "USER", 51 | fields: { 52 | email: { 53 | type: String, 54 | required: true, 55 | unique: true, 56 | }, 57 | name: { 58 | type: String, 59 | required: true, 60 | }, 61 | password: { 62 | type: String, 63 | required: true, 64 | }, 65 | }, 66 | schemaOptions: { 67 | timestamps: true, 68 | }, 69 | model: model, // Required for MongoDB sync 70 | }); 71 | ``` 72 | 73 | ### 3. Use CrudController in Routes 74 | 75 | ```typescript 76 | import { CrudController } from 'expressbolt'; 77 | import { Request, Response, NextFunction } from 'express'; 78 | 79 | export async function getAllUsers(req: Request, res: Response, next: NextFunction) { 80 | const crud = new CrudController({ 81 | next, 82 | request: req, 83 | response: res, 84 | env: "production", 85 | useNext: false, 86 | }); 87 | 88 | await crud.getMany({ 89 | modelData: { Model: userModel.model, select: ["-password"] }, 90 | filter: { name: req.body.name }, 91 | populate: {}, 92 | query: req.query, 93 | }); 94 | } 95 | ``` 96 | 97 | ## 📚 API Reference 98 | 99 | ### CrudController 100 | 101 | The main controller class for handling HTTP requests with CRUD operations. 102 | 103 | #### Constructor Options 104 | 105 | | Option | Type | Default | Description | 106 | |--------|------|---------|-------------| 107 | | `request` | `Request` | - | Express request object | 108 | | `response` | `Response` | - | Express response object | 109 | | `next` | `NextFunction` | - | Express next function | 110 | | `env` | `"development" \| "production"` | `"development"` | Environment mode | 111 | | `useNext` | `boolean` | `false` | Whether to use next() for error handling | 112 | 113 | #### Methods 114 | 115 | ##### `create(options)` 116 | Creates a new document in the database. 117 | 118 | ```typescript 119 | await crud.create({ 120 | modelData: { Model: userModel.model, select: ["-password"] }, 121 | data: userData, 122 | check: { email: userData.email } // Prevent duplicates 123 | }); 124 | ``` 125 | 126 | ##### `getMany(options)` 127 | Fetches multiple documents with advanced querying. 128 | 129 | ```typescript 130 | await crud.getMany({ 131 | modelData: { Model: userModel.model, select: ["-password"] }, 132 | filter: { active: true }, 133 | populate: { path: "profile", fields: ["firstName", "lastName"] }, 134 | query: req.query // Supports ?page=1&limit=10&sort=name&fields=name,email 135 | }); 136 | ``` 137 | 138 | ##### `getOne(options)` 139 | Retrieves a single document. 140 | 141 | ```typescript 142 | await crud.getOne({ 143 | modelData: { Model: userModel.model, select: ["-password"] }, 144 | data: { _id: userId }, 145 | populate: {} 146 | }); 147 | ``` 148 | 149 | ##### `update(options)` 150 | Updates an existing document. 151 | 152 | ```typescript 153 | await crud.update({ 154 | modelData: { Model: userModel.model, select: ["-password"] }, 155 | data: updateData, 156 | filter: { _id: userId } 157 | }); 158 | ``` 159 | 160 | ##### `delete(options)` 161 | Deletes documents from the database. 162 | 163 | ```typescript 164 | await crud.delete({ 165 | modelData: { Model: userModel.model }, 166 | data: { _id: userId } 167 | }); 168 | ``` 169 | 170 | ### CrudService 171 | 172 | For more control over responses, use CrudService directly: 173 | 174 | ```typescript 175 | import { CrudService } from 'expressbolt'; 176 | 177 | export async function createUser(user: UserI) { 178 | try { 179 | const userCreate = await CrudService.create({ 180 | check: { email: user.email }, 181 | data: user, 182 | modelData: { Model: userModel.model, select: ["-password"] }, 183 | }); 184 | return userCreate; 185 | } catch (error) { 186 | // Handle error yourself 187 | throw error; 188 | } 189 | } 190 | ``` 191 | 192 | ### Advanced Population 193 | 194 | Support for nested population: 195 | 196 | ```typescript 197 | // Single level population 198 | populate: { path: "author", fields: ["name", "email"] } 199 | 200 | // Multi-level population 201 | populate: [ 202 | { path: "author", fields: ["name"] }, 203 | { 204 | path: "category", 205 | fields: ["name", "image"], 206 | second_layer_populate: { path: "parent", select: "name" } 207 | } 208 | ] 209 | ``` 210 | 211 | ### Query Parameters 212 | 213 | ExpressBolt automatically handles these query parameters: 214 | 215 | - `?page=2&limit=10` - Pagination 216 | - `?sort=name,-createdAt` - Sorting (prefix with `-` for descending) 217 | - `?fields=name,email` - Field selection 218 | - `?name=john&active=true` - Filtering 219 | 220 | ## 🔧 Error Handling 221 | 222 | ### Global Error Handler 223 | 224 | ```typescript 225 | import { errorCenter } from 'expressbolt'; 226 | 227 | app.use((err: any, req: Request, res: Response, next: NextFunction) => { 228 | errorCenter({ 229 | env: process.env.NODE_ENV as "production" | "development", 230 | error: err, 231 | response: res, 232 | }); 233 | }); 234 | ``` 235 | 236 | ### Response Format 237 | 238 | Success Response: 239 | ```json 240 | { 241 | "message": "Users fetched successfully", 242 | "data": [...], 243 | "success": true, 244 | "doc_length": 25 245 | } 246 | ``` 247 | 248 | Error Response: 249 | ```json 250 | { 251 | "message": "Validation failed", 252 | "error": "Email already exists", 253 | "success": false, 254 | "stack": {} // Only in development mode 255 | } 256 | ``` 257 | 258 | ## 🏗️ Complete Example 259 | 260 |
261 | Click to see a full blog application example 262 | 263 | ```typescript 264 | import express, { Express, NextFunction, Request, Response } from "express"; 265 | import { 266 | CrudController, 267 | CrudService, 268 | errorCenter, 269 | generateDynamicSchema, 270 | } from "expressbolt"; 271 | import mongoose from "mongoose"; 272 | import bcrypt from "bcrypt"; 273 | import { model } from 'mongoose'; 274 | 275 | // User Interface 276 | interface UserI { 277 | name: string; 278 | email: string; 279 | password: string; 280 | } 281 | 282 | // Blog Interface 283 | interface BlogI { 284 | author: mongoose.Types.ObjectId; 285 | category: mongoose.Types.ObjectId; 286 | title: string; 287 | content: string; 288 | likes: number; 289 | } 290 | 291 | // Create User Model 292 | const userModel = generateDynamicSchema({ 293 | modelName: "USER", 294 | fields: { 295 | email: { type: String, required: true, unique: true }, 296 | name: { type: String, required: true }, 297 | password: { type: String, required: true }, 298 | }, 299 | schemaOptions: { timestamps: true }, 300 | model: model, 301 | }); 302 | 303 | const { model: USER, schema: userSchema } = userModel; 304 | 305 | // Hash password before saving 306 | userSchema.pre("save", async function (next) { 307 | this.password = await bcrypt.hash(this.password, 10); 308 | next(); 309 | }); 310 | 311 | // Create Blog Model 312 | const blogModel = generateDynamicSchema({ 313 | modelName: "BLOG", 314 | fields: { 315 | author: { type: mongoose.Schema.Types.ObjectId, required: true, ref: "USER" }, 316 | category: { type: mongoose.Schema.Types.ObjectId, required: true, ref: "CATEGORY" }, 317 | title: { type: String, required: true }, 318 | content: { type: String, required: true }, 319 | likes: { type: Number, default: 0 }, 320 | }, 321 | schemaOptions: { timestamps: true }, 322 | model: model, 323 | }); 324 | 325 | const { model: BLOG } = blogModel; 326 | 327 | // Routes 328 | export async function getAllBlogs(req: Request, res: Response, next: NextFunction) { 329 | const crud = new CrudController({ 330 | next, request: req, response: res, 331 | env: process.env.NODE_ENV as "production" | "development", 332 | useNext: false, 333 | }); 334 | 335 | await crud.getMany({ 336 | modelData: { Model: BLOG, select: ["-__v"] }, 337 | populate: [ 338 | { path: "author", fields: ["name"] }, 339 | { path: "category", fields: ["name"] }, 340 | ], 341 | query: req.query, 342 | }); 343 | } 344 | 345 | export async function createBlog(req: Request, res: Response, next: NextFunction) { 346 | const crud = new CrudController({ 347 | next, request: req, response: res, 348 | env: process.env.NODE_ENV as "production" | "development", 349 | useNext: false, 350 | }); 351 | 352 | await crud.create({ 353 | modelData: { Model: BLOG, select: ["-__v"] }, 354 | data: { ...req.body, author: req.user.id }, 355 | check: { title: req.body.title } 356 | }); 357 | } 358 | 359 | const app: Express = express(); 360 | 361 | app.use(express.json()); 362 | app.get("/blogs", getAllBlogs); 363 | app.post("/blogs", createBlog); 364 | 365 | // Error handling middleware 366 | app.use((err: any, req: Request, res: Response, next: NextFunction) => { 367 | errorCenter({ 368 | env: process.env.NODE_ENV as "production" | "development", 369 | error: err, 370 | response: res, 371 | }); 372 | }); 373 | 374 | export default app; 375 | ``` 376 | 377 |
378 | 379 | ## 🤝 Contributing 380 | 381 | We welcome contributions! Please follow these steps: 382 | 383 | 1. Fork the repository 384 | 2. Create your feature branch: `git checkout -b feature/amazing-feature` 385 | 3. Commit your changes: `git commit -m 'Add some amazing feature'` 386 | 4. Push to the branch: `git push origin feature/amazing-feature` 387 | 5. Open a Pull Request 388 | 389 | ## 📄 License 390 | 391 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 392 | 393 | ## ⭐ Show Your Support 394 | 395 | If ExpressBolt helps you build better APIs faster, please consider giving it a star on [GitHub](https://github.com/derekzyl/expressbolt)! 396 | 397 | --- 398 | 399 |
400 |

Made with ❤️ by Derek

401 |
402 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | /* Projects */ 5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 11 | /* Language and Environment */ 12 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 14 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 15 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 24 | /* Modules */ 25 | "module": "ES2020", /* Specify what module code is generated. */ 26 | "rootDir": "./src/", /* Specify the root folder within your source files. */ 27 | "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 28 | "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 29 | "paths": { 30 | "@/*": [ 31 | "./src/*" 32 | ], 33 | }, /* Specify a set of entries that re-map imports to additional lookup locations. */ 34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 36 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 38 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 39 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 40 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 41 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 42 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 43 | // "resolveJsonModule": true, /* Enable importing .json files. */ 44 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 45 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | /* Emit */ 51 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 52 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 53 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 54 | "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 55 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 56 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 57 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 58 | "removeComments": false, /* Disable emitting comments. */ 59 | // "noEmit": true, /* Disable emitting files from a compilation. */ 60 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 61 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 62 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 63 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 64 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 65 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 66 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 67 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 68 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 69 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 70 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 71 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 72 | // "declarationDir": "./dist/types", /* Specify the output directory for generated declaration files. */ 73 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 74 | /* Interop Constraints */ 75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 76 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 77 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 78 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 79 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 80 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 81 | /* Type Checking */ 82 | "strict": true, /* Enable all strict type-checking options. */ 83 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 84 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 85 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 86 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 87 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 88 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 89 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 90 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 91 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 92 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 93 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 94 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 95 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 96 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 97 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 98 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 99 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 100 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 101 | /* Completeness */ 102 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 103 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 104 | }, 105 | "include": [ 106 | "src/*.ts", 107 | "src/**/*.ts" 108 | ], 109 | "exclude": [ 110 | "node_modules" 111 | ] 112 | } -------------------------------------------------------------------------------- /src/files/crud.controller.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import httpStatus from "http-status"; 3 | import { FilterQuery, UpdateQuery } from "mongoose"; 4 | import CrudService from "./crud.service"; 5 | import { errorCenter } from "./error.handler"; 6 | import { CrudModelI, PopulateFieldI } from "./interface.crud"; 7 | 8 | /* The `CrudController` class in TypeScript provides methods for handling CRUD operations with a 9 | request, response, and next function, supporting create, update, delete, and fetch operations. */ 10 | class CrudController { 11 | request: Request; 12 | 13 | response: Response; 14 | 15 | next: NextFunction; 16 | 17 | useNext: boolean; 18 | 19 | env: "development" | "production"; 20 | /** 21 | * Constructor for creating an instance of the class. 22 | * 23 | * @param {Object} options - The options object containing request, response, next, useNext, and env. 24 | * @param {Request} options.request - The request object. 25 | * @param {Response} options.response - The response object. 26 | * @param {NextFunction} options.next - The next function. 27 | * @param {boolean} [options.useNext=true] - Flag indicating whether to use the next function. 28 | * @param {"development" | "production"} [options.env="development"] - The environment setting. 29 | */ 30 | constructor({ 31 | request, 32 | response, 33 | next, 34 | useNext = true, 35 | env = "development", 36 | }: { 37 | request: Request; 38 | response: Response; 39 | next: NextFunction; 40 | useNext?: boolean; 41 | env?: "development" | "production"; 42 | }) { 43 | this.request = request; 44 | this.response = response; 45 | this.next = next; 46 | this.useNext = useNext; 47 | this.env = env; 48 | } 49 | /** 50 | * A description of the entire function. 51 | * 52 | * @param {CrudModelI} modelData - description of parameter 53 | * @param {T} data - description of parameter 54 | * @param {FilterQuery} check - description of parameter 55 | * @return {Promise} description of return value 56 | */ 57 | async create({ 58 | modelData, 59 | data, 60 | check, 61 | }: { 62 | modelData: CrudModelI; 63 | data: T; 64 | check: FilterQuery; 65 | }): Promise { 66 | try { 67 | const response = await CrudService.create({ modelData, data, check }); 68 | 69 | return this.response.status(httpStatus.CREATED).json(response); 70 | } catch (error) { 71 | return this.useNext 72 | ? this.next(error) 73 | : errorCenter({ env: this.env, error: error, response: this.response }); 74 | } 75 | } 76 | 77 | /** 78 | * A function to create multiple records in the database. 79 | * 80 | * @param {Object} param0 - Object containing modelData, data, and check 81 | * @param {CrudModelI} param0.modelData - The model data for the records 82 | * @param {T[]} param0.data - The array of records to be created 83 | * @param {FilterQuery[]} param0.check - The filter query for checking existing records 84 | * @return {Promise} A promise that resolves to a response or next function, or void in case of error 85 | */ 86 | async createMany({ 87 | check, 88 | modelData, 89 | data, 90 | }: { 91 | modelData: CrudModelI; 92 | data: T[]; 93 | check: FilterQuery[]; 94 | }): Promise { 95 | try { 96 | const response = await CrudService.createMany({ modelData, data, check }); 97 | return this.response.status(httpStatus.CREATED).json(response); 98 | } catch (error) { 99 | return this.useNext 100 | ? this.next(error) 101 | : errorCenter({ env: this.env, error: error, response: this.response }); 102 | } 103 | } 104 | 105 | /** 106 | * The function updates a model using the provided data and filter, and returns the updated response. 107 | * @param {CrudModelI} modelData - A variable representing a CRUD model interface. It is used to 108 | * perform CRUD operations on a specific model. 109 | * @param data - The `data` parameter is an object that contains the fields and values to be updated 110 | * in the database. It represents the update query that will be executed on the `modelData` collection. 111 | * The type `T` represents the shape of the data object. 112 | * @param filter - The `filter` parameter is a query object used to filter the documents to be 113 | * updated in the database. It specifies the criteria that the documents must meet in order to be 114 | * updated. The `filter` parameter is of type `FilterQuery`, where `U` is the type of the filter 115 | * @returns a promise that resolves to a response object. 116 | */ 117 | async update({ 118 | data, 119 | modelData, 120 | filter, 121 | }: { 122 | modelData: CrudModelI; 123 | data: UpdateQuery; 124 | filter: FilterQuery; 125 | }) { 126 | try { 127 | const response = await CrudService.update({ modelData, data, filter }); 128 | 129 | return this.response.status(httpStatus.OK).json(response); 130 | } catch (error) { 131 | return this.useNext 132 | ? this.next(error) 133 | : errorCenter({ env: this.env, error: error, response: this.response }); 134 | } 135 | } 136 | 137 | /** 138 | * Fetches multiple documents from the database based on the provided query parameters. 139 | * Supports pagination, sorting, and field selection. 140 | * Can fetch data from multiple models if an array of models is provided. 141 | * 142 | * @param {CrudModelI } modelData - The model(s) to fetch data from. 143 | * @param {typeof this.request.query} query - The query parameters. 144 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The fields to populate. 145 | * @param {FilterQuery | null} [category=null] - The category filter. 146 | * @returns {Promise} - JSON response with the fetched data, success status, message, and the length of the fetched documents. 147 | * 148 | * @example 149 | * const crud = new Crud(request, response, next); 150 | * const modelData = { 151 | * Model: Model, 152 | * select: ['field1','field2'], 153 | * }; 154 | * const query = request.query; 155 | * const populate = { 156 | * model: 'relatedModel', 157 | * fields: ['field1', 'field2'], 158 | * second_layer_populate: 'nestedModel', 159 | * }; 160 | * const category = { category: 'category1' }; 161 | * 162 | * await crud.getMany(modelData, query, populate, category); 163 | * 164 | * This example initializes the `Crud` class object and calls the `getMany` method to fetch documents from the `Model` based on the provided query parameters. 165 | * It also specifies the fields to be populated and the category filter. 166 | */ 167 | async getMany({ 168 | filter = null, 169 | modelData, 170 | populate, 171 | query, 172 | }: { 173 | modelData: CrudModelI; 174 | query: any; 175 | populate: PopulateFieldI | PopulateFieldI[]; 176 | filter: FilterQuery | null; 177 | }) { 178 | try { 179 | const response = await CrudService.getMany({ 180 | modelData, 181 | query, 182 | populate, 183 | filter, 184 | }); 185 | 186 | this.response.status(httpStatus.OK).json(response); 187 | } catch (error) { 188 | return this.useNext 189 | ? this.next(error) 190 | : errorCenter({ env: this.env, error: error, response: this.response }); 191 | } 192 | } 193 | 194 | /** 195 | * The function is an asynchronous method that deletes data from a model using a filter query and 196 | * returns the response. 197 | * @param {CrudModelI} modelData - The modelData parameter is an instance of a CRUD model. It represents 198 | * the model or schema that you want to perform the delete operation on. It should implement the 199 | * CrudModelI interface. 200 | * @param data - The `data` parameter is a filter query object used to specify the criteria for 201 | * deleting documents from the database. It is of type `FilterQuery`, where `T` represents the 202 | * type of the documents being deleted. The `FilterQuery` type is typically used in MongoDB queries 203 | * to filter documents 204 | * @example - The example below shows how to use the `delete` function to delete a document from the 205 | * `Model` using a filter query. 206 | * const crud = new Crud(request, response, next); 207 | * const modelData = { 208 | * Model: Model, 209 | * select: 'field1 field2', 210 | * } 211 | */ 212 | async delete({ 213 | data, 214 | modelData, 215 | }: { 216 | modelData: CrudModelI; 217 | data: FilterQuery; 218 | }) { 219 | try { 220 | const response = await CrudService.delete({ modelData, data }); 221 | 222 | this.response.status(httpStatus.OK).json(response); 223 | } catch (error) { 224 | return this.useNext 225 | ? this.next(error) 226 | : errorCenter({ env: this.env, error: error, response: this.response }); 227 | } 228 | } 229 | 230 | /** 231 | * This TypeScript function deletes multiple documents based on a filter query using a CRUD service. 232 | * @param - The `deleteMany` function takes in two parameters: 233 | * @returns The `deleteMany` method is returning the response from the `CrudService.deleteMany` 234 | * function call if successful. If there is an error, it will either call `this.next(error)` if 235 | * `this.useNext` is true, or it will call `errorCenter` function with the provided parameters. 236 | */ 237 | async deleteMany({ 238 | data, 239 | modelData, 240 | }: { 241 | modelData: CrudModelI; 242 | data: FilterQuery; 243 | }) { 244 | try { 245 | const response = await CrudService.deleteMany({ modelData, data }); 246 | 247 | this.response.status(httpStatus.OK).json(response); 248 | } catch (error) { 249 | return this.useNext 250 | ? this.next(error) 251 | : errorCenter({ env: this.env, error: error, response: this.response }); 252 | } 253 | } 254 | 255 | /** 256 | * The function retrieves one document from a database using a given model, data filter, and optional 257 | * population fields, and returns the response as JSON. 258 | * @param {CrudModelI} modelData - The `modelData` parameter is the model that you want to perform the 259 | * operation on. It should be an instance of a CRUD model that implements the `CrudModelI` interface. 260 | * @param data - The `data` parameter is a filter query object used to specify the conditions for 261 | * finding a document in the database. It can be used to filter the documents based on certain 262 | * criteria such as equality, inequality, greater than, less than, etc. The type `T` represents the 263 | * type of the data 264 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The "populate" parameter is used to specify 265 | * which fields of the retrieved document(s) should be populated with their referenced documents. It 266 | * can be a single field or an array of fields to populate. 267 | * @example 268 | * ```ts 269 | * CrudModelI { 270 | * model: Model; 271 | *select: string; 272 | * } 273 | * populate: { model?: string | undefined; fields?: string | undefined } ``` 274 | * 275 | * @example 276 | * // returns a response 277 | * getOne< T the model >(modelData, category, populate: { model?: string | undefined; fields?: string | undefined)}) 278 | */ 279 | 280 | async getOne({ 281 | data, 282 | modelData, 283 | populate, 284 | }: { 285 | modelData: CrudModelI; 286 | data: FilterQuery; 287 | populate: PopulateFieldI | PopulateFieldI[]; 288 | }) { 289 | try { 290 | const response = await CrudService.getOne({ modelData, data, populate }); 291 | 292 | this.response.status(httpStatus.OK).json(response); 293 | } catch (error) { 294 | this.next(error); 295 | } 296 | } 297 | } 298 | 299 | export default CrudController; 300 | -------------------------------------------------------------------------------- /dist/files/crud.controller.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | import httpStatus from "http-status"; 11 | import CrudService from "./crud.service"; 12 | import { errorCenter } from "./error.handler"; 13 | /* The `CrudController` class in TypeScript provides methods for handling CRUD operations with a 14 | request, response, and next function, supporting create, update, delete, and fetch operations. */ 15 | class CrudController { 16 | /** 17 | * Constructor for creating an instance of the class. 18 | * 19 | * @param {Object} options - The options object containing request, response, next, useNext, and env. 20 | * @param {Request} options.request - The request object. 21 | * @param {Response} options.response - The response object. 22 | * @param {NextFunction} options.next - The next function. 23 | * @param {boolean} [options.useNext=true] - Flag indicating whether to use the next function. 24 | * @param {"development" | "production"} [options.env="development"] - The environment setting. 25 | */ 26 | constructor({ request, response, next, useNext = true, env = "development", }) { 27 | this.request = request; 28 | this.response = response; 29 | this.next = next; 30 | this.useNext = useNext; 31 | this.env = env; 32 | } 33 | /** 34 | * A description of the entire function. 35 | * 36 | * @param {CrudModelI} modelData - description of parameter 37 | * @param {T} data - description of parameter 38 | * @param {FilterQuery} check - description of parameter 39 | * @return {Promise} description of return value 40 | */ 41 | create(_a) { 42 | return __awaiter(this, arguments, void 0, function* ({ modelData, data, check, }) { 43 | try { 44 | const response = yield CrudService.create({ modelData, data, check }); 45 | return this.response.status(httpStatus.CREATED).json(response); 46 | } 47 | catch (error) { 48 | return this.useNext 49 | ? this.next(error) 50 | : errorCenter({ env: this.env, error: error, response: this.response }); 51 | } 52 | }); 53 | } 54 | /** 55 | * A function to create multiple records in the database. 56 | * 57 | * @param {Object} param0 - Object containing modelData, data, and check 58 | * @param {CrudModelI} param0.modelData - The model data for the records 59 | * @param {T[]} param0.data - The array of records to be created 60 | * @param {FilterQuery[]} param0.check - The filter query for checking existing records 61 | * @return {Promise} A promise that resolves to a response or next function, or void in case of error 62 | */ 63 | createMany(_a) { 64 | return __awaiter(this, arguments, void 0, function* ({ check, modelData, data, }) { 65 | try { 66 | const response = yield CrudService.createMany({ modelData, data, check }); 67 | return this.response.status(httpStatus.CREATED).json(response); 68 | } 69 | catch (error) { 70 | return this.useNext 71 | ? this.next(error) 72 | : errorCenter({ env: this.env, error: error, response: this.response }); 73 | } 74 | }); 75 | } 76 | /** 77 | * The function updates a model using the provided data and filter, and returns the updated response. 78 | * @param {CrudModelI} modelData - A variable representing a CRUD model interface. It is used to 79 | * perform CRUD operations on a specific model. 80 | * @param data - The `data` parameter is an object that contains the fields and values to be updated 81 | * in the database. It represents the update query that will be executed on the `modelData` collection. 82 | * The type `T` represents the shape of the data object. 83 | * @param filter - The `filter` parameter is a query object used to filter the documents to be 84 | * updated in the database. It specifies the criteria that the documents must meet in order to be 85 | * updated. The `filter` parameter is of type `FilterQuery`, where `U` is the type of the filter 86 | * @returns a promise that resolves to a response object. 87 | */ 88 | update(_a) { 89 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, filter, }) { 90 | try { 91 | const response = yield CrudService.update({ modelData, data, filter }); 92 | return this.response.status(httpStatus.OK).json(response); 93 | } 94 | catch (error) { 95 | return this.useNext 96 | ? this.next(error) 97 | : errorCenter({ env: this.env, error: error, response: this.response }); 98 | } 99 | }); 100 | } 101 | /** 102 | * Fetches multiple documents from the database based on the provided query parameters. 103 | * Supports pagination, sorting, and field selection. 104 | * Can fetch data from multiple models if an array of models is provided. 105 | * 106 | * @param {CrudModelI } modelData - The model(s) to fetch data from. 107 | * @param {typeof this.request.query} query - The query parameters. 108 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The fields to populate. 109 | * @param {FilterQuery | null} [category=null] - The category filter. 110 | * @returns {Promise} - JSON response with the fetched data, success status, message, and the length of the fetched documents. 111 | * 112 | * @example 113 | * const crud = new Crud(request, response, next); 114 | * const modelData = { 115 | * Model: Model, 116 | * select: ['field1','field2'], 117 | * }; 118 | * const query = request.query; 119 | * const populate = { 120 | * model: 'relatedModel', 121 | * fields: ['field1', 'field2'], 122 | * second_layer_populate: 'nestedModel', 123 | * }; 124 | * const category = { category: 'category1' }; 125 | * 126 | * await crud.getMany(modelData, query, populate, category); 127 | * 128 | * This example initializes the `Crud` class object and calls the `getMany` method to fetch documents from the `Model` based on the provided query parameters. 129 | * It also specifies the fields to be populated and the category filter. 130 | */ 131 | getMany(_a) { 132 | return __awaiter(this, arguments, void 0, function* ({ filter = null, modelData, populate, query, }) { 133 | try { 134 | const response = yield CrudService.getMany({ 135 | modelData, 136 | query, 137 | populate, 138 | filter, 139 | }); 140 | this.response.status(httpStatus.OK).json(response); 141 | } 142 | catch (error) { 143 | return this.useNext 144 | ? this.next(error) 145 | : errorCenter({ env: this.env, error: error, response: this.response }); 146 | } 147 | }); 148 | } 149 | /** 150 | * The function is an asynchronous method that deletes data from a model using a filter query and 151 | * returns the response. 152 | * @param {CrudModelI} modelData - The modelData parameter is an instance of a CRUD model. It represents 153 | * the model or schema that you want to perform the delete operation on. It should implement the 154 | * CrudModelI interface. 155 | * @param data - The `data` parameter is a filter query object used to specify the criteria for 156 | * deleting documents from the database. It is of type `FilterQuery`, where `T` represents the 157 | * type of the documents being deleted. The `FilterQuery` type is typically used in MongoDB queries 158 | * to filter documents 159 | * @example - The example below shows how to use the `delete` function to delete a document from the 160 | * `Model` using a filter query. 161 | * const crud = new Crud(request, response, next); 162 | * const modelData = { 163 | * Model: Model, 164 | * select: 'field1 field2', 165 | * } 166 | */ 167 | delete(_a) { 168 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, }) { 169 | try { 170 | const response = yield CrudService.delete({ modelData, data }); 171 | this.response.status(httpStatus.OK).json(response); 172 | } 173 | catch (error) { 174 | return this.useNext 175 | ? this.next(error) 176 | : errorCenter({ env: this.env, error: error, response: this.response }); 177 | } 178 | }); 179 | } 180 | /** 181 | * This TypeScript function deletes multiple documents based on a filter query using a CRUD service. 182 | * @param - The `deleteMany` function takes in two parameters: 183 | * @returns The `deleteMany` method is returning the response from the `CrudService.deleteMany` 184 | * function call if successful. If there is an error, it will either call `this.next(error)` if 185 | * `this.useNext` is true, or it will call `errorCenter` function with the provided parameters. 186 | */ 187 | deleteMany(_a) { 188 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, }) { 189 | try { 190 | const response = yield CrudService.deleteMany({ modelData, data }); 191 | this.response.status(httpStatus.OK).json(response); 192 | } 193 | catch (error) { 194 | return this.useNext 195 | ? this.next(error) 196 | : errorCenter({ env: this.env, error: error, response: this.response }); 197 | } 198 | }); 199 | } 200 | /** 201 | * The function retrieves one document from a database using a given model, data filter, and optional 202 | * population fields, and returns the response as JSON. 203 | * @param {CrudModelI} modelData - The `modelData` parameter is the model that you want to perform the 204 | * operation on. It should be an instance of a CRUD model that implements the `CrudModelI` interface. 205 | * @param data - The `data` parameter is a filter query object used to specify the conditions for 206 | * finding a document in the database. It can be used to filter the documents based on certain 207 | * criteria such as equality, inequality, greater than, less than, etc. The type `T` represents the 208 | * type of the data 209 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The "populate" parameter is used to specify 210 | * which fields of the retrieved document(s) should be populated with their referenced documents. It 211 | * can be a single field or an array of fields to populate. 212 | * @example 213 | * ```ts 214 | * CrudModelI { 215 | * model: Model; 216 | *select: string; 217 | * } 218 | * populate: { model?: string | undefined; fields?: string | undefined } ``` 219 | * 220 | * @example 221 | * // returns a response 222 | * getOne< T the model >(modelData, category, populate: { model?: string | undefined; fields?: string | undefined)}) 223 | */ 224 | getOne(_a) { 225 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, populate, }) { 226 | try { 227 | const response = yield CrudService.getOne({ modelData, data, populate }); 228 | this.response.status(httpStatus.OK).json(response); 229 | } 230 | catch (error) { 231 | this.next(error); 232 | } 233 | }); 234 | } 235 | } 236 | export default CrudController; 237 | //# sourceMappingURL=crud.controller.js.map -------------------------------------------------------------------------------- /src/files/crud.service.ts: -------------------------------------------------------------------------------- 1 | import { request } from "express"; 2 | import httpStatus from "http-status"; 3 | import { Document, FilterQuery, UpdateQuery } from "mongoose"; 4 | import CustomError from "./error.handler"; 5 | import { CrudModelI, CustomMessageI, PopulateFieldI } from "./interface.crud"; 6 | import Queries from "./query"; 7 | import responseMessage from "./responseMessage"; 8 | 9 | class CrudService { 10 | /** 11 | * The `create` function creates a document in a MongoDB collection. 12 | * It takes in a `CrudModelI` object, the data to be created, and a filter to check for existing documents. 13 | * It returns a Promise that resolves to a response message containing the successfully created document. 14 | * 15 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 16 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the MongoDB collection. 17 | * @param {T} param.data - The data to be created. 18 | * @param {FilterQuery} param.check - A filter to check for existing documents. 19 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created document. 20 | */ 21 | static async create({ 22 | check, 23 | modelData, 24 | data, 25 | }: { 26 | modelData: CrudModelI; 27 | data: T; 28 | check: FilterQuery; 29 | }): Promise> { 30 | type U = T & Document; 31 | const find = 32 | Object.keys(check).length !== 0 33 | ? await modelData.Model.findOne(check) 34 | : null; 35 | if (find) { 36 | throw new CustomError( 37 | httpStatus.BAD_REQUEST, 38 | `the data for: ${JSON.stringify( 39 | Object.keys(check).join(", ") 40 | )} already exists in the database` 41 | ); 42 | } 43 | 44 | const create = new modelData.Model(data); 45 | const created = await create.save(); 46 | 47 | if (!created) { 48 | throw new CustomError( 49 | httpStatus.BAD_REQUEST, 50 | `${modelData.Model.collection.collectionName} is not successfully created` 51 | ); 52 | } 53 | 54 | const dat = await modelData.Model.findById(created._id).select( 55 | modelData.select.join(" ") 56 | ); 57 | 58 | return responseMessage({ 59 | success: true, 60 | data: dat, 61 | message: "Successfully created", 62 | }); 63 | } 64 | 65 | /** 66 | * The function `createMany` creates multiple documents in a MongoDB collection based on an array of data. 67 | * It takes in a `CrudModelI` object, an array of data, and an array of filters to check for existing documents. 68 | * It returns a Promise that resolves to a response message containing the successfully created documents. 69 | * 70 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 71 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the model to be created. 72 | * @param {T[]} param.data - An array of data to be created. 73 | * @param {FilterQuery[]} param.check - An array of filters to check for existing documents. 74 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created documents. 75 | */ 76 | 77 | static async createMany({ 78 | check, 79 | data, 80 | modelData, 81 | }: { 82 | modelData: CrudModelI; 83 | data: T[]; 84 | check: Partial>[]; 85 | }): Promise> { 86 | if (check) { 87 | const checks = check.map((findr) => { 88 | return Object.keys(findr).length !== 0 89 | ? modelData.Model.findOne(findr) 90 | : null; 91 | }); 92 | 93 | const finds = await Promise.all(checks); 94 | 95 | finds.forEach((find, index) => { 96 | if (find) { 97 | throw new CustomError( 98 | httpStatus.BAD_REQUEST, 99 | `the data ${JSON.stringify( 100 | Object.keys(check[index]).join(", ") 101 | )} already exists in the database` 102 | ); 103 | } 104 | }); 105 | } 106 | 107 | const created = await modelData.Model.insertMany(data); 108 | 109 | if (!created) { 110 | throw new CustomError( 111 | httpStatus.BAD_REQUEST, 112 | `${modelData.Model.collection.collectionName} is not successfully created` 113 | ); 114 | } 115 | 116 | const selectedData = await Promise.all( 117 | created.map((item) => 118 | modelData.Model.findById(item._id).select(modelData.select.join(" ")) 119 | ) 120 | ); 121 | 122 | return responseMessage<(T & Document)[]>({ 123 | success: true, 124 | data: selectedData, 125 | message: "Successfully created", 126 | }); 127 | } 128 | 129 | /** 130 | * The `update` static method is used to update a document in a database based on specified criteria. 131 | * 132 | * @param {Object} params - The `params` object contains the necessary parameters for the update operation. 133 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to update. 134 | * @param {UpdateQuery} params.data - The `data` parameter represents the update data. 135 | * @param {FilterQuery} params.filter - The `filter` parameter represents the criteria to 136 | * identify the document(s) to update. 137 | * @returns {Promise} The method returns a promise that resolves to a response message containing the 138 | * updated document and a success status. 139 | */ 140 | static async update({ 141 | data, 142 | filter, 143 | modelData, 144 | }: { 145 | modelData: CrudModelI; 146 | data: UpdateQuery; 147 | filter: FilterQuery; 148 | }): Promise> { 149 | const dataF: Array = []; 150 | 151 | const findAndUpdate = 152 | modelData.select.length > 0 153 | ? await modelData.Model.findOneAndUpdate(filter, data).select( 154 | modelData.select.join(" ") 155 | ) 156 | : await modelData.Model.findOneAndUpdate(filter, data); 157 | if (!findAndUpdate) { 158 | throw new CustomError( 159 | httpStatus.BAD_REQUEST, 160 | `{modelData.Model.collection.collectionName} not updated successfully` 161 | ); 162 | } else { 163 | dataF.push(findAndUpdate); 164 | } 165 | 166 | return responseMessage({ 167 | success: true, 168 | data: dataF[0], 169 | message: "Successfully updated", 170 | }); 171 | } 172 | 173 | /** 174 | * This function retrieves multiple documents from a MongoDB collection based on the provided 175 | * filter, model, populate, and query parameters. 176 | * @param {Object} params - The `params` object contains the necessary parameters for the getMany 177 | * operation. 178 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to 179 | * retrieve documents from. 180 | * @param {FilterQuery | null} params.filter - The `filter` parameter represents the criteria to 181 | * identify the documents to retrieve. 182 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The `populate` parameter 183 | * represents the fields to populate in the retrieved documents. 184 | * @param {typeof request.query} params.query - The `query` parameter represents the query parameters 185 | * from the request. 186 | * @returns {Promise} The method returns a promise that resolves to a response message object 187 | * containing the retrieved documents, a success status, and other relevant information. 188 | */ 189 | static async getMany({ 190 | filter, 191 | modelData, 192 | populate, 193 | query, 194 | }: { 195 | modelData: CrudModelI; 196 | query: typeof request.query; 197 | populate: PopulateFieldI | PopulateFieldI[]; 198 | filter: FilterQuery | null; 199 | }): Promise> { 200 | const all: any[] = []; 201 | const processModel = async (model: CrudModelI) => { 202 | let modelFind = filter ? model.Model.find(filter) : model.Model.find(); 203 | if (model.select) { 204 | modelFind = modelFind.select(model.select.join(" ")); 205 | } 206 | if (populate) { 207 | modelFind = CrudService.populateModel(modelFind, populate); 208 | } 209 | const queryf = new Queries(modelFind, query) 210 | .filter() 211 | .limitFields() 212 | .paginate() 213 | .sort(); 214 | const queryG = await queryf.model; 215 | if (!queryG) { 216 | throw new CustomError(httpStatus.NOT_FOUND, `${model} is not fetched`); 217 | } 218 | all.push(queryG); 219 | }; 220 | 221 | // if (Array.isArray(modelData)) { 222 | // await Promise.all(modelData.map(processModel)); 223 | // } else { 224 | await processModel(modelData); 225 | // } 226 | type U = T & Document; 227 | return responseMessage({ 228 | success: true, 229 | message: "Data fetched successfully", 230 | data: all[0], 231 | doc_length: await CrudService.getModelLength(modelData, filter), 232 | }); 233 | } 234 | 235 | private static async getModelLength( 236 | model: CrudModelI, 237 | filter: FilterQuery | null 238 | ): Promise { 239 | const count = await model.Model.countDocuments(filter || {}); 240 | return count; 241 | } 242 | 243 | /** 244 | * The function `populateModel` populates a model with specified fields and nested fields based on 245 | * the provided criteria. 246 | * @param {any} modelFind - The `modelFind` parameter is the model object that you want to populate 247 | * with additional data. It could be a Mongoose model instance or any other object that supports 248 | * population of fields. 249 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The `populate` parameter in the 250 | * `populateModel` function is used to specify which fields in the model should be populated with 251 | * additional data. It can be a single `PopulateFieldI` object or an array of `PopulateFieldI` 252 | * objects. Each `PopulateFieldI` object 253 | * @returns The `populateModel` function returns the result of populating the specified fields in the 254 | * modelFind object based on the populate configuration provided. 255 | */ 256 | private static populateModel( 257 | modelFind: any, 258 | populate: PopulateFieldI | PopulateFieldI[] 259 | ): any { 260 | if (!populate) { 261 | return modelFind; 262 | } 263 | 264 | const populateArr = Array.isArray(populate) ? populate : [populate]; 265 | 266 | return populateArr.reduce((model, pop) => { 267 | if (!pop.path) { 268 | return model; 269 | } 270 | 271 | return model.populate({ 272 | path: pop.path, 273 | select: pop.fields?.join(" "), 274 | populate: pop.second_layer_populate, 275 | }); 276 | }, modelFind); 277 | } 278 | 279 | /** 280 | * The function `delete` is an asynchronous static method in TypeScript that deletes data based on 281 | * the provided filter query and model data, handling errors and returning a success message. 282 | * @param - The `delete` method you provided is an asynchronous function that deletes data from a 283 | * database using the given `modelData` and `data` parameters. Here's a breakdown of the parameters: 284 | * @returns The `delete` method is returning a Promise that resolves to an object with the following 285 | * properties: 286 | * - `success`: a boolean value indicating the success status of the deletion operation 287 | * - `message`: a string message indicating that the deletion was successful 288 | * - `data`: a string value indicating that the data was deleted 289 | */ 290 | static async delete({ 291 | data, 292 | modelData, 293 | }: { 294 | modelData: CrudModelI; 295 | data: FilterQuery; 296 | }): Promise> { 297 | // if (Array.isArray(modelData)) { 298 | // Promise.all( 299 | // modelData.map(async (model) => { 300 | // const delet = await model.Model.deleteOne(data); 301 | // if (!delet) { 302 | // throw new CustomError ( 303 | // httpStatus.NOT_IMPLEMENTED, 304 | // `${model} is not successfully deleted` 305 | // ); 306 | // } 307 | // }) 308 | // ); 309 | // } else { 310 | const delet = await modelData.Model.deleteOne(data); 311 | if (!delet) { 312 | throw new CustomError( 313 | httpStatus.NOT_FOUND, 314 | `{modelData.Model.collection.collectionName} is not successfully deleted` 315 | ); 316 | // } 317 | } 318 | 319 | return responseMessage({ 320 | success: true, 321 | message: "Deleted successfully", 322 | data: "deleted", 323 | }); 324 | } 325 | /** 326 | * This TypeScript function deletes multiple documents based on a filter query using the deleteMany 327 | * method and returns a success message. 328 | * @param - The `deleteMany` function takes in two parameters: 329 | * @returns The `deleteMany` function returns a Promise that resolves to an object with the following 330 | * properties: 331 | * - `success`: a boolean indicating the success status of the deletion operation (true in 332 | * this case) 333 | * - `message`: a string message indicating that the deletion was successful ("Deleted successfully" 334 | * in this case) 335 | * - `data`: a string value indicating that the data was deleted ("deleted" in this case 336 | */ 337 | static async deleteMany({ 338 | data, 339 | modelData, 340 | }: { 341 | modelData: CrudModelI; 342 | data: FilterQuery; 343 | }): Promise> { 344 | // if (Array.isArray(modelData)) { 345 | // Promise.all( 346 | // modelData.map(async (model) => { 347 | // const delet = await model.Model.deleteOne(data); 348 | // if (!delet) { 349 | // throw new CustomError ( 350 | // httpStatus.NOT_IMPLEMENTED, 351 | // `${model} is not successfully deleted` 352 | // ); 353 | // } 354 | // }) 355 | // ); 356 | // } else { 357 | const delet = await modelData.Model.deleteMany(data); 358 | if (!delet) { 359 | throw new CustomError( 360 | httpStatus.NOT_FOUND, 361 | `${modelData.Model.collection.collectionName} is not successfully deleted` 362 | ); 363 | // } 364 | } 365 | 366 | return responseMessage({ 367 | success: true, 368 | message: "Deleted successfully", 369 | data: "deleted", 370 | }); 371 | } 372 | 373 | /** 374 | * Asynchronously retrieves a single document from a MongoDB collection. 375 | * 376 | * @param {Object} params - The parameters for retrieving the document. 377 | * @param {CrudModelI} params.modelData - The model data for the MongoDB collection. 378 | * @param {FilterQuery} params.data - The filter query for finding the document. 379 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The populate options for the document. 380 | * @returns {Promise} The response message containing the retrieved document. 381 | * @throws {CustomError} If the document is not found. 382 | */ 383 | static async getOne({ 384 | modelData, 385 | data, 386 | populate, 387 | }: { 388 | modelData: CrudModelI; 389 | data: FilterQuery; 390 | populate: PopulateFieldI | PopulateFieldI[]; 391 | }): Promise> { 392 | // const response = await CrudService. 393 | const getData = []; 394 | let getOne: any; 395 | 396 | getOne = modelData.Model.findOne(data).select(modelData.select.join(" ")); 397 | 398 | if (!getOne) 399 | throw new CustomError( 400 | httpStatus.NOT_FOUND, 401 | `{modelData.Model.collection.collectionName} is not successfully fetched` 402 | ); 403 | 404 | if (populate) getOne = CrudService.populateModel(getOne, populate); 405 | const gotten = await getOne.exec(); 406 | 407 | getData.push(gotten); 408 | type U = T & Document; 409 | return responseMessage({ 410 | success: true, 411 | message: " fetched successfully", 412 | data: getData[0], 413 | }); 414 | } 415 | } 416 | 417 | export default CrudService; 418 | -------------------------------------------------------------------------------- /dist/files/crud.service.js: -------------------------------------------------------------------------------- 1 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 2 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | import httpStatus from "http-status"; 11 | import CustomError from "./error.handler"; 12 | import Queries from "./query"; 13 | import responseMessage from "./responseMessage"; 14 | class CrudService { 15 | /** 16 | * The `create` function creates a document in a MongoDB collection. 17 | * It takes in a `CrudModelI` object, the data to be created, and a filter to check for existing documents. 18 | * It returns a Promise that resolves to a response message containing the successfully created document. 19 | * 20 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 21 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the MongoDB collection. 22 | * @param {T} param.data - The data to be created. 23 | * @param {FilterQuery} param.check - A filter to check for existing documents. 24 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created document. 25 | */ 26 | static create(_a) { 27 | return __awaiter(this, arguments, void 0, function* ({ check, modelData, data, }) { 28 | const find = Object.keys(check).length !== 0 29 | ? yield modelData.Model.findOne(check) 30 | : null; 31 | if (find) { 32 | throw new CustomError(httpStatus.BAD_REQUEST, `the data for: ${JSON.stringify(Object.keys(check).join(", "))} already exists in the database`); 33 | } 34 | const create = new modelData.Model(data); 35 | const created = yield create.save(); 36 | if (!created) { 37 | throw new CustomError(httpStatus.BAD_REQUEST, `${modelData.Model.collection.collectionName} is not successfully created`); 38 | } 39 | const dat = yield modelData.Model.findById(created._id).select(modelData.select.join(" ")); 40 | return responseMessage({ 41 | success: true, 42 | data: dat, 43 | message: "Successfully created", 44 | }); 45 | }); 46 | } 47 | /** 48 | * The function `createMany` creates multiple documents in a MongoDB collection based on an array of data. 49 | * It takes in a `CrudModelI` object, an array of data, and an array of filters to check for existing documents. 50 | * It returns a Promise that resolves to a response message containing the successfully created documents. 51 | * 52 | * @param {Object} param - An object containing the `modelData`, `data`, and `check` properties. 53 | * @param {CrudModelI} param.modelData - A `CrudModelI` object representing the model to be created. 54 | * @param {T[]} param.data - An array of data to be created. 55 | * @param {FilterQuery[]} param.check - An array of filters to check for existing documents. 56 | * @returns {Promise} A Promise that resolves to a response message containing the successfully created documents. 57 | */ 58 | static createMany(_a) { 59 | return __awaiter(this, arguments, void 0, function* ({ check, data, modelData, }) { 60 | if (check) { 61 | const checks = check.map((findr) => { 62 | return Object.keys(findr).length !== 0 63 | ? modelData.Model.findOne(findr) 64 | : null; 65 | }); 66 | const finds = yield Promise.all(checks); 67 | finds.forEach((find, index) => { 68 | if (find) { 69 | throw new CustomError(httpStatus.BAD_REQUEST, `the data ${JSON.stringify(Object.keys(check[index]).join(", "))} already exists in the database`); 70 | } 71 | }); 72 | } 73 | const created = yield modelData.Model.insertMany(data); 74 | if (!created) { 75 | throw new CustomError(httpStatus.BAD_REQUEST, `${modelData.Model.collection.collectionName} is not successfully created`); 76 | } 77 | const selectedData = yield Promise.all(created.map((item) => modelData.Model.findById(item._id).select(modelData.select.join(" ")))); 78 | return responseMessage({ 79 | success: true, 80 | data: selectedData, 81 | message: "Successfully created", 82 | }); 83 | }); 84 | } 85 | /** 86 | * The `update` static method is used to update a document in a database based on specified criteria. 87 | * 88 | * @param {Object} params - The `params` object contains the necessary parameters for the update operation. 89 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to update. 90 | * @param {UpdateQuery} params.data - The `data` parameter represents the update data. 91 | * @param {FilterQuery} params.filter - The `filter` parameter represents the criteria to 92 | * identify the document(s) to update. 93 | * @returns {Promise} The method returns a promise that resolves to a response message containing the 94 | * updated document and a success status. 95 | */ 96 | static update(_a) { 97 | return __awaiter(this, arguments, void 0, function* ({ data, filter, modelData, }) { 98 | const dataF = []; 99 | const findAndUpdate = modelData.select.length > 0 100 | ? yield modelData.Model.findOneAndUpdate(filter, data).select(modelData.select.join(" ")) 101 | : yield modelData.Model.findOneAndUpdate(filter, data); 102 | if (!findAndUpdate) { 103 | throw new CustomError(httpStatus.BAD_REQUEST, `{modelData.Model.collection.collectionName} not updated successfully`); 104 | } 105 | else { 106 | dataF.push(findAndUpdate); 107 | } 108 | return responseMessage({ 109 | success: true, 110 | data: dataF[0], 111 | message: "Successfully updated", 112 | }); 113 | }); 114 | } 115 | /** 116 | * This function retrieves multiple documents from a MongoDB collection based on the provided 117 | * filter, model, populate, and query parameters. 118 | * @param {Object} params - The `params` object contains the necessary parameters for the getMany 119 | * operation. 120 | * @param {CrudModelI} params.modelData - The `modelData` parameter represents the model(s) to 121 | * retrieve documents from. 122 | * @param {FilterQuery | null} params.filter - The `filter` parameter represents the criteria to 123 | * identify the documents to retrieve. 124 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The `populate` parameter 125 | * represents the fields to populate in the retrieved documents. 126 | * @param {typeof request.query} params.query - The `query` parameter represents the query parameters 127 | * from the request. 128 | * @returns {Promise} The method returns a promise that resolves to a response message object 129 | * containing the retrieved documents, a success status, and other relevant information. 130 | */ 131 | static getMany(_a) { 132 | return __awaiter(this, arguments, void 0, function* ({ filter, modelData, populate, query, }) { 133 | const all = []; 134 | const processModel = (model) => __awaiter(this, void 0, void 0, function* () { 135 | let modelFind = filter ? model.Model.find(filter) : model.Model.find(); 136 | if (model.select) { 137 | modelFind = modelFind.select(model.select.join(" ")); 138 | } 139 | if (populate) { 140 | modelFind = CrudService.populateModel(modelFind, populate); 141 | } 142 | const queryf = new Queries(modelFind, query) 143 | .filter() 144 | .limitFields() 145 | .paginate() 146 | .sort(); 147 | const queryG = yield queryf.model; 148 | if (!queryG) { 149 | throw new CustomError(httpStatus.NOT_FOUND, `${model} is not fetched`); 150 | } 151 | all.push(queryG); 152 | }); 153 | // if (Array.isArray(modelData)) { 154 | // await Promise.all(modelData.map(processModel)); 155 | // } else { 156 | yield processModel(modelData); 157 | return responseMessage({ 158 | success: true, 159 | message: "Data fetched successfully", 160 | data: all[0], 161 | doc_length: yield CrudService.getModelLength(modelData, filter), 162 | }); 163 | }); 164 | } 165 | static getModelLength(model, filter) { 166 | return __awaiter(this, void 0, void 0, function* () { 167 | const count = yield model.Model.countDocuments(filter || {}); 168 | return count; 169 | }); 170 | } 171 | /** 172 | * The function `populateModel` populates a model with specified fields and nested fields based on 173 | * the provided criteria. 174 | * @param {any} modelFind - The `modelFind` parameter is the model object that you want to populate 175 | * with additional data. It could be a Mongoose model instance or any other object that supports 176 | * population of fields. 177 | * @param {PopulateFieldI | PopulateFieldI[]} populate - The `populate` parameter in the 178 | * `populateModel` function is used to specify which fields in the model should be populated with 179 | * additional data. It can be a single `PopulateFieldI` object or an array of `PopulateFieldI` 180 | * objects. Each `PopulateFieldI` object 181 | * @returns The `populateModel` function returns the result of populating the specified fields in the 182 | * modelFind object based on the populate configuration provided. 183 | */ 184 | static populateModel(modelFind, populate) { 185 | if (!populate) { 186 | return modelFind; 187 | } 188 | const populateArr = Array.isArray(populate) ? populate : [populate]; 189 | return populateArr.reduce((model, pop) => { 190 | var _a; 191 | if (!pop.path) { 192 | return model; 193 | } 194 | return model.populate({ 195 | path: pop.path, 196 | select: (_a = pop.fields) === null || _a === void 0 ? void 0 : _a.join(" "), 197 | populate: pop.second_layer_populate, 198 | }); 199 | }, modelFind); 200 | } 201 | /** 202 | * The function `delete` is an asynchronous static method in TypeScript that deletes data based on 203 | * the provided filter query and model data, handling errors and returning a success message. 204 | * @param - The `delete` method you provided is an asynchronous function that deletes data from a 205 | * database using the given `modelData` and `data` parameters. Here's a breakdown of the parameters: 206 | * @returns The `delete` method is returning a Promise that resolves to an object with the following 207 | * properties: 208 | * - `success`: a boolean value indicating the success status of the deletion operation 209 | * - `message`: a string message indicating that the deletion was successful 210 | * - `data`: a string value indicating that the data was deleted 211 | */ 212 | static delete(_a) { 213 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, }) { 214 | // if (Array.isArray(modelData)) { 215 | // Promise.all( 216 | // modelData.map(async (model) => { 217 | // const delet = await model.Model.deleteOne(data); 218 | // if (!delet) { 219 | // throw new CustomError ( 220 | // httpStatus.NOT_IMPLEMENTED, 221 | // `${model} is not successfully deleted` 222 | // ); 223 | // } 224 | // }) 225 | // ); 226 | // } else { 227 | const delet = yield modelData.Model.deleteOne(data); 228 | if (!delet) { 229 | throw new CustomError(httpStatus.NOT_FOUND, `{modelData.Model.collection.collectionName} is not successfully deleted`); 230 | // } 231 | } 232 | return responseMessage({ 233 | success: true, 234 | message: "Deleted successfully", 235 | data: "deleted", 236 | }); 237 | }); 238 | } 239 | /** 240 | * This TypeScript function deletes multiple documents based on a filter query using the deleteMany 241 | * method and returns a success message. 242 | * @param - The `deleteMany` function takes in two parameters: 243 | * @returns The `deleteMany` function returns a Promise that resolves to an object with the following 244 | * properties: 245 | * - `success`: a boolean indicating the success status of the deletion operation (true in 246 | * this case) 247 | * - `message`: a string message indicating that the deletion was successful ("Deleted successfully" 248 | * in this case) 249 | * - `data`: a string value indicating that the data was deleted ("deleted" in this case 250 | */ 251 | static deleteMany(_a) { 252 | return __awaiter(this, arguments, void 0, function* ({ data, modelData, }) { 253 | // if (Array.isArray(modelData)) { 254 | // Promise.all( 255 | // modelData.map(async (model) => { 256 | // const delet = await model.Model.deleteOne(data); 257 | // if (!delet) { 258 | // throw new CustomError ( 259 | // httpStatus.NOT_IMPLEMENTED, 260 | // `${model} is not successfully deleted` 261 | // ); 262 | // } 263 | // }) 264 | // ); 265 | // } else { 266 | const delet = yield modelData.Model.deleteMany(data); 267 | if (!delet) { 268 | throw new CustomError(httpStatus.NOT_FOUND, `${modelData.Model.collection.collectionName} is not successfully deleted`); 269 | // } 270 | } 271 | return responseMessage({ 272 | success: true, 273 | message: "Deleted successfully", 274 | data: "deleted", 275 | }); 276 | }); 277 | } 278 | /** 279 | * Asynchronously retrieves a single document from a MongoDB collection. 280 | * 281 | * @param {Object} params - The parameters for retrieving the document. 282 | * @param {CrudModelI} params.modelData - The model data for the MongoDB collection. 283 | * @param {FilterQuery} params.data - The filter query for finding the document. 284 | * @param {PopulateFieldI | PopulateFieldI[]} params.populate - The populate options for the document. 285 | * @returns {Promise} The response message containing the retrieved document. 286 | * @throws {CustomError} If the document is not found. 287 | */ 288 | static getOne(_a) { 289 | return __awaiter(this, arguments, void 0, function* ({ modelData, data, populate, }) { 290 | // const response = await CrudService. 291 | const getData = []; 292 | let getOne; 293 | getOne = modelData.Model.findOne(data).select(modelData.select.join(" ")); 294 | if (!getOne) 295 | throw new CustomError(httpStatus.NOT_FOUND, `{modelData.Model.collection.collectionName} is not successfully fetched`); 296 | if (populate) 297 | getOne = CrudService.populateModel(getOne, populate); 298 | const gotten = yield getOne.exec(); 299 | getData.push(gotten); 300 | return responseMessage({ 301 | success: true, 302 | message: " fetched successfully", 303 | data: getData[0], 304 | }); 305 | }); 306 | } 307 | } 308 | export default CrudService; 309 | //# sourceMappingURL=crud.service.js.map -------------------------------------------------------------------------------- /dist/all.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import { NextFunction, Request, Response } from "express"; 3 | // import httpStatus from "http-status"; 4 | // import mongoose, { 5 | // Document, 6 | // FilterQuery, 7 | // Model, 8 | // Schema, 9 | // SchemaDefinition, 10 | // SchemaDefinitionProperty, 11 | // UpdateQuery, 12 | // model, 13 | // } from "mongoose"; 14 | // import CrudService from "./crud.service"; 15 | // import { errorCenter } from "./error.handler"; 16 | // import { CrudModelI, PopulateFieldI } from "./interface.crud"; 17 | // class CrudController { 18 | // request: Request; 19 | // response: Response; 20 | // next: NextFunction; 21 | // useNext: boolean; 22 | // env: "development" | "production"; 23 | // constructor({ 24 | // request, 25 | // response, 26 | // next, 27 | // useNext = true, 28 | // env = "development", 29 | // }: { 30 | // request: Request; 31 | // response: Response; 32 | // next: NextFunction; 33 | // useNext?: boolean; 34 | // env?: "development" | "production"; 35 | // }) { 36 | // this.request = request; 37 | // this.response = response; 38 | // this.next = next; 39 | // this.useNext = useNext; 40 | // this.env = env; 41 | // } 42 | // async create({ 43 | // modelData, 44 | // data, 45 | // check, 46 | // }: { 47 | // modelData: CrudModelI; 48 | // data: T; 49 | // check: FilterQuery; 50 | // }): Promise { 51 | // try { 52 | // const response = await CrudService.create({ modelData, data, check }); 53 | // return this.response.status(httpStatus.CREATED).json(response); 54 | // } catch (error) { 55 | // return this.useNext 56 | // ? this.next(error) 57 | // : errorCenter({ env: this.env, error: error, response: this.response }); 58 | // } 59 | // } 60 | // async createMany({ 61 | // check, 62 | // modelData, 63 | // data, 64 | // }: { 65 | // modelData: CrudModelI; 66 | // data: T[]; 67 | // check: FilterQuery[]; 68 | // }): Promise { 69 | // try { 70 | // const response = await CrudService.createMany({ modelData, data, check }); 71 | // return this.response.status(httpStatus.CREATED).json(response); 72 | // } catch (error) { 73 | // return this.useNext 74 | // ? this.next(error) 75 | // : errorCenter({ env: this.env, error: error, response: this.response }); 76 | // } 77 | // } 78 | // async update({ 79 | // data, 80 | // modelData, 81 | // filter, 82 | // }: { 83 | // modelData: CrudModelI; 84 | // data: UpdateQuery; 85 | // filter: FilterQuery; 86 | // }) { 87 | // try { 88 | // const response = await CrudService.update({ modelData, data, filter }); 89 | // return this.response.status(httpStatus.OK).json(response); 90 | // } catch (error) { 91 | // return this.useNext 92 | // ? this.next(error) 93 | // : errorCenter({ env: this.env, error: error, response: this.response }); 94 | // } 95 | // } 96 | // async getMany({ 97 | // filter = null, 98 | // modelData, 99 | // populate, 100 | // query, 101 | // }: { 102 | // modelData: CrudModelI; 103 | // query: any; 104 | // populate: PopulateFieldI | PopulateFieldI[]; 105 | // filter: FilterQuery | null; 106 | // }) { 107 | // try { 108 | // const response = await CrudService.getMany({ 109 | // modelData, 110 | // query, 111 | // populate, 112 | // filter, 113 | // }); 114 | // this.response.status(httpStatus.OK).json(response); 115 | // } catch (error) { 116 | // return this.useNext 117 | // ? this.next(error) 118 | // : errorCenter({ env: this.env, error: error, response: this.response }); 119 | // } 120 | // } 121 | // async delete({ 122 | // data, 123 | // modelData, 124 | // }: { 125 | // modelData: CrudModelI; 126 | // data: FilterQuery; 127 | // }) { 128 | // try { 129 | // const response = await CrudService.delete({ modelData, data }); 130 | // this.response.status(httpStatus.OK).json(response); 131 | // } catch (error) { 132 | // return this.useNext 133 | // ? this.next(error) 134 | // : errorCenter({ env: this.env, error: error, response: this.response }); 135 | // } 136 | // } 137 | // async getOne({ 138 | // data, 139 | // modelData, 140 | // populate, 141 | // }: { 142 | // modelData: CrudModelI; 143 | // data: FilterQuery; 144 | // populate: PopulateFieldI | PopulateFieldI[]; 145 | // }) { 146 | // try { 147 | // const response = await CrudService.getOne({ modelData, data, populate }); 148 | // this.response.status(httpStatus.OK).json(response); 149 | // } catch (error) { 150 | // this.next(error); 151 | // } 152 | // } 153 | // } 154 | // export default CrudController; 155 | // // Interface for dynamic field definition 156 | // type DynamicField = { 157 | // [key in keyof T]: SchemaDefinitionProperty; 158 | // }; 159 | // const generateDynamicSchema = ({ 160 | // fields, 161 | // modelName, 162 | // plugins, 163 | // schemaOptions, 164 | // }: { 165 | // modelName: string; 166 | // fields: DynamicField; 167 | // plugins?: any[]; 168 | // schemaOptions?: Record; 169 | // }) => { 170 | // type ITDoc = T & Document; 171 | // type ITModel = Model & U; 172 | // const schemaDefinition: SchemaDefinition = {}; 173 | // for (const [keys, values] of Object.entries(fields)) { 174 | // const fieldOptions = values; 175 | // if (typeof fieldOptions === "object") { 176 | // schemaDefinition[keys] = fieldOptions as SchemaDefinitionProperty; 177 | // } else if (Array.isArray(fieldOptions)) { 178 | // switch (typeof fieldOptions[0]) { 179 | // case "string": 180 | // schemaDefinition[keys] = { 181 | // type: [String], 182 | // }; 183 | // break; 184 | // case "number": 185 | // schemaDefinition[keys] = { 186 | // type: [Number], 187 | // }; 188 | // break; 189 | // case "object": 190 | // { 191 | // Object.prototype.hasOwnProperty.call(fieldOptions[0], "ref") && 192 | // Object.prototype.hasOwnProperty.call(fieldOptions[0], "type") && 193 | // fieldOptions[0].type === mongoose.Schema.Types.ObjectId 194 | // ? (schemaDefinition[keys] = fieldOptions) 195 | // : (schemaDefinition[keys] = generateDynamicSchema({ 196 | // modelName: "", 197 | // fields: fieldOptions[0], 198 | // schemaOptions: schemaOptions ?? {}, 199 | // }).schema); 200 | // } 201 | // break; 202 | // case "bigint": 203 | // schemaDefinition[keys] = { 204 | // type: [BigInt], 205 | // }; 206 | // break; 207 | // case "boolean": 208 | // schemaDefinition[keys] = { 209 | // type: [Boolean], 210 | // }; 211 | // break; 212 | // case "symbol": 213 | // schemaDefinition[keys] = { 214 | // type: [String], 215 | // }; 216 | // break; 217 | // default: 218 | // break; 219 | // } 220 | // } else { 221 | // schemaDefinition[keys] = { 222 | // type: fieldOptions, 223 | // }; 224 | // } 225 | // } 226 | // // Create and return the schema 227 | // const schemaDef = new Schema(schemaDefinition, schemaOptions); 228 | // plugins?.forEach((plugin) => schemaDef.plugin(plugin)); 229 | // return { 230 | // model: model(modelName, schemaDef), 231 | // schema: schemaDef, 232 | // }; 233 | // }; 234 | // export default generateDynamicSchema; 235 | // import { request } from "express"; 236 | // import CustomError from "./error.handler"; 237 | // import { CustomMessageI } from "./interface.crud"; 238 | // import Queries from "./query"; 239 | // import responseMessage from "./responseMessage"; 240 | // class CrudService { 241 | // static async create({ 242 | // check, 243 | // modelData, 244 | // data, 245 | // }: { 246 | // modelData: CrudModelI; 247 | // data: T; 248 | // check: FilterQuery; 249 | // }): Promise { 250 | // const find = 251 | // Object.keys(check).length !== 0 252 | // ? await modelData.Model.findOne(check) 253 | // : null; 254 | // if (find) { 255 | // throw new CustomError( 256 | // httpStatus.BAD_REQUEST, 257 | // `the data ${JSON.stringify( 258 | // Object.keys(check).join(", ") 259 | // )} already exists in the database` 260 | // ); 261 | // } 262 | // const create = new modelData.Model(data); 263 | // const created = await create.save(); 264 | // if (!created) { 265 | // throw new CustomError( 266 | // httpStatus.BAD_REQUEST, 267 | // `${modelData.Model.collection.collectionName} is not successfully created` 268 | // ); 269 | // } 270 | // const dat = await modelData.Model.findById(created._id).select( 271 | // modelData.exempt 272 | // ); 273 | // return responseMessage({ 274 | // success_status: true, 275 | // data: dat, 276 | // message: "Successfully created", 277 | // }); 278 | // } 279 | // static async createMany({ 280 | // check, 281 | // data, 282 | // modelData, 283 | // }: { 284 | // modelData: CrudModelI; 285 | // data: T[]; 286 | // check: FilterQuery[]; 287 | // }): Promise { 288 | // const checks = check.map((findr) => { 289 | // return Object.keys(findr).length !== 0 290 | // ? modelData.Model.findOne(findr) 291 | // : null; 292 | // }); 293 | // const finds = await Promise.all(checks); 294 | // finds.forEach((find, index) => { 295 | // if (find) { 296 | // throw new CustomError( 297 | // httpStatus.BAD_REQUEST, 298 | // `the data ${JSON.stringify( 299 | // Object.keys(check[index]).join(", ") 300 | // )} already exists in the database` 301 | // ); 302 | // } 303 | // }); 304 | // const created = await modelData.Model.insertMany(data); 305 | // if (!created) { 306 | // throw new CustomError( 307 | // httpStatus.BAD_REQUEST, 308 | // `${modelData.Model.collection.collectionName} is not successfully created` 309 | // ); 310 | // } 311 | // const exemptedData = await Promise.all( 312 | // created.map((item) => 313 | // modelData.Model.findById(item._id).select(modelData.exempt) 314 | // ) 315 | // ); 316 | // return responseMessage({ 317 | // success_status: true, 318 | // data: exemptedData, 319 | // message: "Successfully created", 320 | // }); 321 | // } 322 | // static async update({ 323 | // data, 324 | // filter, 325 | // modelData, 326 | // }: { 327 | // modelData: CrudModelI; 328 | // data: UpdateQuery; 329 | // filter: FilterQuery; 330 | // }): Promise { 331 | // const dataF: Array = []; 332 | // const findAndUpdate = await modelData.Model.findOneAndUpdate( 333 | // filter, 334 | // data 335 | // ).select(modelData.exempt); 336 | // if (!findAndUpdate) { 337 | // throw new CustomError( 338 | // httpStatus.BAD_REQUEST, 339 | // `${data} not updated successfully` 340 | // ); 341 | // } else { 342 | // dataF.push(findAndUpdate); 343 | // } 344 | // return responseMessage({ 345 | // success_status: true, 346 | // data: dataF[0], 347 | // message: "Successfully updated", 348 | // }); 349 | // } 350 | // static async getMany({ 351 | // filter, 352 | // modelData, 353 | // populate, 354 | // query, 355 | // }: { 356 | // modelData: CrudModelI; 357 | // query: typeof request.query; 358 | // populate: PopulateFieldI | PopulateFieldI[]; 359 | // filter: FilterQuery | null; 360 | // }): Promise { 361 | // const all: any[] = []; 362 | // const processModel = async (model: CrudModelI) => { 363 | // let modelFind = filter ? model.Model.find(filter) : model.Model.find(); 364 | // if (model.exempt) { 365 | // modelFind = modelFind.select(model.exempt); 366 | // } 367 | // if (populate) { 368 | // modelFind = CrudService.populateModel(modelFind, populate); 369 | // } 370 | // const queryf = new Queries(modelFind, query) 371 | // .filter() 372 | // .limitFields() 373 | // .paginate() 374 | // .sort(); 375 | // const queryG = await queryf.model; 376 | // if (!queryG) { 377 | // throw new CustomError(httpStatus.NOT_FOUND, `${model} is not fetched`); 378 | // } 379 | // all.push(queryG); 380 | // }; 381 | // // if (Array.isArray(modelData)) { 382 | // // await Promise.all(modelData.map(processModel)); 383 | // // } else { 384 | // await processModel(modelData); 385 | // // } 386 | // return responseMessage({ 387 | // success_status: true, 388 | // message: "Data fetched successfully", 389 | // data: all[0], 390 | // doc_length: all.length, 391 | // }); 392 | // } 393 | // private static populateModel( 394 | // modelFind: any, 395 | // populate: PopulateFieldI | PopulateFieldI[] 396 | // ): any { 397 | // if (!populate) { 398 | // return modelFind; 399 | // } 400 | // const populateArr = Array.isArray(populate) ? populate : [populate]; 401 | // return populateArr.reduce((model, pop) => { 402 | // if (!pop.model) { 403 | // return model; 404 | // } 405 | // return model.populate({ 406 | // path: pop.model, 407 | // select: pop.fields, 408 | // populate: pop.second_layer_populate, 409 | // }); 410 | // }, modelFind); 411 | // } 412 | // static async delete({ 413 | // data, 414 | // modelData, 415 | // }: { 416 | // modelData: CrudModelI; 417 | // data: FilterQuery; 418 | // }): Promise { 419 | // // if (Array.isArray(modelData)) { 420 | // // Promise.all( 421 | // // modelData.map(async (model) => { 422 | // // const delet = await model.Model.deleteOne(data); 423 | // // if (!delet) { 424 | // // throw new CustomError ( 425 | // // httpStatus.NOT_IMPLEMENTED, 426 | // // `${model} is not successfully deleted` 427 | // // ); 428 | // // } 429 | // // }) 430 | // // ); 431 | // // } else { 432 | // const delet = await modelData.Model.deleteOne(data); 433 | // if (!delet) { 434 | // throw new CustomError( 435 | // httpStatus.NOT_FOUND, 436 | // `${modelData} is not successfully deleted` 437 | // ); 438 | // // } 439 | // } 440 | // return responseMessage({ 441 | // success_status: true, 442 | // message: "Deleted successfully", 443 | // data: "deleted", 444 | // }); 445 | // } 446 | // static async getOne({ 447 | // modelData, 448 | // data, 449 | // populate, 450 | // }: { 451 | // modelData: CrudModelI; 452 | // data: FilterQuery; 453 | // populate: PopulateFieldI | PopulateFieldI[]; 454 | // }) { 455 | // // const response = await CrudService. 456 | // const getData = []; 457 | // let getOne: any; 458 | // getOne = modelData.Model.findOne(data).select(modelData.exempt); 459 | // if (!getOne) 460 | // throw new CustomError( 461 | // httpStatus.NOT_FOUND, 462 | // `${modelData} is not successfully fetched` 463 | // ); 464 | // if (populate && Array.isArray(populate)) 465 | // populate.forEach((pop) => { 466 | // if (pop.model) 467 | // getOne = getOne.populate({ 468 | // path: pop.model, 469 | // select: pop.fields, 470 | // populate: pop.second_layer_populate, 471 | // }); 472 | // }); 473 | // else if (populate && !Array.isArray(populate)) 474 | // if (populate.model) 475 | // getOne = getOne.populate({ 476 | // path: populate.model, 477 | // select: populate.fields, 478 | // populate: populate.second_layer_populate, 479 | // }); 480 | // const gotten = await getOne.exec(); 481 | // getData.push(gotten); 482 | // return responseMessage({ 483 | // success_status: true, 484 | // message: " fetched successfully", 485 | // data: getData[0], 486 | // }); 487 | // } 488 | // } 489 | // export default CrudService; 490 | // class CustomError extends Error { 491 | // status: number; 492 | // isOperational: boolean; 493 | // constructor(status: number, message: string, isOperational = true) { 494 | // super(message); 495 | // this.status = status; 496 | // this.isOperational = isOperational; 497 | // // Set the prototype explicitly. 498 | // Object.setPrototypeOf(this, CustomError.prototype); 499 | // } 500 | // } 501 | // export const errorCenter = ({ 502 | // error, 503 | // response, 504 | // env = "development", 505 | // }: { 506 | // error: any; 507 | // response: Response; 508 | // env: "production" | "development"; 509 | // }) => { 510 | // const error_status = error.statusCode ?? 500; 511 | // let error_message: string = error.message; 512 | // //mongodb error 513 | // if (error_message.includes("E11000")) { 514 | // error_message = "data already exist in the database"; 515 | // } 516 | // const error_info = error.information; 517 | // response.status(error_status).json( 518 | // responseMessage( 519 | // { 520 | // message: error_info, 521 | // success_status: false, 522 | // data: error_message, 523 | // stack: error.stack, 524 | // }, 525 | // env 526 | // ) 527 | // ); 528 | // }; 529 | // export default CustomError; 530 | // function responseMessage( 531 | // msg: CustomMessageI, 532 | // config: "development" | "production" = "development" 533 | // ) { 534 | // switch (msg.success_status) { 535 | // case true: 536 | // return { 537 | // message: msg.message, 538 | // data: msg.data, 539 | // success: msg.success_status, 540 | // doc_length: msg.doc_length, 541 | // }; 542 | // case false: 543 | // return { 544 | // message: msg.message, 545 | // error: msg.error, 546 | // success: msg.success_status, 547 | // stack: config === "development" ? msg.stack : {}, 548 | // }; 549 | // } 550 | // } 551 | // export default responseMessage; 552 | // class Queries { 553 | // model: any; 554 | // request_query: any; 555 | // constructor(model: any, request_query: any) { 556 | // this.model = model; 557 | // this.request_query = request_query; 558 | // } 559 | // /** 560 | // * The filter function removes excluded fields from the query object and converts it into a string 561 | // * that can be used to filter the model. 562 | // * @returns The filter() method is returning the updated instance of the object. 563 | // */ 564 | // filter() { 565 | // const queryObj = { ...this.request_query }; 566 | // const excludedFields = ["page", "sort", "limit", "fields"]; 567 | // excludedFields.forEach((el) => delete queryObj[el]); 568 | // let queryStr = JSON.stringify(queryObj); 569 | // queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`); 570 | // this.model = this.model.find(JSON.parse(queryStr)); 571 | // return this; 572 | // } 573 | // sort() { 574 | // if (this.request_query.sort) { 575 | // const sortBy = this.request_query.sort.split(",").join(" "); 576 | // this.model = this.model.sort(sortBy); 577 | // } else { 578 | // this.model = this.model.sort("-created_at"); 579 | // } 580 | // return this; 581 | // } 582 | // limitFields() { 583 | // if (this.request_query.fields) { 584 | // const fields = this.request_query.fields.split(",").join(" "); 585 | // this.model = this.model.select(fields); 586 | // } else { 587 | // this.model = this.model.select("-__v"); 588 | // } 589 | // return this; 590 | // } 591 | // paginate() { 592 | // const page = this.request_query.page * 1 || 1; 593 | // const limit = this.request_query.limit * 1 || 100; 594 | // const skip = (page - 1) * limit; 595 | // this.model = this.model.skip(skip).limit(limit); 596 | // return this; 597 | // } 598 | // } 599 | // export default Queries; 600 | //# sourceMappingURL=all.js.map --------------------------------------------------------------------------------