easy-express
is a CLI tool to automate the setup of a common Express.js backend. This tool helps you quickly scaffold a backend setup without rewriting the setup each time.
- Predefined Backend Setup: Copies a predefined backend setup to your current working directory.
- Common Configurations: Includes configurations such as
.env.example
,.eslintignore
,.eslintrc
,.gitignore
,.prettierrc
,docker-compose.yml
,Dockerfile
,package.json
, andtsconfig.json
. - Prebuilt Functionalities:
- 🔐 Authentication and authorization
- 🔑 JWT handling
- 🔗 Login with Google Auth
- 📜 Logger setup
- 🐳 Docker configuration
- 🚀 Redis integration
- 📂 File upload middleware (Cloudinary, AWS S3)
- 📘 Swagger for API documentation
- 🍪 Cookies handling
- 🔒 Security features
- 🛠️ Many more features upcoming
npm install -g easy-express-cwa
mkdir server
cd server
npx easy-express-cwa
Configure your environment variables:
Copy the .env.example
file to .env
and update the values as needed:
NODE_ENV=development
PORT=8000
DB_URL=mongodb://localhost:27017/yourdb
ENCRYPTION_METHOD=AES-256-CBC
ENCRYPTION_KEY=DR8j97BtgHVBiEKAjqRlfn6VSLTJTIpwsgNo0vTWKvA=
BCRYPT_SALT_ROUNDS=14
DOMAIN=yourdomain.com
APP_ID=your-app-id
APP_CERTIFICATE=your-app-certificate
JWT_SECRET=GiCj9Qrmy4vYeDbBjrVCszy0xlN5PGZQQ77iLExHVuI=
JWT_REFRESH_SECRET=jiS8zP3qHU2fgKblrhqVKhFEYYqpwsrh/6Z/Ak0ZhL8=
JWT_EXPIRATION_TIME=3d
JWT_REFRESH_EXPIRATION_TIME=3d
CLOUDINARY_CLOUD_NAME=""
CLOUDINARY_API_KEY=""
CLOUDINARY_API_SECRET=""
REDIS_PASSWORD=your-redis-password
REDIS_HOST=your-redis-host
REDIS_PORT=your-redis-port
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=your-google-callback-url
GOOGLE_REDIRECT_URL=your-google-redirect-url
GOOGLE_APP_USER=your-google-app-user
GOOGLE_APP_PASSWORD=your-google-app-password
Here's the folder structure generated by easy-express
:
└── 📁 server
└── 🚫 .dockerignore
└── 🛠️ .env
└── 🛠️ .env.example
└── 🐳 Dockerfile
└── 📜 README.md
└── 🐳 docker-compose.yml
└── 📦 package.json
└── 📁 src
└── 📁 app
└── 📁 middlewares
└── 🔒 auth.ts
└── 📁 cloudinary
└── ☁️ cloudinary.ts
└── ⚠️ globalErrorHandler.ts
└── 🛠️ handleZodError.ts
└── 📁 multer
└── 📤 multer.ts
└── 📁 redis
└── 🛠️ redis.ts
└── ✅ validateRequest.ts
└── 📁 modules
└── 📁 auth
└── 👤 auth.controller.ts
└── 🚦 auth.route.ts
└── 🛠️ auth.service.ts
└── 📁 example
└── 📄 example.controller.ts
└── 📄 example.interface.ts
└── 🗃️ example.model.ts
└── 🚦 example.route.ts
└── 🛠️ example.service.ts
└── ✅ example.validation.ts
└── 📁 googleOAuth
└── 🌐 googleOAuth.controller.ts
└── 🚦 googleOAuth.route.ts
└── 🛠️ googleOAuth.service.ts
└── 📁 user
└── 👤 user.controller.ts
└── 🗃️ user.interface.ts
└── 🗃️ user.model.ts
└── 🚦 user.route.ts
└── 🛠️ user.service.ts
└── ✅ user.validation.ts
└── 📁 routes
└── 🚦 index.ts
└── 🛠️ app.ts
└── 📁 config
└── ⚙️ index.ts
└── 🛂 passport.ts
└── 📁 constants
└── 💬 message.ts
└── 🔢 pagination.ts
└── ⏳ redisCacheExpireDate.ts
└── 🔑 redisKeys.ts
└── 📁 enums
└── 📄 common.ts
└── 📄 user.ts
└── 📁 errors
└── 🛠️ ApiError.ts
└── ❌ handleCastError.ts
└── 🛠️ handleValidationError.ts
└── 🛠️ handleZodError.ts
└── 📁 helpers
└── 🛡️ jwtHelper.ts
└── 🛠️ paginationHelper.ts
└── 📁 interfaces
└── 📄 common.ts
└── 📄 error.ts
└── 📄 index.d.ts
└── 📄 pagination.ts
└── 🛠️ server.ts
└── 📁 shared
└── 🛠️ catchAsync.ts
└── 📋 logger.ts
└── 🛠️ pick.ts
└── ✉️ sendResponse.ts
└── 📁 utils
└── 📧 mail.util.ts
└── 🔑 oAuth.ts
└── ⚙️ tsconfig.json
└── 📦 yarn.lock
Access the Swagger API documentation at http://localhost:3000/api-docs
.
Contributions are welcome! Please open an issue or submit a pull request for any improvements.
This project is licensed under the MIT License.
For any inquiries or feedback, please reach out at codewithashim@gmail.com.
To add the example code snippets for the example
entity CRUD operations in your README.md
file, you can follow this structure:
- Overview of the Example Module
- Interface
- Model
- Controller
- Service
- Route
- Validation
Here’s how you can add them to your README.md
:
This module provides CRUD operations for the example
entity. Below are the code snippets for the controller, interface, model, route, service, and validation.
import { Model } from "mongoose";
export type IExample = {
title: string;
description: string;
createdAt?: Date;
updatedAt?: Date;
}
export type ExampleModel =Model<IExample, Record<string, unknown>>;
import { Schema, model } from "mongoose";
import { IExample, ExampleModel } from "./example.interface";
const ExampleSchema = new Schema<IExample>(
{
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
},
{
timestamps: true,
toJSON: {
virtuals: true,
},
}
);
export const Example = model<IExample, ExampleModel>("Example", ExampleSchema);
import { Request, Response } from "express";
import httpStatus from "http-status";
import catchAsync from "../../../shared/catchAsync";
import sendResponse from "../../../shared/sendResponse";
import { IExample } from "./example.interface";
import { ExampleService } from "./example.service";
import { responseMessage } from "../../../constants/message";
const getAllExamples = catchAsync(async (req: Request, res: Response) => {
const result = await ExampleService.getAllExamples();
sendResponse<IExample[]>(res, {
statusCode: httpStatus.OK,
success: true,
message: responseMessage.GET_ALL_EXAMPLES_MESSAGE,
data: result,
});
});
const getExampleById = catchAsync(async (req: Request, res: Response) => {
const result = await ExampleService.getExampleById(req.params.id);
sendResponse<IExample>(res, {
statusCode: httpStatus.OK,
success: true,
message: responseMessage.GET_EXAMPLE_BY_ID_MESSAGE,
data: result,
});
});
const updateExample = catchAsync(async (req: Request, res: Response) => {
const id = req.params.id;
const updatedData = req.body;
const result = await ExampleService.updateExample(id, updatedData);
sendResponse<IExample>(res, {
statusCode: httpStatus.OK,
success: true,
message: responseMessage.UPDATE_EXAMPLE_MESSAGE,
data: result,
});
});
const deleteExample = catchAsync(async (req: Request, res: Response) => {
const { id } = req.params;
const result = await ExampleService.deleteExample(id);
sendResponse<IExample>(res, {
statusCode: httpStatus.OK,
success: true,
message: responseMessage.DELETE_EXAMPLE_MESSAGE,
data: result,
});
});
export const ExampleController = {
getAllExamples,
getExampleById,
updateExample,
deleteExample,
};
import ApiError from "../../../errors/ApiError";
import { Example } from "./example.model";
import { IExample } from "./example.interface";
import httpStatus from "http-status";
import { responseMessage } from "../../../constants/message";
const getAllExamples = async (): Promise<Array<IExample>> => {
try {
const examples = await Example.find();
return examples;
} catch (error) {
throw new ApiError(
httpStatus.INTERNAL_SERVER_ERROR,
`${responseMessage.FAILED_MESSAGE} get all examples`
);
}
};
const getExampleById = async (id: string): Promise<IExample | null> => {
try {
const example = await Example.findById(id);
if (!example) {
throw new ApiError(
httpStatus.NOT_FOUND,
`Example ${responseMessage.NOT_FOUND_MESSAGE}`
);
}
return example;
} catch (error) {
throw new ApiError(
httpStatus.INTERNAL_SERVER_ERROR,
`${responseMessage.FAILED_MESSAGE} get example by ID`
);
}
};
const updateExample = async (
id: string,
payload: Partial<IExample>
): Promise<IExample | null> => {
try {
const isExist = await Example.findOne({ _id: id });
if (!isExist) {
throw new ApiError(
httpStatus.NOT_FOUND,
`Example ${responseMessage.NOT_FOUND_MESSAGE}`
);
}
const updateExampleData = payload;
const result = await Example.findOneAndUpdate({ _id: id }, updateExampleData, {
new: true,
});
return result;
} catch (error) {
throw new ApiError(
httpStatus.INTERNAL_SERVER_ERROR,
`${responseMessage.FAILED_MESSAGE} update example`
);
}
};
const deleteExample = async (id: string): Promise<IExample | null> => {
try {
const example = await Example.findByIdAndDelete(id);
if (!example) {
throw new ApiError(
httpStatus.NOT_FOUND,
`Example ${responseMessage.NOT_FOUND_MESSAGE}`
);
}
return example;
} catch (error) {
throw new ApiError(
httpStatus.INTERNAL_SERVER_ERROR,
`${responseMessage.FAILED_MESSAGE} delete example`
);
}
};
export const ExampleService = {
getAllExamples,
getExampleById,
updateExample,
deleteExample,
};
import express from "express";
import validateRequest from "../../middlewares/validateRequest";
import { ExampleController } from "./example.controller";
import { createExampleValidator } from "./example.validation";
const router = express.Router();
router.get("/", ExampleController.getAllExamples);
router.get("/:id", ExampleController.getExampleById);
router.patch(
"/:id",
validateRequest(createExampleValidator.updateExampleZodSchema),
ExampleController.updateExample
);
router.delete("/:id", ExampleController.deleteExample);
export const ExampleRoutes = router
import { z } from "zod";
const createExampleZodSchema = z.object({
body: z.object({
name: z.string({
required_error: "Name is required",
}),
description: z.string({
required_error: "Description is required",
}),
}),
});
const updateExampleZodSchema = z.object({
body: z.object({
name: z.string().optional(),
description: z.string().optional(),
}),
});
export const createExampleValidator = {
createExampleZodSchema,
updateExampleZodSchema,
};