crud-api-express 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +51 -0
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -0
- package/package.json +27 -0
- package/readme.md +91 -0
- package/rollup.config.js +16 -0
- package/src/index.ts +216 -0
- package/tsconfig.json +17 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/// <reference types="mongoose/types/aggregate" />
|
|
2
|
+
/// <reference types="mongoose/types/callback" />
|
|
3
|
+
/// <reference types="mongoose/types/collection" />
|
|
4
|
+
/// <reference types="mongoose/types/connection" />
|
|
5
|
+
/// <reference types="mongoose/types/cursor" />
|
|
6
|
+
/// <reference types="mongoose/types/document" />
|
|
7
|
+
/// <reference types="mongoose/types/error" />
|
|
8
|
+
/// <reference types="mongoose/types/expressions" />
|
|
9
|
+
/// <reference types="mongoose/types/helpers" />
|
|
10
|
+
/// <reference types="mongoose/types/middlewares" />
|
|
11
|
+
/// <reference types="mongoose/types/indexes" />
|
|
12
|
+
/// <reference types="mongoose/types/models" />
|
|
13
|
+
/// <reference types="mongoose/types/mongooseoptions" />
|
|
14
|
+
/// <reference types="mongoose/types/pipelinestage" />
|
|
15
|
+
/// <reference types="mongoose/types/populate" />
|
|
16
|
+
/// <reference types="mongoose/types/query" />
|
|
17
|
+
/// <reference types="mongoose/types/schemaoptions" />
|
|
18
|
+
/// <reference types="mongoose/types/schematypes" />
|
|
19
|
+
/// <reference types="mongoose/types/session" />
|
|
20
|
+
/// <reference types="mongoose/types/types" />
|
|
21
|
+
/// <reference types="mongoose/types/utility" />
|
|
22
|
+
/// <reference types="mongoose/types/validation" />
|
|
23
|
+
/// <reference types="mongoose/types/virtuals" />
|
|
24
|
+
/// <reference types="mongoose/types/inferschematype" />
|
|
25
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
26
|
+
import { Request, Response, Router, NextFunction } from 'express';
|
|
27
|
+
import { Document, Model } from 'mongoose';
|
|
28
|
+
interface CrudOptions<T extends Document> {
|
|
29
|
+
middleware?: ((req: Request, res: Response, next: NextFunction) => void)[];
|
|
30
|
+
onSuccess?: (res: Response, method: string, result: T | T[]) => void;
|
|
31
|
+
onError?: (res: Response, method: string, error: Error) => void;
|
|
32
|
+
methods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[];
|
|
33
|
+
relatedModel?: Model<any>;
|
|
34
|
+
relatedField?: string;
|
|
35
|
+
relatedMethods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[];
|
|
36
|
+
aggregatePipeline?: object[];
|
|
37
|
+
customRoutes?: {
|
|
38
|
+
method: 'post' | 'get' | 'put' | 'delete';
|
|
39
|
+
path: string;
|
|
40
|
+
handler: (req: Request, res: Response) => void;
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
declare class CrudController<T extends Document> {
|
|
44
|
+
private model;
|
|
45
|
+
private endpoint;
|
|
46
|
+
private router;
|
|
47
|
+
constructor(model: Model<T>, endpoint: string, options?: CrudOptions<T>);
|
|
48
|
+
private configureRoutes;
|
|
49
|
+
getRouter(): Router;
|
|
50
|
+
}
|
|
51
|
+
export default CrudController;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
|
|
3
|
+
class CrudController {
|
|
4
|
+
constructor(model, endpoint, options = {}) {
|
|
5
|
+
this.model = model;
|
|
6
|
+
this.endpoint = endpoint;
|
|
7
|
+
this.router = Router();
|
|
8
|
+
this.configureRoutes(options);
|
|
9
|
+
}
|
|
10
|
+
configureRoutes(options) {
|
|
11
|
+
const { middleware = [], onSuccess = (res, method, result) => res.status(200).send(result), onError = (res, method, error) => res.status(400).send(error), methods = ['POST', 'GET', 'PUT', 'DELETE'] } = options;
|
|
12
|
+
const applyMiddleware = (routeHandler) => {
|
|
13
|
+
return [...middleware, routeHandler];
|
|
14
|
+
};
|
|
15
|
+
// Create
|
|
16
|
+
if (methods.includes('POST')) {
|
|
17
|
+
this.router.post(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
18
|
+
const method = 'POST';
|
|
19
|
+
try {
|
|
20
|
+
const item = new this.model(req.body);
|
|
21
|
+
const result = await item.save();
|
|
22
|
+
if (options.relatedModel && options.relatedMethods?.includes('POST')) {
|
|
23
|
+
await options.relatedModel.create({ [options.relatedField]: result._id, ...req.body });
|
|
24
|
+
}
|
|
25
|
+
onSuccess(res, method, result);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
onError(res, method, error);
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
if (methods.includes('GET')) {
|
|
33
|
+
this.router.get(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
34
|
+
const method = 'GET';
|
|
35
|
+
try {
|
|
36
|
+
const { filter, sort, page, limit } = req.query;
|
|
37
|
+
const query = filter ? JSON.parse(filter) : {};
|
|
38
|
+
const sortOrder = sort ? JSON.parse(sort) : {};
|
|
39
|
+
const pageNumber = parseInt(page, 10) || 1;
|
|
40
|
+
const pageSize = parseInt(limit, 10) || 10;
|
|
41
|
+
const skip = (pageNumber - 1) * pageSize;
|
|
42
|
+
let items;
|
|
43
|
+
if (options.relatedModel && options.relatedMethods?.includes('GET')) {
|
|
44
|
+
items = await this.model.aggregate([
|
|
45
|
+
{ $match: query },
|
|
46
|
+
{
|
|
47
|
+
$lookup: {
|
|
48
|
+
from: options.relatedModel.collection.name,
|
|
49
|
+
localField: options.relatedField,
|
|
50
|
+
foreignField: '_id',
|
|
51
|
+
as: 'relatedData'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{ $sort: sortOrder },
|
|
55
|
+
{ $skip: skip },
|
|
56
|
+
{ $limit: pageSize }
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
items = await this.model.find(query).sort(sortOrder).skip(skip).limit(pageSize);
|
|
61
|
+
}
|
|
62
|
+
onSuccess(res, method, items);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
onError(res, method, error);
|
|
66
|
+
}
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
if (methods.includes('GET')) {
|
|
70
|
+
this.router.get(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
71
|
+
const method = 'GET';
|
|
72
|
+
try {
|
|
73
|
+
let item;
|
|
74
|
+
if (options.relatedModel && options.relatedMethods?.includes('GET')) {
|
|
75
|
+
const aggregateResult = await this.model.aggregate([
|
|
76
|
+
{ $match: { _id: req.params.id } },
|
|
77
|
+
{
|
|
78
|
+
$lookup: {
|
|
79
|
+
from: options.relatedModel.collection.name,
|
|
80
|
+
localField: options.relatedField,
|
|
81
|
+
foreignField: '_id',
|
|
82
|
+
as: 'relatedData'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]);
|
|
86
|
+
item = aggregateResult[0];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
item = await this.model.findById(req.params.id);
|
|
90
|
+
}
|
|
91
|
+
if (!item) {
|
|
92
|
+
return res.status(404).send();
|
|
93
|
+
}
|
|
94
|
+
onSuccess(res, method, item);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
onError(res, method, error);
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
if (methods.includes('PUT')) {
|
|
102
|
+
this.router.put(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
103
|
+
const method = 'PUT';
|
|
104
|
+
try {
|
|
105
|
+
const item = await this.model.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
|
|
106
|
+
if (!item) {
|
|
107
|
+
return res.status(404).send();
|
|
108
|
+
}
|
|
109
|
+
if (options.relatedModel && options.relatedMethods?.includes('PUT')) {
|
|
110
|
+
await options.relatedModel.updateMany({ [options.relatedField]: item._id }, req.body);
|
|
111
|
+
}
|
|
112
|
+
onSuccess(res, method, item);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
onError(res, method, error);
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
if (methods.includes('DELETE')) {
|
|
120
|
+
this.router.delete(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
121
|
+
const method = 'DELETE';
|
|
122
|
+
try {
|
|
123
|
+
const query = req.query.filter ? JSON.parse(req.query.filter) : {};
|
|
124
|
+
const deleteResult = await this.model.deleteMany(query);
|
|
125
|
+
if (deleteResult.deletedCount === 0) {
|
|
126
|
+
return res.status(404).send();
|
|
127
|
+
}
|
|
128
|
+
if (options.relatedModel && options.relatedMethods?.includes('DELETE')) {
|
|
129
|
+
await options.relatedModel.deleteMany({ [options.relatedField]: { $in: query } });
|
|
130
|
+
}
|
|
131
|
+
onSuccess(res, method, deleteResult);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
onError(res, method, error);
|
|
135
|
+
}
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
if (methods.includes('DELETE')) {
|
|
139
|
+
this.router.delete(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
140
|
+
const method = 'DELETE';
|
|
141
|
+
try {
|
|
142
|
+
const item = await this.model.findByIdAndDelete(req.params.id);
|
|
143
|
+
if (!item) {
|
|
144
|
+
return res.status(404).send();
|
|
145
|
+
}
|
|
146
|
+
if (options.relatedModel && options.relatedMethods?.includes('DELETE')) {
|
|
147
|
+
await options.relatedModel.deleteMany({ [options.relatedField]: item._id });
|
|
148
|
+
}
|
|
149
|
+
onSuccess(res, method, item);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
onError(res, method, error);
|
|
153
|
+
}
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
if (methods.includes('GET') && options.aggregatePipeline) {
|
|
157
|
+
this.router.get(`/${this.endpoint}/aggregate`, applyMiddleware(async (req, res) => {
|
|
158
|
+
const method = 'GET (Aggregate)';
|
|
159
|
+
try {
|
|
160
|
+
const pipeline = options.aggregatePipeline ?? [];
|
|
161
|
+
const results = await this.model.aggregate(pipeline);
|
|
162
|
+
onSuccess(res, method, results);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
onError(res, method, error);
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
if (options.customRoutes) {
|
|
170
|
+
options.customRoutes.forEach(route => {
|
|
171
|
+
const { method, path, handler } = route;
|
|
172
|
+
if (methods.includes(method.toUpperCase())) {
|
|
173
|
+
this.router[method](`/${this.endpoint}${path}`, applyMiddleware(handler));
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
getRouter() {
|
|
179
|
+
return this.router;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { CrudController as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crud-api-express",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "npx rollup -c"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"express": "^4.19.2",
|
|
16
|
+
"mongoose": "^8.4.3",
|
|
17
|
+
"rollup": "^4.12.0",
|
|
18
|
+
"typescript": "^5.4.5"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
22
|
+
"@types/express": "^4.17.21",
|
|
23
|
+
"@types/mongoose": "^5.11.97",
|
|
24
|
+
"@types/react": "^18.3.3",
|
|
25
|
+
"tslib": "^2.6.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# CRUD API Controller using Express and Mongoose
|
|
2
|
+
|
|
3
|
+
npm install crud-api
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
This project provides a flexible and reusable CRUD (Create, Read, Update, Delete) API controller for MongoDB using Express.js and Mongoose.
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Introduction](#introduction)
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Usage](#usage)
|
|
13
|
+
- [API](#api)
|
|
14
|
+
- [Examples](#examples)
|
|
15
|
+
- [Contributing](#contributing)
|
|
16
|
+
- [License](#license)
|
|
17
|
+
|
|
18
|
+
## Introduction
|
|
19
|
+
|
|
20
|
+
The `CrudController` class is designed to simplify the creation of RESTful APIs in Node.js applications that use MongoDB as the database backend. It abstracts away common CRUD operations, error handling, middleware integration, and supports custom routes and aggregation pipelines.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
To use `CrudController` in your Node.js project, follow these steps:
|
|
25
|
+
|
|
26
|
+
1. **Install Node.js**: Make sure you have Node.js installed on your system.
|
|
27
|
+
2. **Install Dependencies**: Navigate to your project directory and run:
|
|
28
|
+
```bash
|
|
29
|
+
npm install express mongoose
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Usage
|
|
33
|
+
Here's a basic example of how to use CrudController:
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
import express, { Request, Response } from 'express';
|
|
37
|
+
import mongoose, { Document, Model } from 'mongoose';
|
|
38
|
+
import CrudController, { CrudOptions } from 'crud-api';
|
|
39
|
+
|
|
40
|
+
// Define your Mongoose schema and model
|
|
41
|
+
interface ExampleModel extends Document {
|
|
42
|
+
name: string;
|
|
43
|
+
age: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ExampleSchema = new mongoose.Schema<ExampleModel>({
|
|
47
|
+
name: { type: String, required: true },
|
|
48
|
+
age: { type: Number, required: true },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const ExampleModel = mongoose.model<ExampleModel>('Example', ExampleSchema);
|
|
52
|
+
|
|
53
|
+
// Create an instance of CrudController
|
|
54
|
+
const exampleController = new CrudController<ExampleModel>(ExampleModel, 'examples', {
|
|
55
|
+
// Optional configuration
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Create Express application
|
|
59
|
+
const app = express();
|
|
60
|
+
|
|
61
|
+
// Mount the CrudController router
|
|
62
|
+
app.use('/api/examples', exampleController.getRouter());
|
|
63
|
+
|
|
64
|
+
// Start the server
|
|
65
|
+
const PORT = process.env.PORT || 3000;
|
|
66
|
+
app.listen(PORT, () => {
|
|
67
|
+
console.log(`Server is running on port ${PORT}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# API
|
|
72
|
+
|
|
73
|
+
model: Mongoose model for CRUD operations.
|
|
74
|
+
endpoint: API endpoint path.
|
|
75
|
+
options: Optional configuration for CRUD operations.
|
|
76
|
+
Methods
|
|
77
|
+
getRouter(): Router - Returns the Express Router instance configured with CRUD routes.
|
|
78
|
+
|
|
79
|
+
CrudOptions<T>
|
|
80
|
+
Configuration options for CrudController.
|
|
81
|
+
|
|
82
|
+
# options
|
|
83
|
+
middleware?: ((req: Request, res: Response, next: NextFunction) => void)[] - Array of middleware functions.
|
|
84
|
+
onSuccess?: (res: Response, method: string, result: T | T[]) => void - Success handler function.
|
|
85
|
+
onError?: (res: Response, method: string, error: Error) => void - Error handler function.
|
|
86
|
+
methods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[] - Array of HTTP methods to support.
|
|
87
|
+
relatedModel?: Model<any> - Related Mongoose model for relational operations.
|
|
88
|
+
relatedField?: string - Field name for related models.
|
|
89
|
+
relatedMethods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[] - Methods to apply on related models.
|
|
90
|
+
aggregatePipeline?: object[] - MongoDB aggregation pipeline stages.
|
|
91
|
+
customRoutes?: { method: 'post' | 'get' | 'put' | 'delete', path: string, handler: (req: Request, res: Response) => void }[] - Array of custom routes definitions.
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from "rollup";
|
|
2
|
+
import typescript from "@rollup/plugin-typescript";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
input: 'src/index.ts',
|
|
9
|
+
output:{
|
|
10
|
+
dir:'dist',
|
|
11
|
+
format:'es',
|
|
12
|
+
name:"crud-api-express"
|
|
13
|
+
},
|
|
14
|
+
external:['express', 'mongoose'],
|
|
15
|
+
plugins:[typescript({tsconfig:'tsconfig.json'})]
|
|
16
|
+
})
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { Request, Response, Router, NextFunction } from 'express';
|
|
2
|
+
import { Document, Model, Aggregate } from 'mongoose';
|
|
3
|
+
|
|
4
|
+
interface CrudOptions<T extends Document> {
|
|
5
|
+
middleware?: ((req: Request, res: Response, next: NextFunction) => void)[];
|
|
6
|
+
onSuccess?: (res: Response, method: string, result: T | T[]) => void;
|
|
7
|
+
onError?: (res: Response, method: string, error: Error) => void;
|
|
8
|
+
methods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[];
|
|
9
|
+
relatedModel?: Model<any>;
|
|
10
|
+
relatedField?: string;
|
|
11
|
+
relatedMethods?: ('POST' | 'GET' | 'PUT' | 'DELETE')[];
|
|
12
|
+
aggregatePipeline?: object[];
|
|
13
|
+
customRoutes?: { method: 'post' | 'get' | 'put' | 'delete', path: string, handler: (req: Request, res: Response) => void }[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class CrudController<T extends Document> {
|
|
17
|
+
private model: Model<T>;
|
|
18
|
+
private endpoint: string;
|
|
19
|
+
private router: Router;
|
|
20
|
+
|
|
21
|
+
constructor(model: Model<T>, endpoint: string, options: CrudOptions<T> = {}) {
|
|
22
|
+
this.model = model;
|
|
23
|
+
this.endpoint = endpoint;
|
|
24
|
+
this.router = Router();
|
|
25
|
+
this.configureRoutes(options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private configureRoutes(options: CrudOptions<T>) {
|
|
29
|
+
const {
|
|
30
|
+
middleware = [],
|
|
31
|
+
onSuccess = (res, method, result) => res.status(200).send(result),
|
|
32
|
+
onError = (res, method, error) => res.status(400).send(error),
|
|
33
|
+
methods = ['POST', 'GET', 'PUT', 'DELETE']
|
|
34
|
+
} = options;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const applyMiddleware = (routeHandler: (req: Request, res: Response) => void) => {
|
|
38
|
+
return [...middleware, routeHandler];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Create
|
|
42
|
+
if (methods.includes('POST')) {
|
|
43
|
+
this.router.post(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
44
|
+
const method = 'POST';
|
|
45
|
+
try {
|
|
46
|
+
const item = new this.model(req.body);
|
|
47
|
+
const result:any = await item.save();
|
|
48
|
+
if (options.relatedModel && options.relatedMethods?.includes('POST')) {
|
|
49
|
+
await options.relatedModel.create({ [options.relatedField!]: result._id, ...req.body });
|
|
50
|
+
}
|
|
51
|
+
onSuccess(res, method, result);
|
|
52
|
+
} catch (error:any) {
|
|
53
|
+
onError(res, method, error);
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if (methods.includes('GET')) {
|
|
60
|
+
this.router.get(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
61
|
+
const method = 'GET';
|
|
62
|
+
try {
|
|
63
|
+
const { filter, sort, page, limit } = req.query;
|
|
64
|
+
const query = filter ? JSON.parse(filter as string) : {};
|
|
65
|
+
const sortOrder = sort ? JSON.parse(sort as string) : {};
|
|
66
|
+
const pageNumber = parseInt(page as string, 10) || 1;
|
|
67
|
+
const pageSize = parseInt(limit as string, 10) || 10;
|
|
68
|
+
const skip = (pageNumber - 1) * pageSize;
|
|
69
|
+
|
|
70
|
+
let items: T[] | Aggregate<any[]>;
|
|
71
|
+
if (options.relatedModel && options.relatedMethods?.includes('GET')) {
|
|
72
|
+
items = await this.model.aggregate([
|
|
73
|
+
{ $match: query },
|
|
74
|
+
{
|
|
75
|
+
$lookup: {
|
|
76
|
+
from: options.relatedModel.collection.name,
|
|
77
|
+
localField: options.relatedField!,
|
|
78
|
+
foreignField: '_id',
|
|
79
|
+
as: 'relatedData'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ $sort: sortOrder },
|
|
83
|
+
{ $skip: skip },
|
|
84
|
+
{ $limit: pageSize }
|
|
85
|
+
]);
|
|
86
|
+
} else {
|
|
87
|
+
items = await this.model.find(query).sort(sortOrder).skip(skip).limit(pageSize);
|
|
88
|
+
}
|
|
89
|
+
onSuccess(res, method, items);
|
|
90
|
+
} catch (error:any) {
|
|
91
|
+
onError(res, method, error);
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (methods.includes('GET')) {
|
|
97
|
+
this.router.get(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
98
|
+
const method = 'GET';
|
|
99
|
+
try {
|
|
100
|
+
let item: T | null;
|
|
101
|
+
if (options.relatedModel && options.relatedMethods?.includes('GET')) {
|
|
102
|
+
const aggregateResult = await this.model.aggregate([
|
|
103
|
+
{ $match: { _id: req.params.id } },
|
|
104
|
+
{
|
|
105
|
+
$lookup: {
|
|
106
|
+
from: options.relatedModel.collection.name,
|
|
107
|
+
localField: options.relatedField!,
|
|
108
|
+
foreignField: '_id',
|
|
109
|
+
as: 'relatedData'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]);
|
|
113
|
+
item = aggregateResult[0] as T;
|
|
114
|
+
} else {
|
|
115
|
+
item = await this.model.findById(req.params.id);
|
|
116
|
+
}
|
|
117
|
+
if (!item) {
|
|
118
|
+
return res.status(404).send();
|
|
119
|
+
}
|
|
120
|
+
onSuccess(res, method, item);
|
|
121
|
+
} catch (error:any) {
|
|
122
|
+
onError(res, method, error);
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if (methods.includes('PUT')) {
|
|
129
|
+
this.router.put(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
130
|
+
const method = 'PUT';
|
|
131
|
+
try {
|
|
132
|
+
const item = await this.model.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
|
|
133
|
+
if (!item) {
|
|
134
|
+
return res.status(404).send();
|
|
135
|
+
}
|
|
136
|
+
if (options.relatedModel && options.relatedMethods?.includes('PUT')) {
|
|
137
|
+
await options.relatedModel.updateMany({ [options.relatedField!]: item._id }, req.body);
|
|
138
|
+
}
|
|
139
|
+
onSuccess(res, method, item);
|
|
140
|
+
} catch (error:any) {
|
|
141
|
+
onError(res, method, error);
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if (methods.includes('DELETE')) {
|
|
148
|
+
this.router.delete(`/${this.endpoint}`, applyMiddleware(async (req, res) => {
|
|
149
|
+
const method = 'DELETE';
|
|
150
|
+
try {
|
|
151
|
+
const query = req.query.filter ? JSON.parse(req.query.filter as string) : {};
|
|
152
|
+
const deleteResult:any = await this.model.deleteMany(query);
|
|
153
|
+
if (deleteResult.deletedCount === 0) {
|
|
154
|
+
return res.status(404).send();
|
|
155
|
+
}
|
|
156
|
+
if (options.relatedModel && options.relatedMethods?.includes('DELETE')) {
|
|
157
|
+
await options.relatedModel.deleteMany({ [options.relatedField!]: { $in: query } });
|
|
158
|
+
}
|
|
159
|
+
onSuccess(res, method, deleteResult);
|
|
160
|
+
} catch (error: any) {
|
|
161
|
+
onError(res, method, error);
|
|
162
|
+
}
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if (methods.includes('DELETE')) {
|
|
169
|
+
this.router.delete(`/${this.endpoint}/:id`, applyMiddleware(async (req, res) => {
|
|
170
|
+
const method = 'DELETE';
|
|
171
|
+
try {
|
|
172
|
+
const item = await this.model.findByIdAndDelete(req.params.id);
|
|
173
|
+
if (!item) {
|
|
174
|
+
return res.status(404).send();
|
|
175
|
+
}
|
|
176
|
+
if (options.relatedModel && options.relatedMethods?.includes('DELETE')) {
|
|
177
|
+
await options.relatedModel.deleteMany({ [options.relatedField!]: item._id });
|
|
178
|
+
}
|
|
179
|
+
onSuccess(res, method, item);
|
|
180
|
+
} catch (error:any) {
|
|
181
|
+
onError(res, method, error);
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if (methods.includes('GET') && options.aggregatePipeline) {
|
|
188
|
+
this.router.get(`/${this.endpoint}/aggregate`, applyMiddleware(async (req, res) => {
|
|
189
|
+
const method = 'GET (Aggregate)';
|
|
190
|
+
try {
|
|
191
|
+
const pipeline:any = options.aggregatePipeline ?? [];
|
|
192
|
+
const results = await this.model.aggregate(pipeline);
|
|
193
|
+
onSuccess(res, method, results);
|
|
194
|
+
} catch (error:any) {
|
|
195
|
+
onError(res, method, error);
|
|
196
|
+
}
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if (options.customRoutes) {
|
|
202
|
+
options.customRoutes.forEach(route => {
|
|
203
|
+
const { method, path, handler } = route;
|
|
204
|
+
if (methods.includes(method.toUpperCase() as 'POST' | 'GET' | 'PUT' | 'DELETE')) {
|
|
205
|
+
this.router[method](`/${this.endpoint}${path}`, applyMiddleware(handler));
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public getRouter(): Router {
|
|
212
|
+
return this.router;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default CrudController;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"sourceMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|