@akash-electron/ts-backend 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +35 -0
- package/dist/config/env.d.ts +5 -0
- package/dist/config/env.js +22 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -0
- package/dist/middlewares/errorMiddleware.d.ts +2 -0
- package/dist/middlewares/errorMiddleware.js +31 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +25 -0
- package/dist/utils/AppError.d.ts +6 -0
- package/dist/utils/AppError.js +13 -0
- package/dist/utils/catchAsync.d.ts +2 -0
- package/dist/utils/catchAsync.js +9 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
## 📦 Installation
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
npm install @akash-electron/ts-backend
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
## 🚀 Usage
|
|
8
|
+
|
|
9
|
+
This package provides a set of utilities and middlewares to build robust TypeScript backends.
|
|
10
|
+
|
|
11
|
+
### Global Error Handling
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import express from "express";
|
|
15
|
+
import { globalErrorHandler, AppError, catchAsync } from "backlinks_backend";
|
|
16
|
+
|
|
17
|
+
const app = express();
|
|
18
|
+
|
|
19
|
+
// Your routes
|
|
20
|
+
app.get(
|
|
21
|
+
"/example",
|
|
22
|
+
catchAsync(async (req, res) => {
|
|
23
|
+
if (!req.query.id) {
|
|
24
|
+
throw new AppError("ID is required", 400);
|
|
25
|
+
}
|
|
26
|
+
res.json({ id: req.query.id });
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Use the global error handler at the end
|
|
31
|
+
app.use(globalErrorHandler);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 📂 Folder Structure (Inherited)
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
src/
|
|
40
|
+
├── config/ # Configuration files (env vars, database, etc.)
|
|
41
|
+
├── controllers/ # Request handlers (processes input/output)
|
|
42
|
+
├── middlewares/ # Custom Express middlewares (error, auth, validation)
|
|
43
|
+
├── models/ # Data models/schemas (Prisma/Mongoose)
|
|
44
|
+
├── routes/ # API route definitions
|
|
45
|
+
├── services/ # Business logic (kept separate from controllers)
|
|
46
|
+
├── utils/ # Helper functions and utility classes
|
|
47
|
+
├── validations/ # Request validation schemas (Zod)
|
|
48
|
+
├── types/ # Global TypeScript interfaces & types
|
|
49
|
+
├── app.ts # Express application configuration
|
|
50
|
+
└── server.ts # Entry point and server initialization
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🛠️ Key Components & Implementation
|
|
56
|
+
|
|
57
|
+
### 1. Global Error Handling
|
|
58
|
+
|
|
59
|
+
We use a centralized error handling mechanism to ensure consistent error responses across the entire application.
|
|
60
|
+
|
|
61
|
+
- **`AppError` Class**: A custom error class to handle operational errors.
|
|
62
|
+
- **`catchAsync` Utility**: A wrapper to eliminate the need for `try-catch` blocks in controllers.
|
|
63
|
+
- **Global Error Middleware**: The final destination for all errors.
|
|
64
|
+
|
|
65
|
+
### 2. Request Validation
|
|
66
|
+
|
|
67
|
+
Using **Zod** for schema validation ensures that only valid data reaches your business logic. We validate:
|
|
68
|
+
|
|
69
|
+
- Request Body
|
|
70
|
+
- Query Parameters
|
|
71
|
+
- URL Parameters
|
|
72
|
+
|
|
73
|
+
### 3. Business Logic (Services)
|
|
74
|
+
|
|
75
|
+
Controllers should only handle requests and responses. All "heavy lifting" and business rules are encapsulated in **Services**, making the code testable and reusable.
|
|
76
|
+
|
|
77
|
+
### 4. Consistent API Responses
|
|
78
|
+
|
|
79
|
+
All successful responses follow a standard format:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"status": "success",
|
|
84
|
+
"data": { ... }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🚀 Getting Started
|
|
91
|
+
|
|
92
|
+
### Prerequisites
|
|
93
|
+
|
|
94
|
+
- Node.js (v18+)
|
|
95
|
+
- npm or pnpm
|
|
96
|
+
|
|
97
|
+
### Initial Setup
|
|
98
|
+
|
|
99
|
+
1. **Initialize Project**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm init -y
|
|
103
|
+
npm install typescript ts-node nodemon @types/node @types/express express dotenv
|
|
104
|
+
npx tsc --init
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
2. **Install Essentials**
|
|
108
|
+
```bash
|
|
109
|
+
npm install zod cors helmet morgan winston
|
|
110
|
+
npm install -D @types/cors @types/morgan
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🛡️ Important Files (Quick Reference)
|
|
116
|
+
|
|
117
|
+
### `src/utils/AppError.ts`
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
export class AppError extends Error {
|
|
121
|
+
public readonly statusCode: number;
|
|
122
|
+
public readonly isOperational: boolean;
|
|
123
|
+
|
|
124
|
+
constructor(message: string, statusCode: number) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.statusCode = statusCode;
|
|
127
|
+
this.isOperational = true;
|
|
128
|
+
Error.captureStackTrace(this, this.constructor);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `src/middlewares/errorMiddleware.ts`
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { Request, Response, NextFunction } from "express";
|
|
137
|
+
import { AppError } from "../utils/AppError";
|
|
138
|
+
|
|
139
|
+
export const globalErrorHandler = (
|
|
140
|
+
err: any,
|
|
141
|
+
req: Request,
|
|
142
|
+
res: Response,
|
|
143
|
+
next: NextFunction,
|
|
144
|
+
) => {
|
|
145
|
+
err.statusCode = err.statusCode || 500;
|
|
146
|
+
err.status = err.status || "error";
|
|
147
|
+
|
|
148
|
+
res.status(err.statusCode).json({
|
|
149
|
+
status: err.status,
|
|
150
|
+
message: err.message,
|
|
151
|
+
...(process.env.NODE_ENV === "development" && { stack: err.stack }),
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `src/utils/catchAsync.ts`
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { Request, Response, NextFunction } from "express";
|
|
160
|
+
|
|
161
|
+
export const catchAsync = (fn: Function) => {
|
|
162
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
163
|
+
fn(req, res, next).catch(next);
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 📝 Best Practices Included
|
|
171
|
+
|
|
172
|
+
- **Environment Safety**: Validate `.env` variables at startup.
|
|
173
|
+
- **Security Check**: Pre-configured with `helmet` for secure headers.
|
|
174
|
+
- **Clean Code**: Deep separation of concerns (Routes → Controllers → Services).
|
|
175
|
+
- **Graceful Shutdown**: Handles `SIGTERM` and `SIGINT` signals to close DB connections properly.
|
package/dist/app.d.ts
ADDED
package/dist/app.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const cors_1 = __importDefault(require("cors"));
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const helmet_1 = __importDefault(require("helmet"));
|
|
9
|
+
const morgan_1 = __importDefault(require("morgan"));
|
|
10
|
+
const errorMiddleware_1 = require("./middlewares/errorMiddleware");
|
|
11
|
+
const AppError_1 = require("./utils/AppError");
|
|
12
|
+
const app = (0, express_1.default)();
|
|
13
|
+
// Security HTTP headers
|
|
14
|
+
app.use((0, helmet_1.default)());
|
|
15
|
+
// Development logging
|
|
16
|
+
if (process.env.NODE_ENV === "development") {
|
|
17
|
+
app.use((0, morgan_1.default)("dev"));
|
|
18
|
+
}
|
|
19
|
+
// Body parser
|
|
20
|
+
app.use(express_1.default.json({ limit: "10kb" }));
|
|
21
|
+
// CORS
|
|
22
|
+
app.use((0, cors_1.default)());
|
|
23
|
+
// Routes
|
|
24
|
+
// app.use('/api/v1/users', userRouter);
|
|
25
|
+
// Health check
|
|
26
|
+
app.get("/health", (req, res) => {
|
|
27
|
+
res.status(200).json({ status: "ok" });
|
|
28
|
+
});
|
|
29
|
+
// Handle undefined routes
|
|
30
|
+
app.all("*", (req, res, next) => {
|
|
31
|
+
next(new AppError_1.AppError(`Can't find ${req.originalUrl} on this server!`, 404));
|
|
32
|
+
});
|
|
33
|
+
// Global error handling middleware
|
|
34
|
+
app.use(errorMiddleware_1.globalErrorHandler);
|
|
35
|
+
exports.default = app;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.env = void 0;
|
|
7
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
dotenv_1.default.config();
|
|
10
|
+
const envSchema = zod_1.z.object({
|
|
11
|
+
NODE_ENV: zod_1.z
|
|
12
|
+
.enum(["development", "test", "production"])
|
|
13
|
+
.default("development"),
|
|
14
|
+
PORT: zod_1.z.coerce.number().default(5000),
|
|
15
|
+
DATABASE_URL: zod_1.z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
const envVars = envSchema.safeParse(process.env);
|
|
18
|
+
if (!envVars.success) {
|
|
19
|
+
console.error("❌ Invalid environment variables:", envVars.error.format());
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
exports.env = envVars.data;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config/env"), exports);
|
|
18
|
+
__exportStar(require("./middlewares/errorMiddleware"), exports);
|
|
19
|
+
__exportStar(require("./utils/AppError"), exports);
|
|
20
|
+
__exportStar(require("./utils/catchAsync"), exports);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.globalErrorHandler = void 0;
|
|
4
|
+
const globalErrorHandler = (err, req, res, next) => {
|
|
5
|
+
err.statusCode = err.statusCode || 500;
|
|
6
|
+
err.status = err.status || "error";
|
|
7
|
+
if (process.env.NODE_ENV === "development") {
|
|
8
|
+
res.status(err.statusCode).json({
|
|
9
|
+
status: err.status,
|
|
10
|
+
error: err,
|
|
11
|
+
message: err.message,
|
|
12
|
+
stack: err.stack,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
if (err.isOperational) {
|
|
17
|
+
res.status(err.statusCode).json({
|
|
18
|
+
status: err.status,
|
|
19
|
+
message: err.message,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.error("ERROR 💥", err);
|
|
24
|
+
res.status(500).json({
|
|
25
|
+
status: "error",
|
|
26
|
+
message: "Something went very wrong!",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
exports.globalErrorHandler = globalErrorHandler;
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const app_1 = __importDefault(require("./app"));
|
|
7
|
+
const env_1 = require("./config/env");
|
|
8
|
+
const port = env_1.env.PORT || 5000;
|
|
9
|
+
const server = app_1.default.listen(port, () => {
|
|
10
|
+
console.log(`🚀 Server running in ${env_1.env.NODE_ENV} mode on port ${port}`);
|
|
11
|
+
});
|
|
12
|
+
// Handle unhandled rejections
|
|
13
|
+
process.on("unhandledRejection", (err) => {
|
|
14
|
+
console.log("UNHANDLED REJECTION! 💥 Shutting down...");
|
|
15
|
+
console.log(err.name, err.message);
|
|
16
|
+
server.close(() => {
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
// Handle uncaught exceptions
|
|
21
|
+
process.on("uncaughtException", (err) => {
|
|
22
|
+
console.log("UNCAUGHT EXCEPTION! 💥 Shutting down...");
|
|
23
|
+
console.log(err.name, err.message);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppError = void 0;
|
|
4
|
+
class AppError extends Error {
|
|
5
|
+
constructor(message, statusCode) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
|
|
9
|
+
this.isOperational = true;
|
|
10
|
+
Error.captureStackTrace(this, this.constructor);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.AppError = AppError;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akash-electron/ts-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A robust, scalable, and production-ready starter template for building Node.js backends with TypeScript. This boilerplate follows industry best practices for architecture, error handling, and type safety.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "nodemon --exec ts-node src/server.ts",
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/server.js",
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@types/cors": "^2.8.19",
|
|
24
|
+
"@types/express": "^5.0.6",
|
|
25
|
+
"@types/morgan": "^1.9.10",
|
|
26
|
+
"@types/node": "^25.2.2",
|
|
27
|
+
"cors": "^2.8.6",
|
|
28
|
+
"dotenv": "^17.2.4",
|
|
29
|
+
"express": "^5.2.1",
|
|
30
|
+
"helmet": "^8.1.0",
|
|
31
|
+
"morgan": "^1.10.1",
|
|
32
|
+
"nodemon": "^3.1.11",
|
|
33
|
+
"ts-node": "^10.9.2",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"winston": "^3.19.0",
|
|
36
|
+
"zod": "^4.3.6"
|
|
37
|
+
}
|
|
38
|
+
}
|