1.2.10 β€’ Published 1 year ago

easy-express-cwa v1.2.10

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

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, and tsconfig.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:

  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,
};