diff --git a/server/express/package.json b/server/express/package.json index c067a31..b776f78 100644 --- a/server/express/package.json +++ b/server/express/package.json @@ -21,13 +21,17 @@ "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", - "mongodb": "^6.20.0" + "mongodb": "^6.20.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/node": "^20.10.5", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", "jest": "^29.7.0", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", diff --git a/server/express/src/app.ts b/server/express/src/app.ts index 571ffd4..1d4898f 100644 --- a/server/express/src/app.ts +++ b/server/express/src/app.ts @@ -9,6 +9,7 @@ import express from "express"; import cors from "cors"; import dotenv from "dotenv"; +import swaggerUi from "swagger-ui-express"; import { closeDatabaseConnection, connectToDatabase, @@ -16,6 +17,7 @@ import { } from "./config/database"; import { errorHandler } from "./utils/errorHandler"; import moviesRouter from "./routes/movies"; +import { swaggerSpec } from "./config/swagger"; // Load environment variables from .env file // This must be called before any other imports that use environment variables @@ -44,6 +46,12 @@ app.use( app.use(express.json({ limit: "10mb" })); app.use(express.urlencoded({ extended: true, limit: "10mb" })); +/** + * Swagger API Documentation + * Provides interactive API documentation at /api-docs + */ +app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); + /** * API Routes * All movie-related CRUD operations are handled by the movies router @@ -53,6 +61,37 @@ app.use("/api/movies", moviesRouter); /** * Root Endpoint * Provides basic information about the API + * @swagger + * /: + * get: + * summary: Get API information + * description: Returns basic information about the API and available endpoints + * tags: [Info] + * responses: + * 200: + * description: API information + * content: + * application/json: + * schema: + * type: object + * properties: + * name: + * type: string + * example: MongoDB Sample MFlix API + * version: + * type: string + * example: 1.0.0 + * description: + * type: string + * endpoints: + * type: object + * properties: + * movies: + * type: string + * example: /api/movies + * documentation: + * type: string + * example: /api-docs */ app.get("/", (req, res) => { res.json({ @@ -62,6 +101,7 @@ app.get("/", (req, res) => { "Express.js backend demonstrating MongoDB operations with the sample_mflix dataset", endpoints: { movies: "/api/movies", + documentation: "/api-docs", }, }); }); @@ -94,7 +134,7 @@ async function startServer() { // Start the Express server app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); - console.log(`API documentation available at http://localhost:${PORT}`); + console.log(`API documentation available at http://localhost:${PORT}/api-docs`); }); } catch (error) { console.error("Failed to start server:", error); diff --git a/server/express/src/config/swagger.ts b/server/express/src/config/swagger.ts new file mode 100644 index 0000000..3002da9 --- /dev/null +++ b/server/express/src/config/swagger.ts @@ -0,0 +1,354 @@ +/** + * Swagger/OpenAPI Configuration + * + * This module configures swagger-jsdoc to generate OpenAPI documentation + * from JSDoc comments in the codebase. + */ + +import swaggerJsdoc from "swagger-jsdoc"; + +const options: swaggerJsdoc.Options = { + definition: { + openapi: "3.0.0", + info: { + title: "MongoDB Sample MFlix API", + version: "1.0.0", + description: + "Express.js backend demonstrating MongoDB operations with the sample_mflix dataset. " + + "This API provides CRUD operations for movies, including text search, filtering, pagination, and batch operations.", + contact: { + name: "API Support", + }, + license: { + name: "Apache 2.0", + url: "https://www.apache.org/licenses/LICENSE-2.0.html", + }, + }, + servers: [ + { + url: "http://localhost:3001", + description: "Development server", + }, + ], + tags: [ + { + name: "Movies", + description: "Movie CRUD operations and queries", + }, + { + name: "Info", + description: "API information", + }, + ], + components: { + schemas: { + Movie: { + type: "object", + required: ["title"], + properties: { + _id: { + type: "string", + description: "MongoDB ObjectId", + example: "573a1390f29313caabcd4135", + }, + title: { + type: "string", + description: "Movie title", + example: "The Shawshank Redemption", + }, + year: { + type: "integer", + description: "Release year", + example: 1994, + }, + plot: { + type: "string", + description: "Short plot summary", + example: "Two imprisoned men bond over a number of years...", + }, + fullplot: { + type: "string", + description: "Full plot description", + }, + runtime: { + type: "integer", + description: "Runtime in minutes", + example: 142, + }, + poster: { + type: "string", + description: "URL to movie poster", + }, + genres: { + type: "array", + items: { + type: "string", + }, + description: "Movie genres", + example: ["Drama"], + }, + directors: { + type: "array", + items: { + type: "string", + }, + description: "Movie directors", + example: ["Frank Darabont"], + }, + writers: { + type: "array", + items: { + type: "string", + }, + description: "Movie writers", + }, + cast: { + type: "array", + items: { + type: "string", + }, + description: "Movie cast", + example: ["Tim Robbins", "Morgan Freeman"], + }, + countries: { + type: "array", + items: { + type: "string", + }, + description: "Countries of origin", + example: ["USA"], + }, + languages: { + type: "array", + items: { + type: "string", + }, + description: "Languages", + example: ["English"], + }, + rated: { + type: "string", + description: "MPAA rating", + example: "R", + }, + awards: { + type: "object", + properties: { + wins: { + type: "integer", + example: 21, + }, + nominations: { + type: "integer", + example: 42, + }, + text: { + type: "string", + example: "Nominated for 7 Oscars. Another 21 wins & 42 nominations.", + }, + }, + }, + imdb: { + type: "object", + properties: { + rating: { + type: "number", + format: "float", + example: 9.3, + }, + votes: { + type: "integer", + example: 2343110, + }, + id: { + type: "integer", + example: 111161, + }, + }, + }, + tomatoes: { + type: "object", + properties: { + viewer: { + type: "object", + properties: { + rating: { + type: "number", + format: "float", + }, + numReviews: { + type: "integer", + }, + meter: { + type: "integer", + }, + }, + }, + critic: { + type: "object", + properties: { + rating: { + type: "number", + format: "float", + }, + numReviews: { + type: "integer", + }, + meter: { + type: "integer", + }, + }, + }, + }, + }, + metacritic: { + type: "integer", + example: 80, + }, + type: { + type: "string", + example: "movie", + }, + }, + }, + CreateMovieRequest: { + type: "object", + required: ["title"], + properties: { + title: { + type: "string", + description: "Movie title (required)", + example: "My New Movie", + }, + year: { + type: "integer", + example: 2024, + }, + plot: { + type: "string", + example: "An exciting new film...", + }, + fullplot: { + type: "string", + }, + genres: { + type: "array", + items: { + type: "string", + }, + example: ["Action", "Adventure"], + }, + directors: { + type: "array", + items: { + type: "string", + }, + example: ["Jane Director"], + }, + writers: { + type: "array", + items: { + type: "string", + }, + }, + cast: { + type: "array", + items: { + type: "string", + }, + }, + countries: { + type: "array", + items: { + type: "string", + }, + }, + languages: { + type: "array", + items: { + type: "string", + }, + }, + rated: { + type: "string", + }, + runtime: { + type: "integer", + }, + poster: { + type: "string", + }, + }, + }, + UpdateMovieRequest: { + type: "object", + properties: { + title: { + type: "string", + }, + year: { + type: "integer", + }, + plot: { + type: "string", + }, + genres: { + type: "array", + items: { + type: "string", + }, + }, + }, + description: "All fields are optional for partial updates", + }, + SuccessResponse: { + type: "object", + properties: { + success: { + type: "boolean", + example: true, + }, + message: { + type: "string", + example: "Operation completed successfully", + }, + data: { + type: "object", + description: "Response data (varies by endpoint)", + }, + }, + }, + ErrorResponse: { + type: "object", + properties: { + success: { + type: "boolean", + example: false, + }, + error: { + type: "object", + properties: { + message: { + type: "string", + example: "An error occurred", + }, + code: { + type: "string", + example: "ERROR_CODE", + }, + details: { + type: "string", + }, + }, + }, + }, + }, + }, + }, + }, + // Path to the API routes files where JSDoc comments are located + apis: ["./src/routes/*.ts", "./src/app.ts"], +}; + +export const swaggerSpec = swaggerJsdoc(options); + diff --git a/server/express/src/controllers/movieController.ts b/server/express/src/controllers/movieController.ts index 14269dc..d913a09 100644 --- a/server/express/src/controllers/movieController.ts +++ b/server/express/src/controllers/movieController.ts @@ -848,7 +848,7 @@ export async function vectorSearchMovies(req: Request, res: Response): Promise