forked from swapnil.bendal/TypeScript-Backend-Template
[added] - new routes
This commit is contained in:
@@ -6,4 +6,9 @@ import tseslint from 'typescript-eslint';
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -4,9 +4,9 @@ import config from "./config/config";
|
||||
import morgan from "./config/morgan";
|
||||
import path from 'path';
|
||||
import logger from './config/logger';
|
||||
import ApiError from './utils/helper/ApiError';
|
||||
import error from './middleware/error';
|
||||
import routes from './routes';
|
||||
import ApiError from './utils/helper/ApiError';
|
||||
|
||||
class App {
|
||||
private app: Application;
|
||||
@@ -38,8 +38,12 @@ class App {
|
||||
}
|
||||
|
||||
private initializeErrorHandling(): void {
|
||||
this.app.use(error.errorConverter);
|
||||
this.app.use(error.errorHandler);
|
||||
this.app.use(error.errorConverter);
|
||||
this.app.use((req, res, next) => {
|
||||
console.log('Request passed through middleware.');
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
public listen(port: number): ReturnType<typeof this.app.listen> {
|
||||
|
||||
@@ -35,6 +35,16 @@ export class ProductController {
|
||||
res.status(200).json(new ApiResponse(200, data, 'Products retrieved successfully'));
|
||||
}
|
||||
|
||||
@AsyncHandler()
|
||||
async onGetByIDProducts(req: Request, res: Response) {
|
||||
const data = await this.interactor.getByIdProduct(Number(req.params.id));
|
||||
if (!data) {
|
||||
res.status(200).json(new ApiResponse(200, null, 'No products found'));
|
||||
return;
|
||||
}
|
||||
res.status(200).json(new ApiResponse(200, data, 'Products retrieved successfully'));
|
||||
}
|
||||
|
||||
@AsyncHandler()
|
||||
async onUpdateStock(req: Request, res: Response) {
|
||||
const data = await this.interactor.updateStock(parseInt(`${req.params.id}`, 10), parseInt(`${req.body.stock}`, 10));
|
||||
|
||||
@@ -38,5 +38,8 @@ export class ProductInteractor implements IProductInteractor {
|
||||
async getProducts(limit: number, offset: number): Promise<Product[]> {
|
||||
return await this.repository.find(limit, offset)
|
||||
}
|
||||
async getByIdProduct(id: number): Promise<Product> {
|
||||
return await this.repository.findById(id)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Product } from "../entities/Product";
|
||||
|
||||
export interface IProductInteractor {
|
||||
createProduct(input: Partial<Product>): Promise<Product>;
|
||||
updateStock(id: number, stock: number): Promise<Product>;
|
||||
getProducts(limit: number, offset: number): Promise<Product[]>;
|
||||
}
|
||||
createProduct(input: Partial<Product>): Promise<Product>;
|
||||
updateStock(id: number, stock: number): Promise<Product>;
|
||||
getProducts(limit: number, offset: number): Promise<Product[]>;
|
||||
getByIdProduct(id: number): Promise<Product>;
|
||||
}
|
||||
@@ -4,4 +4,5 @@ export interface IProductRepository {
|
||||
create(data: Product): Promise<Product>;
|
||||
update(id: number, data: Partial<Product>): Promise<Product>;
|
||||
find(limit: number, offset: number): Promise<Product[]>;
|
||||
findById(id: number): Promise<Product>;
|
||||
}
|
||||
@@ -40,36 +40,44 @@ class error {
|
||||
static errorHandler(
|
||||
err: ApiError,
|
||||
req: Request,
|
||||
res: Response
|
||||
res: Response,
|
||||
_next: NextFunction // Retain this even if unused
|
||||
): void {
|
||||
let { statusCode, message } = err;
|
||||
|
||||
// Production environment: Ensure only operational errors are shown
|
||||
if (config.env === 'production' && !err.isOperational) {
|
||||
// Extract error details with fallback defaults
|
||||
let { statusCode = 500, message = "Internal server error" } = err;
|
||||
|
||||
// Production: Ensure only operational errors reveal their messages
|
||||
if (config.env === "production" && !err.isOperational) {
|
||||
statusCode = 500;
|
||||
message = "Internal server error";
|
||||
message = "An unexpected error occurred";
|
||||
}
|
||||
|
||||
|
||||
// Attach error message to response locals for potential templating
|
||||
res.locals.errorMessage = err.message;
|
||||
|
||||
// Response structure
|
||||
|
||||
// Construct the response payload
|
||||
const response = {
|
||||
code: statusCode,
|
||||
message,
|
||||
...(config.env === 'development' && { stack: err.stack })
|
||||
...(config.env === "development" && err.stack && { stack: err.stack }), // Include stack trace only in development
|
||||
...(err.errors && { errors: err.errors.map((e: Error) => e.message) }), // Include nested validation errors if any
|
||||
};
|
||||
|
||||
// Log error in development
|
||||
if (config.env === 'development') {
|
||||
logger.error(err);
|
||||
}
|
||||
|
||||
// Ensure the response is sent
|
||||
res.status(statusCode).send(response);
|
||||
|
||||
// Don't call next() unless you want to propagate the error further
|
||||
// If this is the final middleware, remove next()
|
||||
|
||||
// Log the error in all environments
|
||||
logger.error({
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
statusCode,
|
||||
errors: err.errors || [],
|
||||
});
|
||||
|
||||
// Send the response
|
||||
res.status(statusCode).json(response);
|
||||
|
||||
// Do not call `next()` here, as this is the final error handler.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default error;
|
||||
|
||||
@@ -40,4 +40,8 @@ export class ProductRepository implements IProductRepository {
|
||||
order: { id: "ASC" }, // Optional: Add sorting for consistent results
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id: number): Promise<Product> {
|
||||
return await this.productRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,5 +36,6 @@ const productController = container.get<ProductController>(INTERFACE_TYPE.Produc
|
||||
router.post('/', productController.onCreateProduct.bind(productController));
|
||||
router.get('/', productController.onGetProducts.bind(productController));
|
||||
router.patch('/:id', productController.onUpdateStock.bind(productController));
|
||||
router.get('/:id', productController.onGetByIDProducts.bind(productController));
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user