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 and npx 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 πŸ› οΈ

  1. 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:

  1. Overview of the Example Module

  2. Interface

  3. Model

  4. Controller

  5. Service

  6. Route

  7. 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

2
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.