diff --git a/package.json b/package.json
index e99696f..e22ecf2 100644
--- a/package.json
+++ b/package.json
@@ -2,10 +2,10 @@
"name": "express-typescript-boilerplate",
"version": "1.0.14",
"description": "An Express boilerplate backend",
- "author": "Edwin Hernandez",
- "repository": "edwinhern/express-typescript-2024",
+ "author": "Alejandro Ruiz",
+ "repository": "alex-ruiz/express-typescript-2024",
"license": "MIT",
- "main": "index.ts",
+ "main": "src/index.ts",
"private": true,
"scripts": {
"dev": "tsx watch --clear-screen=false src/index.ts | pino-pretty",
diff --git a/src/api-docs/__tests__/openAPIRouter.test.ts b/src/api-docs/__tests__/openAPIRouter.test.ts
index 131b293..9b6dbc7 100644
--- a/src/api-docs/__tests__/openAPIRouter.test.ts
+++ b/src/api-docs/__tests__/openAPIRouter.test.ts
@@ -1,7 +1,7 @@
import { StatusCodes } from "http-status-codes";
import request from "supertest";
-import { app } from "@/server";
+import { app } from "@/bin/server";
import { generateOpenAPIDocument } from "../openAPIDocumentGenerator";
@@ -12,7 +12,7 @@ describe("OpenAPI Router", () => {
const expectedResponse = generateOpenAPIDocument();
// Act
- const response = await request(app).get("/swagger.json");
+ const response = await request(app).get("/swagger/swagger.json");
// Assert
expect(response.status).toBe(StatusCodes.OK);
@@ -22,7 +22,7 @@ describe("OpenAPI Router", () => {
it("should serve the Swagger UI", async () => {
// Act
- const response = await request(app).get("/");
+ const response = await request(app).get("/swagger/");
// Assert
expect(response.status).toBe(StatusCodes.OK);
diff --git a/src/api-docs/openAPIDocumentGenerator.ts b/src/api-docs/openAPIDocumentGenerator.ts
index 4c5abfb..727fda9 100644
--- a/src/api-docs/openAPIDocumentGenerator.ts
+++ b/src/api-docs/openAPIDocumentGenerator.ts
@@ -15,7 +15,7 @@ export function generateOpenAPIDocument() {
},
externalDocs: {
description: "View the raw OpenAPI Specification in JSON format",
- url: "/swagger.json",
+ url: "/swagger/swagger.json",
},
});
}
diff --git a/src/api/healthCheck/__tests__/healthCheckRouter.test.ts b/src/api/healthCheck/__tests__/healthCheckRouter.test.ts
index 791d65c..619033d 100644
--- a/src/api/healthCheck/__tests__/healthCheckRouter.test.ts
+++ b/src/api/healthCheck/__tests__/healthCheckRouter.test.ts
@@ -1,8 +1,8 @@
import { StatusCodes } from "http-status-codes";
import request from "supertest";
+import { app } from "@/bin/server";
import type { ServiceResponse } from "@/common/models/serviceResponse";
-import { app } from "@/server";
describe("Health Check API endpoints", () => {
it("GET / - success", async () => {
diff --git a/src/api/user/__tests__/userRouter.test.ts b/src/api/user/__tests__/userRouter.test.ts
index ce2f92a..00f06bb 100644
--- a/src/api/user/__tests__/userRouter.test.ts
+++ b/src/api/user/__tests__/userRouter.test.ts
@@ -3,8 +3,8 @@ import request from "supertest";
import type { User } from "@/api/user/userModel";
import { users } from "@/api/user/userRepository";
+import { app } from "@/bin/server";
import type { ServiceResponse } from "@/common/models/serviceResponse";
-import { app } from "@/server";
describe("User API Endpoints", () => {
describe("GET /users", () => {
diff --git a/src/api/user/userService.ts b/src/api/user/userService.ts
index dfb2491..22e88a0 100644
--- a/src/api/user/userService.ts
+++ b/src/api/user/userService.ts
@@ -2,8 +2,8 @@ import { StatusCodes } from "http-status-codes";
import type { User } from "@/api/user/userModel";
import { UserRepository } from "@/api/user/userRepository";
+import { logger } from "@/bin/server";
import { ServiceResponse } from "@/common/models/serviceResponse";
-import { logger } from "@/server";
export class UserService {
private userRepository: UserRepository;
diff --git a/src/api/welcome/__tests__/welcomeRouter.test.ts b/src/api/welcome/__tests__/welcomeRouter.test.ts
new file mode 100644
index 0000000..bec11d8
--- /dev/null
+++ b/src/api/welcome/__tests__/welcomeRouter.test.ts
@@ -0,0 +1,14 @@
+import { StatusCodes } from "http-status-codes";
+import request from "supertest";
+
+import { app } from "@/bin/server";
+
+describe("Welcome API endpoints", () => {
+ it("GET / - success", async () => {
+ const response = await request(app).get("/");
+ const result: string = response.text;
+
+ expect(response.statusCode).toEqual(StatusCodes.OK);
+ expect(result).toEqual("
Welcome to Express Typescript Api
");
+ });
+});
diff --git a/src/api/welcome/welcomeRouter.ts b/src/api/welcome/welcomeRouter.ts
new file mode 100644
index 0000000..17d0980
--- /dev/null
+++ b/src/api/welcome/welcomeRouter.ts
@@ -0,0 +1,22 @@
+import { OpenAPIRegistry } from "@asteasolutions/zod-to-openapi";
+import express, { type Request, type Response, type Router } from "express";
+import { z } from "zod";
+
+import { createApiResponse } from "@/api-docs/openAPIResponseBuilders";
+import { HtmlServiceResponse } from "@/common/models/htmlServiceResponse";
+import { handleHtmlServiceResponse } from "@/common/utils/httpHandlers";
+
+export const welcomeRegistry = new OpenAPIRegistry();
+export const welcomeRouter: Router = express.Router();
+
+welcomeRegistry.registerPath({
+ method: "get",
+ path: "/",
+ tags: ["Welcome"],
+ responses: createApiResponse(z.null(), "Success"),
+});
+
+welcomeRouter.get("/", (_req: Request, res: Response) => {
+ const htmlServiceResponse = HtmlServiceResponse.success("Welcome to Express Typescript Api
");
+ return handleHtmlServiceResponse(htmlServiceResponse, res);
+});
diff --git a/src/bin/logger.ts b/src/bin/logger.ts
new file mode 100644
index 0000000..116386e
--- /dev/null
+++ b/src/bin/logger.ts
@@ -0,0 +1,3 @@
+import { pino } from "pino";
+
+export const logger = pino({ name: "Server Start" });
diff --git a/src/bin/routes.ts b/src/bin/routes.ts
new file mode 100644
index 0000000..354d56e
--- /dev/null
+++ b/src/bin/routes.ts
@@ -0,0 +1,16 @@
+import { openAPIRouter } from "@/api-docs/openAPIRouter";
+import { healthCheckRouter } from "@/api/healthCheck/healthCheckRouter";
+import { userRouter } from "@/api/user/userRouter";
+import { welcomeRouter } from "@/api/welcome/welcomeRouter";
+import express, { type Router } from "express";
+
+export const router: Router = express.Router();
+
+// Home
+router.use("/", welcomeRouter);
+
+router.use("/health-check", healthCheckRouter);
+router.use("/users", userRouter);
+
+// Swagger UI
+router.use("/swagger", openAPIRouter);
diff --git a/src/server.ts b/src/bin/server.ts
similarity index 67%
rename from src/server.ts
rename to src/bin/server.ts
index b286b5b..8413418 100644
--- a/src/server.ts
+++ b/src/bin/server.ts
@@ -1,17 +1,14 @@
import cors from "cors";
import express, { type Express } from "express";
import helmet from "helmet";
-import { pino } from "pino";
-import { openAPIRouter } from "@/api-docs/openAPIRouter";
-import { healthCheckRouter } from "@/api/healthCheck/healthCheckRouter";
-import { userRouter } from "@/api/user/userRouter";
+import { logger } from "@/bin/logger";
+import { router as globalRouter } from "@/bin/routes";
import errorHandler from "@/common/middleware/errorHandler";
import rateLimiter from "@/common/middleware/rateLimiter";
import requestLogger from "@/common/middleware/requestLogger";
import { env } from "@/common/utils/envConfig";
-const logger = pino({ name: "server start" });
const app: Express = express();
// Set the application to trust the reverse proxy
@@ -28,11 +25,7 @@ app.use(rateLimiter);
app.use(requestLogger);
// Routes
-app.use("/health-check", healthCheckRouter);
-app.use("/users", userRouter);
-
-// Swagger UI
-app.use(openAPIRouter);
+app.use(globalRouter);
// Error handlers
app.use(errorHandler());
diff --git a/src/common/models/htmlServiceResponse.ts b/src/common/models/htmlServiceResponse.ts
new file mode 100644
index 0000000..191d419
--- /dev/null
+++ b/src/common/models/htmlServiceResponse.ts
@@ -0,0 +1,26 @@
+import { StatusCodes } from "http-status-codes";
+import { z } from "zod";
+
+export class HtmlServiceResponse {
+ readonly html: string;
+ readonly statusCode: number;
+
+ private constructor(html: string, statusCode: number) {
+ this.html = html;
+ this.statusCode = statusCode;
+ }
+
+ static success(html: string, statusCode: number = StatusCodes.OK) {
+ return new HtmlServiceResponse(html, statusCode);
+ }
+
+ static failure(html: string, statusCode: number = StatusCodes.BAD_REQUEST) {
+ return new HtmlServiceResponse(html, statusCode);
+ }
+}
+
+export const HtmlServiceResponseSchema = () =>
+ z.object({
+ html: z.string(),
+ statusCode: z.number(),
+ });
diff --git a/src/common/utils/httpHandlers.ts b/src/common/utils/httpHandlers.ts
index 1464f6b..ea6d627 100644
--- a/src/common/utils/httpHandlers.ts
+++ b/src/common/utils/httpHandlers.ts
@@ -2,12 +2,17 @@ import type { NextFunction, Request, Response } from "express";
import { StatusCodes } from "http-status-codes";
import type { ZodError, ZodSchema } from "zod";
+import type { HtmlServiceResponse } from "@/common/models/htmlServiceResponse";
import { ServiceResponse } from "@/common/models/serviceResponse";
export const handleServiceResponse = (serviceResponse: ServiceResponse, response: Response) => {
return response.status(serviceResponse.statusCode).send(serviceResponse);
};
+export const handleHtmlServiceResponse = (serviceResponse: HtmlServiceResponse, response: Response) => {
+ return response.status(serviceResponse.statusCode).send(serviceResponse.html);
+};
+
export const validateRequest = (schema: ZodSchema) => (req: Request, res: Response, next: NextFunction) => {
try {
schema.parse({ body: req.body, query: req.query, params: req.params });
diff --git a/src/index.ts b/src/index.ts
index f9a7d0d..567cb3a 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,5 @@
+import { app, logger } from "@/bin/server";
import { env } from "@/common/utils/envConfig";
-import { app, logger } from "@/server";
const server = app.listen(env.PORT, () => {
const { NODE_ENV, HOST, PORT } = env;