Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New ✨ : Express MongoDB TypeScript template (opinionated) #41

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions bin/configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,93 @@ export const templates = {
},
],
},
express_mongo_ts: {
name: "express_mongo_ts",
dependencies: [
{
"name": "@types/bcrypt",
"version": "^5.0.2"
},
{
"name": "@types/cookie-parser",
"version": "^1.4.7"
},
{
"name": "@types/cors",
"version": "^2.8.17"
},
{
"name": "@types/dotenv",
"version": "^8.2.3"
},
{
"name": "@types/express",
"version": "^5.0.0"
},
{
"name": "@types/mongoose",
"version": "^5.11.97"
},
{
"name": "@types/morgan",
"version": "^1.9.9"
},
{
"name": "@types/node",
"version": "^22.9.0"
},
{
"name": "bcrypt",
"version": "^5.1.1"
},
{
"name": "cookie-parser",
"version": "^1.4.7"
},
{
"name": "cors",
"version": "^2.8.5"
},
{
"name": "dotenv",
"version": "^16.4.5"
},
{
"name": "env-var",
"version": "^7.5.0"
},
{
"name": "express",
"version": "^4.21.1"
},
{
"name": "helmet",
"version": "^8.0.0"
},
{
"name": "http-errors",
"version": "^2.0.0"
},
{
"name": "mongoose",
"version": "^8.8.1"
},
{
"name": "morgan",
"version": "^1.10.0"
},
{
"name": "nodemon",
"version": "^3.1.7"
},
{
"name": "ts-node",
"version": "^10.9.2"
},
{
"name": "typescript",
"version": "^5.6.3"
}
]
},
};
5 changes: 5 additions & 0 deletions templates/express_mongo_ts/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DB_URL="mongodb://localhost:27017/mydb"
HOST="http://localhost"
PORT=8000

# LOG_LEVEL="tiny"
3 changes: 3 additions & 0 deletions templates/express_mongo_ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
.env
21 changes: 21 additions & 0 deletions templates/express_mongo_ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Express Mongo TypeScript Starter Kit

### Get Started
1. Install required dependencies
```js
npm install && npm run dev
```
2. Create copy of `.env.example` file and name it `.env`:
```js
cp .env.example .env
```
3. Configure the environment variables in the `.env` file according to your
project requirements. This file stores sensitive information like database
credentials and API keys. Make sure to keep it safe and not commit it.

4. Start development server
```js
npm run dev
```

Your Express server should be up and running!
8 changes: 8 additions & 0 deletions templates/express_mongo_ts/nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"watch": [
"src"
],
"ext": ".ts,.js",
"ignore": [],
"exec": "npx ts-node ./src/index.ts"
}
36 changes: 36 additions & 0 deletions templates/express_mongo_ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "express_mongo_ts",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "npm run build && node dist/index.js",
"dev": "npx nodemon"
},
"keywords": [],
"author": "Ritesh Koushik",
"license": "MIT",
"description": "TypeScript variant of Express Mongo starter template",
"dependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/dotenv": "^8.2.3",
"@types/express": "^5.0.0",
"@types/mongoose": "^5.11.97",
"@types/morgan": "^1.9.9",
"@types/node": "^22.9.0",
"bcrypt": "^5.1.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"env-var": "^7.5.0",
"express": "^4.21.1",
"helmet": "^8.0.0",
"http-errors": "^2.0.0",
"mongoose": "^8.8.1",
"morgan": "^1.10.0",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
"typescript": "^5.6.3"
}
}
3 changes: 3 additions & 0 deletions templates/express_mongo_ts/src/configs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * as server from "./serverConfig"

// Add other configs and export them from configs/index.ts
10 changes: 10 additions & 0 deletions templates/express_mongo_ts/src/configs/serverConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import dotenv from "dotenv";
import type { TServerConfig } from "../types";

dotenv.config();

export const config: TServerConfig = {
dbUrl: process.env.DB_URL || "mongodb://localhost:27017/mydb",
host: process.env.HOST || "http://localhost",
port: parseInt(process.env.PORT!) || 8000,
}
36 changes: 36 additions & 0 deletions templates/express_mongo_ts/src/controllers/userController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import bcrypt from 'bcrypt';
import { User } from '../models/userModel';
import type { Request, Response } from 'express';

// Sample endpoint
export const signup = async (req: Request, res: Response) => {
try {
const { body } = req;
const user_username = await User.findOne({ username: body.username });
const user_email = await User.findOne({ email: body.email });

if (user_email || user_username) {
let message = `${user_username ? 'Username' : 'Email'} already exists`;
if (user_username && user_email) {
message = 'Username and email already exist';
}
return res.status(400).json({ error: true, message });
}

const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(body.password, salt);
const new_user = await User.create({ ...body, password: hashedPassword });

return res.status(201).json({
error: false, message: 'User created successfully', user: {
username: new_user.username,
email: new_user.email,
roles: new_user.roles,
_id: new_user._id,
}
});
} catch (error) {
console.log(error);
res.status(500).json({ error: true, message: 'Internal Server Error' });
}
};
6 changes: 6 additions & 0 deletions templates/express_mongo_ts/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { server } from "./configs";
import { InitServer } from "./server";

const app = new InitServer();
app.setup(server.configs);
app.start();
38 changes: 38 additions & 0 deletions templates/express_mongo_ts/src/models/userModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Schema, model } from 'mongoose';

interface IUser {
username: string;
password: string;
email: string;
roles: Array<string>;
words: Array<any>;
}

const UserSchema = new Schema<IUser>({
username: {
type: String,
required: true,
unique: true,
trim: true,
},
password: {
type: String,
trim: true,
},
email: {
type: String,
required: true,
unique: true,
trim: true,
},
roles: {
type: [String],
enum: ['user', 'admin', 'superAdmin', 'sysAdmin'],
default: ['user'],
},
}, {
timestamps: true,
});

export const User = model<IUser>('User', UserSchema);

14 changes: 14 additions & 0 deletions templates/express_mongo_ts/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Router, Request, Response } from "express";
import { user } from "./userRoutes";

const router = Router();

router.get("/", (req: Request, res: Response) => {
res.status(200).json({
message: "Welcome to Express, MongoDB and TypeScript server",
});
});

router.use("/api/user", user);

export default router;
Empty file.
56 changes: 56 additions & 0 deletions templates/express_mongo_ts/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import express, { Express, NextFunction, Request, Response } from "express";
import cookieParser from 'cookie-parser';
import cors from 'cors';
import createError from 'http-errors';
import helmet from 'helmet';
import mongoose from 'mongoose';
import morgan from 'morgan';
import routes from './routes';
import type { TServerConfig } from './types';

export class InitServer {
server: Express;
db: typeof mongoose;

constructor() {
this.server = express();
this.db = mongoose;
}

setup(config: TServerConfig) {

// Setup server configuration
this.server.set('host', config.host);
this.server.set('port', config.port);
this.server.set('dbUrl', config.dbUrl)

// Setup middlewares
this.server.use(cors());
this.server.use(helmet());
this.server.use(morgan('tiny')); // HTTP request logger to stdout
this.server.use(cookieParser());
this.server.use(express.json());
this.server.use(express.urlencoded({ extended: false }));

// Setup routes
this.server.use("/", routes);

// Return 404 if requested route is undefined
this.server.use((req: Request, res: Response, next: NextFunction) => {
next(createError(404));
});
}

async start() {
const host = this.server.get('host');
const port = this.server.get('port');

try {
await this.db.connect(process.env.DB_URL!);
this.server.listen(port, () => console.log(`[server]: Server is running at ${host}:${port}`));
} catch (error) {
console.error(error);
process.exit(1);
}
}
}
15 changes: 15 additions & 0 deletions templates/express_mongo_ts/src/types/configType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type TServerConfig = {
dbUrl: string,
host: string,
// logLevel: string,
port: number,
}

export type TTokenConfig = {
// algorithm: string | jwt.Algorithm | others?
hash_salt: number,
refreshTokenExpirationTime: string,
refreshTokenSecretKey: string,
accessTokenExpirationTime: string,
accessTokenSecretKey: string,
}
4 changes: 4 additions & 0 deletions templates/express_mongo_ts/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './configType';
export * from './userType';

// Export all types from a single location across the entire application
10 changes: 10 additions & 0 deletions templates/express_mongo_ts/src/types/userType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type TUser = {
username: string,
password?: string,
email: string,
roles: Array<string>,
_id: string,
createdAt?: string,
updatedAt?: string,
__v?: number,
};
Loading