Save Time and Effort Using easy-express-cwa for Backend Automation
π οΈ As developers, setting up Express.js backends can be repetitive. That's why I created easy-express-cwa β a CLI tool that automates the setup process with just one command. It copies your entire backend setup, including TypeScript configurations, Docker files, and more, saving you valuable time.
Key Features β¨
π Authentication & 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
π Enhanced Security Features
π οΈ And many more features coming soon!
π¦ Live Demoπ¦
Watch the demo
π¦ Check it out on npm: https://www.npmjs.com/package/easy-express-cwa
π¦ Check it out on github : https://github.com/codewithashim/Easy-Express-Server-CLI.git
Benefits of Using easy-express-cwa π
Time-Saving: Automates the setup process, so you don't have to repeat the same steps every time you start a new project.
Consistency: Ensures a consistent setup across different projects, reducing errors and improving maintainability.
Comprehensive: Includes a wide range of features for a complete backend setup.
Easy to Use: Simply run
npm install -g easy-express-cwa
andnpx easy-express-cwa
to get started.
Why Use easy-express-cwa? π€
π§ Simplifies Backend Setup: Whether you're starting a new project or looking to streamline your workflow, easy-express-cwa makes the setup process easy and quick.
π Boosts Productivity: Focus on writing code and building features rather than spending time on repetitive setup tasks.
π¦ All-in-One Solution: Comes with all the necessary tools and configurations for a robust and scalable backend.
Installation Process π οΈ
- Install the package globally:
npm install -g easy-express-cwa
mkdir server
npx easy-express-cwa
yarn install
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
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,
};
#codewithashim Ashim Rudra Paul
Subscribe to my newsletter
Read articles from Ashim Rudra Paul directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ashim Rudra Paul
Ashim Rudra Paul
I am a Software Engineer at SJ Innovation with over 2 years of experience specializing in the MERN stack. My expertise spans TypeScript, Next.js, React.js, React Native, Express.js, Firebase, Supabase, MongoDB, PostgreSQL, and MySQL. I hold a degree in Computer Science and Engineering from Sylhet Polytechnic Institute and have earned certifications in MERN stack web development and JavaScript, C, C++, Python programming. Previously a Team Lead at elPixala, I excel in collaborating with product and design teams to create seamless user experiences. I prioritize rigorous testing and debugging to ensure high-quality performance. What sets me apart is my passion for coding and problem-solving. I thrive on crafting innovative solutions to complex challenges. Recently venturing into competitive programming on platforms like HackerRank and Codeforces has further sharpened my skills. Choose me for my proven track record of delivering dynamic web applications that combine functionality with visual appeal. My commitment to quality ensures top-notch results every time.