Skip to content

Commit fbb8b87

Browse files
committed
feat: with swagger docs
1 parent 365185e commit fbb8b87

File tree

8 files changed

+348
-1
lines changed

8 files changed

+348
-1
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"pino-pretty": "^7.1.0",
2323
"prom-client": "^14.0.0",
2424
"response-time": "^2.3.2",
25+
"swagger-jsdoc": "^6.1.0",
26+
"swagger-ui-express": "^4.1.6",
2527
"zod": "^3.9.8"
2628
},
2729
"devDependencies": {
@@ -36,6 +38,8 @@
3638
"@types/node": "^16.11.1",
3739
"@types/pino": "^6.3.11",
3840
"@types/response-time": "^2.3.5",
41+
"@types/swagger-jsdoc": "^6.0.1",
42+
"@types/swagger-ui-express": "^4.1.3",
3943
"ts-node-dev": "^1.1.8",
4044
"typescript": "^4.4.4"
4145
}

src/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import logger from "./utils/logger";
66
import routes from "./routes";
77
import deserializeUser from "./middleware/deserializeUser";
88
import { restResponseTimeHistogram, startMetricsServer } from "./utils/metrics";
9+
import swaggerDocs from "./utils/swagger";
910

1011
const port = config.get<number>("port");
1112

@@ -38,4 +39,6 @@ app.listen(port, async () => {
3839
routes(app);
3940

4041
startMetricsServer();
42+
43+
swaggerDocs(app, port);
4144
});

src/routes.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,44 @@ import { createSessionSchema } from "./schema/session.schema";
2323
import { createUserSchema } from "./schema/user.schema";
2424

2525
function routes(app: Express) {
26+
/**
27+
* @openapi
28+
* /healthcheck:
29+
* get:
30+
* tags:
31+
* - Healthcheck
32+
* description: Responds if the app is up and running
33+
* responses:
34+
* 200:
35+
* description: App is up and running
36+
*/
2637
app.get("/healthcheck", (req: Request, res: Response) => res.sendStatus(200));
2738

39+
/**
40+
* @openapi
41+
* '/api/users':
42+
* post:
43+
* tags:
44+
* - User
45+
* summary: Register a user
46+
* requestBody:
47+
* required: true
48+
* content:
49+
* application/json:
50+
* schema:
51+
* $ref: '#/components/schemas/CreateUserInput'
52+
* responses:
53+
* 200:
54+
* description: Success
55+
* content:
56+
* application/json:
57+
* schema:
58+
* $ref: '#/components/schemas/CreateUserResponse'
59+
* 409:
60+
* description: Conflict
61+
* 400:
62+
* description: Bad request
63+
*/
2864
app.post("/api/users", validateResource(createUserSchema), createUserHandler);
2965

3066
app.post(
@@ -43,6 +79,28 @@ function routes(app: Express) {
4379
createProductHandler
4480
);
4581

82+
/**
83+
* @openapi
84+
* '/api/products/{productId}':
85+
* get:
86+
* tags:
87+
* - Products
88+
* summary: Get a single product by the productId
89+
* parameters:
90+
* - name: productId
91+
* in: path
92+
* description: The id of the product
93+
* required: true
94+
* responses:
95+
* 200:
96+
* description: Success
97+
* content:
98+
* application/json:
99+
* schema:
100+
* $ref: '#/components/schema/Product'
101+
* 404:
102+
* description: Product not found
103+
*/
46104
app.put(
47105
"/api/products/:productId",
48106
[requireUser, validateResource(updateProductSchema)],

src/schema/product.schema.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,27 @@
11
import { object, number, string, TypeOf } from "zod";
2+
3+
/**
4+
* @openapi
5+
* components:
6+
* schema:
7+
* Product:
8+
* type: object
9+
* required:
10+
* - title
11+
* - description
12+
* - price
13+
* - image
14+
* properties:
15+
* title:
16+
* type: string
17+
* description:
18+
* type: string
19+
* price:
20+
* type: number
21+
* image:
22+
* type: string
23+
*/
24+
225
const payload = {
326
body: object({
427
title: string({

src/schema/user.schema.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,44 @@
11
import { object, string, TypeOf } from "zod";
22

3+
/**
4+
* @openapi
5+
* components:
6+
* schemas:
7+
* CreateUserInput:
8+
* type: object
9+
* required:
10+
* - email
11+
* - name
12+
* - password
13+
* - passwordConfirmation
14+
* properties:
15+
* email:
16+
* type: string
17+
* default: jane.doe@example.com
18+
* name:
19+
* type: string
20+
* default: Jane Doe
21+
* password:
22+
* type: string
23+
* default: stringPassword123
24+
* passwordConfirmation:
25+
* type: string
26+
* default: stringPassword123
27+
* CreateUserResponse:
28+
* type: object
29+
* properties:
30+
* email:
31+
* type: string
32+
* name:
33+
* type: string
34+
* _id:
35+
* type: string
36+
* createdAt:
37+
* type: string
38+
* updatedAt:
39+
* type: string
40+
*/
41+
342
export const createUserSchema = object({
443
body: object({
544
name: string({

src/utils/swagger.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Express, Request, Response } from "express";
2+
import swaggerJsdoc from "swagger-jsdoc";
3+
import swaggerUi from "swagger-ui-express";
4+
import { version } from "../../package.json";
5+
import log from "./logger";
6+
7+
const options: swaggerJsdoc.Options = {
8+
definition: {
9+
openapi: "3.0.0",
10+
info: {
11+
title: "REST API Docs",
12+
version,
13+
},
14+
components: {
15+
securitySchemas: {
16+
bearerAuth: {
17+
type: "http",
18+
scheme: "bearer",
19+
bearerFormat: "JWT",
20+
},
21+
},
22+
},
23+
security: [
24+
{
25+
bearerAuth: [],
26+
},
27+
],
28+
},
29+
apis: ["./src/routes.ts", "./src/schema/*.ts"],
30+
};
31+
32+
const swaggerSpec = swaggerJsdoc(options);
33+
34+
function swaggerDocs(app: Express, port: number) {
35+
// Swagger page
36+
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
37+
38+
// Docs in JSON format
39+
app.get("/docs.json", (req: Request, res: Response) => {
40+
res.setHeader("Content-Type", "application/json");
41+
res.send(swaggerSpec);
42+
});
43+
44+
log.info(`Docs available at http://localhost:${port}/docs`);
45+
}
46+
47+
export default swaggerDocs;

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
3434
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
3535
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
36-
// "resolveJsonModule": true, /* Enable importing .json files */
36+
"resolveJsonModule": true /* Enable importing .json files */,
3737
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
3838

3939
/* JavaScript Support */

0 commit comments

Comments
 (0)