Package Exports
- easy-express-cwa
- easy-express-cwa/bin/cli.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (easy-express-cwa) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Easy Express CWA π
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.
Features β¨
- 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
Installation π
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
Folder Structure π
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
API Documentation
Access the Swagger API documentation at http://localhost:3000/api-docs
.
Contributing π€
Contributions are welcome! Please open an issue or submit a pull request for any improvements.
License π
This project is licensed under the MIT License.
Contact π¬
For any inquiries or feedback, please reach out at codewithashim@gmail.com.
Example
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
:
Example Module
This module provides CRUD operations for the example
entity. Below are the code snippets for the controller, interface, model, route, service, and validation.
Interface
import { Model } from "mongoose";
export type IExample = {
title: string;
description: string;
createdAt?: Date;
updatedAt?: Date;
}
export type ExampleModel =Model<IExample, Record<string, unknown>>;
Model
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);
Controller
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,
};
Service
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,
};
Route
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
Validation
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,
};