Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/codewithashim/easy-express-server-cli

`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.
https://github.com/codewithashim/easy-express-server-cli

aws codewithashim common-server docker expressjs modules mongodb nodejs npm-package packe s3 server typescript

Last synced: 3 days ago
JSON representation

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

Awesome Lists containing this project

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 πŸ› 

```sh
npm install -g easy-express-cwa
mkdir server
```

```sh
cd server
```

```sh
npx easy-express-cwa
```

Configure your environment variables:

Copy the `.env.example` file to `.env` and update the values as needed:

```sh
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

```typescript
import { Model } from "mongoose";

export type IExample = {
title: string;
description: string;
createdAt?: Date;
updatedAt?: Date;
}

export type ExampleModel =Model>;

```

## Model

```typescript
import { Schema, model } from "mongoose";
import { IExample, ExampleModel } from "./example.interface";

const ExampleSchema = new Schema(
{
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
},
{
timestamps: true,
toJSON: {
virtuals: true,
},
}
);

export const Example = model("Example", ExampleSchema);

```

## Controller

```typescript
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(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(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(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(res, {
statusCode: httpStatus.OK,
success: true,
message: responseMessage.DELETE_EXAMPLE_MESSAGE,
data: result,
});
});

export const ExampleController = {
getAllExamples,
getExampleById,
updateExample,
deleteExample,
};
```

## Service

```typescript
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> => {
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 => {
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
): Promise => {
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 => {
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

```typescript
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

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

```