JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 7
  • Score
    100M100P100Q52843F
  • License MIT

CLI tool to setup a common Express.js backend developed by codewithashim

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