npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

easy-express-cwa

v1.2.10

Published

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

Downloads

114

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 [email protected].

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