Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 25 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,67 @@
### Installation:

To create a new project using MERN-X, simply run the following command:

```
npx mern-x@latest
```

How to run project
How to run project

```
npx run mern // Project Back End Will Run @3000
npx run dev // Complete Project Will Run @3001
```

How create Model

```
npm run create:model YourModelName
```

How create Controller

```
npm run create:controller YourControllerName
```

How create Middleware

```
npm run create:middleware YourMiddlewareName
```

How create Page

```
npm run create:page YourPageName
```

How create Component

```
npm run create:component YourComponentName
```

How create Loader

```
npm run create:loader YourLoaderName
```

How create Layout

```
npm run create:layout YourLayoutName
```




Project Structure
Project Structure

```php
mern-x/
├── app/
│ ├── config/
├── app/
│ ├── config/
│ │ ├── cli.js
│ │ ├── config.js
│ │
Expand All @@ -74,7 +80,7 @@ mern-x/
│ │ ├── emailUtility.js
│ │ ├── tokenUtility.js
│ │ ├── validationUtility.js
│ │
│ │ ├── queryBuilderUtility.js
│ ├── dist/
│ │
│ ├── node_modules/
Expand All @@ -89,23 +95,23 @@ mern-x/
│ │ ├── assets/
│ │ │ ├── css/
│ │ │ │ ├── style.js
│ │
│ │
│ │ ├── components/
│ │ │ ├── CreateForm.jsx
│ │ │ ├── List.jsx
│ │
│ │ │ ├── List.jsx
│ │
│ │ ├── layout/
│ │ │ ├── AppLayout.jsx
│ │ │
│ │
│ │
│ │ ├── loader/
│ │ │ ├── ListLoader.jsx
│ │ │
│ │ │
│ │ ├── pages/
│ │ │ ├── CreatePage.jsx
│ │ │ ├── ListPage.jsx
│ │
│ │ ├── main.jsx
│ │ │ ├── ListPage.jsx
│ │
│ │ ├── main.jsx
│ │
│ │
│ ├── .gitattributes
Expand All @@ -121,14 +127,14 @@ mern-x/
│ ├── vite.config.js
```


- app: This directory contains the main application code.
- config: Configuration files for the application.
- controllers: Controllers for handling business logic.
- Middleware functions for handling requests.
- models: Database models.
- storage: Directory for storing files (if applicable).
- utility: Utility functions.
-queryBuilderUtility:For searching,filter,pagination,limit,fields selection and meta information.
- dist: Distribution files (compiled code).
- node_modules: Node.js modules installed via npm.
- public: Public assets.
Expand All @@ -148,5 +154,5 @@ mern-x/
- package-lock.json: Dependency lock file.
- postcss.config.js: PostCSS configuration file.
- README.md: Project documentation.
-tailwind.config.js: Tailwind CSS configuration file.
-vite.config.js: Vite configuration file.
-tailwind.config.js: Tailwind CSS configuration file.
-vite.config.js: Vite configuration file.
59 changes: 38 additions & 21 deletions app/controllers/todoController.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,49 @@
import todos from "../models/todosModel.js";
import QueryBuilder from "../utility/queryBuilderUtility.js";

export const store = async (req, res) => {
try {
const { name, description } = req.body;
await todos.create({ name, description });
return res.json({ message: 'Item created successfully' });
} catch (error) {
return res.json({ error: error.message || 'Internal Server Error' });
}
try {
const { name, description } = req.body;
await todos.create({ name, description });
return res.json({ message: "Item created successfully" });
} catch (error) {
return res.json({ error: error.message || "Internal Server Error" });
}
};


export const show = async (req, res) => {
try {
const todo = await todos.find();
return res.json({message:"success",result:todo});
} catch (error) {
return res.json({ error: error.message || 'Internal Server Error' });
}
try {
const todo = await todos.find();
return res.json({ message: "success", result: todo });
} catch (error) {
return res.json({ error: error.message || "Internal Server Error" });
}
};

export const showAll = async (req, res) => {
try {
const query = req.query;
new QueryBuilder(todos.find());
const todosQuery = new QueryBuilder(todos.find(), query)
.search(["name", "description"])
.filter()
.sort()
.paginate()
.fields();
const todo = await todosQuery.modelQuery;
const meta = await todosQuery.countTotal();
return res.json({ message: "success", result: todo, meta });
} catch (error) {
return res.json({ error: error.message || "Internal Server Error" });
}
};

export const destroy = async (req, res) => {
try {
const { id } = req.params;
await todos.deleteOne({_id:id});
return res.json({ message: 'Item deleted successfully' });
} catch (error) {
return res.json({ error: error.message || 'Internal Server Error' });
}
try {
const { id } = req.params;
await todos.deleteOne({ _id: id });
return res.json({ message: "Item deleted successfully" });
} catch (error) {
return res.json({ error: error.message || "Internal Server Error" });
}
};
96 changes: 96 additions & 0 deletions app/utility/queryBuilderUtility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* QueryBuilder class for building MongoDB queries with search, filter, sort, pagination, and field selection capabilities.
*/
class QueryBuilder {
/**
* Constructor for QueryBuilder.
* @param {Query} modelQuery The initial query object generated by Mongoose like todos.find().
* @param {object} query The query object containing search terms, filters, pagination, etc maybe req.query.
*/
constructor(modelQuery, query) {
this.modelQuery = modelQuery;
this.query = query;
}

/**
* Adds search functionality to the query.
* @param {string[]} searchableFields Array of field names to search in.
* @returns {QueryBuilder} Returns the QueryBuilder instance for method chaining.
*/
search(searchableFields) {
const searchTerm = this?.query?.searchTerm;
if (searchTerm) {
this.modelQuery = this.modelQuery.find({
$or: searchableFields.map((field) => ({
[field]: { $regex: searchTerm, $options: "i" },
})),
});
}
return this;
}

/**
* Adds filtering functionality to the query.
* @returns {QueryBuilder} Returns the QueryBuilder instance for method chaining.
*/
filter() {
const queryObj = { ...this.query };
const excludeFields = ["searchTerm", "sort", "limit", "page", "fields"];
excludeFields.forEach((el) => delete queryObj[el]);
this.modelQuery = this.modelQuery.find(queryObj);
return this;
}

/**
* Adds sorting functionality to the query.
* @returns {QueryBuilder} Returns the QueryBuilder instance for method chaining.
*/
sort() {
const sort = this?.query?.sort?.split(",")?.join(" ") || "-createdAt";
this.modelQuery = this.modelQuery.sort(sort);
return this;
}

/**
* Adds pagination functionality to the query.
* @returns {QueryBuilder} Returns the QueryBuilder instance for method chaining.
*/
paginate() {
const page = Number(this?.query?.page) || 1;
const limit = Number(this?.query?.limit) || 10;
const skip = (page - 1) * limit;
this.modelQuery = this.modelQuery.skip(skip).limit(limit);
return this;
}

/**
* Selects specific fields to include or exclude from the query result.
* @returns {QueryBuilder} Returns the QueryBuilder instance for method chaining.
*/
fields() {
const fields = this?.query?.fields?.split(",")?.join(" ") || "-__v";
this.modelQuery = this.modelQuery.select(fields);
return this;
}

/**
* Counts the total number of documents matching the query criteria.
* @returns {Promise<object>} Returns a promise resolving to an object containing page, limit, total, and totalPage.
*/
async countTotal() {
const totalQueries = this.modelQuery.getFilter();
const total = await this.modelQuery.model.countDocuments(totalQueries);
const page = Number(this?.query?.page) || 1;
const limit = Number(this?.query?.limit) || 10;
const totalPage = Math.ceil(total / limit);

return {
page,
limit,
total,
totalPage,
};
}
}

export default QueryBuilder;
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions routes/api.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import express from 'express';
import express from "express";
const router = express.Router();
import * as todoController from '../app/controllers/todoController.js';
import * as todoController from "../app/controllers/todoController.js";

router.post('/store', todoController.store);
router.get('/show', todoController.show);
router.delete('/destroy/:id', todoController.destroy);
router.post("/store", todoController.store);
router.get("/show", todoController.show);
router.get("/showAll", todoController.showAll);
router.delete("/destroy/:id", todoController.destroy);

export default router;
export default router;