Skip to content

Commit

Permalink
Template Added
Browse files Browse the repository at this point in the history
  • Loading branch information
rifatsaown committed Apr 7, 2024
1 parent 252a138 commit 74f23ee
Show file tree
Hide file tree
Showing 34 changed files with 3,397 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
logs
yarn-error.log
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
node_modules
.env
23 changes: 23 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "unused-imports"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
"unused-imports/no-unused-imports": "error",
"@typescript-eslint/no-explicit-any": "off"
},
"env": {
"browser": true,
"es2021": true
}
}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
.env.local
node_modules
logs
yarn-error.log
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn lint-staged
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"arrowParens": "avoid"
}
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use a Node.js image
FROM node:18.18.0-alpine

# Set the working directory
WORKDIR /app

# Copy all the application files
COPY . .

# Install dependencies
RUN yarn install

# Build the application
RUN yarn build

# Expose the port if required
EXPOSE 5000

# Command to run the application
CMD ["yarn", "start"]
Binary file added bun.lockb
Binary file not shown.
55 changes: 55 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "express-ts-boilerplate",
"version": "1.0.0",
"main": "index.js",
"repository": "",
"author": "",
"license": "MIT",
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only ./src/index.ts",
"start": "node ./dist/index.js",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"prettier": "prettier --write \"src/**/*.ts\"",
"format": "yarn prettier && yarn lint:fix",
"pre": "git pull && yarn && yarn dev",
"build": "rm -rf dist && yarn tsc",
"tsc": "tsc"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"yarn lint:fix",
"yarn prettier"
]
},
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"ejs": "^3.1.9",
"eslint": "^9.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-unused-imports": "^3.1.0",
"express": "^4.19.2",
"http-status": "^1.7.4",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"mongoose": "^8.3.0",
"pino": "^8.20.0",
"pino-pretty": "^11.0.0",
"prettier": "^3.2.5",
"ts-node-dev": "^2.0.0",
"typescript": "^5.4.4",
"winston": "^3.13.0",
"winston-daily-rotate-file": "^5.0.0"
},
"devDependencies": {
"@types/body-parser": "^1.19.5",
"@types/cors": "^2.8.13",
"@types/ejs": "^3.1.4",
"@types/express": "^4.17.17",
"@typescript-eslint/eslint-plugin": "^7.5.0",
"@typescript-eslint/parser": "^7.5.0",
"ts-node": "^10.9.1"
}
}
38 changes: 38 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import cors from 'cors'
import 'dotenv/config'
import express, { Application, NextFunction, Request, Response } from 'express'
import * as path from 'path'
import routes from './app/routes'

// import routes here
import globalErrorHandler from './errors/globalErrorHandler'
import { dbConnect } from './utils/dbConnect'
const app: Application = express()

app.use(cors())

// Set EJS as the view engine
app.set('view engine', 'ejs')

// Set the path to the views directory
app.set('views', path.join(__dirname, '../views'))

//parser
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// Database connection
dbConnect()

// Application routes
app.use('/', routes)

//Welcome route
app.get('/', async (req: Request, res: Response, next: NextFunction) => {
res.render('welcome')
})

// Error handling
app.use(globalErrorHandler)

export { app }
Binary file added src/app/modules/.DS_Store
Binary file not shown.
25 changes: 25 additions & 0 deletions src/app/modules/States/state.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NextFunction, Request, Response } from 'express'

import sendResponse from '../../../utils/responseHandler'
import { stateServices } from './state.service'

const getAllStates = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const result = await stateServices.getStates()
// console.log(result)
sendResponse(res, {
statusCode: 200,
success: true,
message: 'Successfully fetched all states',
data: result,
})
} catch (error) {
next(error)
}
}

export const statesControllers = { getAllStates }
8 changes: 8 additions & 0 deletions src/app/modules/States/state.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// State.ts
import { Document } from 'mongoose'

export type State = {
stateName: string
abbreviation: string
capital: string
} & Document
20 changes: 20 additions & 0 deletions src/app/modules/States/state.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// main.ts
import mongoose, { Document, Model, Schema } from 'mongoose'
import { State } from './state.interface'

const objectId = mongoose.Schema.Types.ObjectId

const stateSchema = new Schema<State>({
stateName: {
type: String,
},
abbreviation: {
type: String,
},
capital: {
type: String,
},
})

const States: Model<Document & State> = mongoose.model('State', stateSchema)
export default States
8 changes: 8 additions & 0 deletions src/app/modules/States/state.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express, { Router } from 'express'
import { statesControllers } from './state.controller'

const router: Router = express.Router()

router.get('/', statesControllers.getAllStates)

export const statesRoutes = router
9 changes: 9 additions & 0 deletions src/app/modules/States/state.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { State } from './state.interface'
import States from './state.model'

const getStates = async (): Promise<State[]> => {
const result: State[] = await States.find({})
return result
}

export const stateServices = { getStates }
9 changes: 9 additions & 0 deletions src/app/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express'
import { statesRoutes } from './../modules/States/state.routes'

const router = express.Router()

const moduleRoutes = [{ path: '/states', route: statesRoutes }]

moduleRoutes.forEach(route => router.use(route.path, route.route))
export default router
5 changes: 5 additions & 0 deletions src/configs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
port: process.env.PORT,
database_url: process.env.MONGO_URI,
env: process.env.NODE_ENV,
}
14 changes: 14 additions & 0 deletions src/errors/ApiErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class ApiError extends Error {
statusCode: number
constructor(statusCode: number, message: string | undefined, stack = '') {
super(message)
this.statusCode = statusCode
if (stack) {
this.stack = stack
} else {
Error.captureStackTrace(this, this.constructor)
}
}
}

export { ApiError }
65 changes: 65 additions & 0 deletions src/errors/globalErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ErrorRequestHandler } from 'express'
import configs from '../configs'
import { IErrorSources } from '../interfaces/error'
import { ApiError } from './ApiErrors'
import handleCastError from './handleCastError'
import handleDuplicateError from './handleDuplicateError'
import handleValidationError from './handlevadiationErrors'

const globalErrorHandler: ErrorRequestHandler = (err, req, res, next) => {
if (configs.env === 'development') console.log(err) //logging the error in development mode only
//setting default values
let statusCode = 500
let message = 'Something went wrong!'
let errorSources: IErrorSources = [
{
path: '',
message: 'Something went wrong',
},
]

if (err?.name === 'ValidationError') {
const simplifiedError = handleValidationError(err)
statusCode = simplifiedError?.statusCode
message = simplifiedError?.message
errorSources = simplifiedError?.errorSources
} else if (err?.name === 'CastError') {
const simplifiedError = handleCastError(err)
statusCode = simplifiedError?.statusCode
message = simplifiedError?.message
errorSources = simplifiedError?.errorSources
} else if (err?.code === 11000) {
const simplifiedError = handleDuplicateError(err)
statusCode = simplifiedError?.statusCode
message = simplifiedError?.message
errorSources = simplifiedError?.errorSources
} else if (err instanceof ApiError) {
statusCode = err?.statusCode
message = err.message
errorSources = [
{
path: '',
message: err?.message,
},
]
} else if (err instanceof Error) {
message = err.message
errorSources = [
{
path: '',
message: err?.message,
},
]
}

//ultimate return
return res.status(statusCode).json({
success: false,
message,
errorSources,
err,
stack: configs.env === 'development' ? err?.stack : null,
})
}

export default globalErrorHandler
24 changes: 24 additions & 0 deletions src/errors/handleCastError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import mongoose from 'mongoose'
import { IErrorSources, IGenericErrorResponse } from '../interfaces/error'

//CastError is thrown when a value is passed to mongoose that is not a valid type for the Schema type it is defined in. For example, passing a string to a Number type field. This error can be caught and handled by the handleCastError function.
const handleCastError = (
err: mongoose.Error.CastError
): IGenericErrorResponse => {
const errorSources: IErrorSources = [
{
path: err.path,
message: err.message,
},
]

const statusCode = 400

return {
statusCode,
message: 'Invalid ID',
errorSources,
}
}

export default handleCastError
28 changes: 28 additions & 0 deletions src/errors/handleDuplicateError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import { IErrorSources, IGenericErrorResponse } from '../interfaces/error'

const handleDuplicateError = (err: any): IGenericErrorResponse => {
// Extract value within double quotes using regex
const match = err.message.match(/"([^"]*)"/)

// The extracted value will be in the first capturing group
const extractedMessage = match && match[1]

const errorSources: IErrorSources = [
{
path: '',
message: `${extractedMessage} is already exists`,
},
]

const statusCode = 400

return {
statusCode,
message: 'Invalid ID',
errorSources,
}
}

export default handleDuplicateError
Loading

0 comments on commit 74f23ee

Please sign in to comment.