so I'm trying to set up Swagger for my NodeJS app but I'm struggling with a problem for a while now. I will try to explain as detailed as possible.
So I have swagger.util.js file:
import swaggerJSDoc from "swagger-jsdoc";
const options = {
failOnErrors: true,
definition: {
openapi: "3.0.0",
info: {
title: "API",
version: "1.0.0",
description: "Endpoints"
},
servers: [
{
url: "http://localhost:80",
description: "HTTP"
},
{
url: "http://localhost:443",
description: "HTTPS"
}
],
},
apis: ["../routes/*.js"]
};
const specs = swaggerJSDoc(options);
export default specs;
This user.route.js file:
import express from "express";
import trimRequest from "trim-request";
import { register } from "../controllers/user.controller.js";
const router = express.Router();
/**
* @swagger
* components:
* schemas:
* UserAccount:
* type: object
* required:
* - username
* - email
* - password
* properties:
* username:
* type: string
* email:
* type: string
* password:
* type: string
* example:
* username: Test
* email: [email protected]
* password: TestTest1!
*/
router.route("/register").post(trimRequest.all, register);
export default router;
And this app.js file that is used in index.js:
import compression from "compression";
import cookieParser from "cookie-parser";
import cors from "cors";
import dotenv from "dotenv";
import express from "express";
import mongoSanitize from "express-mongo-sanitize";
import helmet from "helmet";
import createHttpError from "http-errors";
import morgan from "morgan";
import swaggerUI from "swagger-ui-express";
import routes from "./routes/index.route.js";
import specs from "./utils/swagger.utils.js";
dotenv.config();
const app = express();
app.use(compression());
app.use(cookieParser());
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use(helmet());
app.use(mongoSanitize());
if (process.env.ENV !== "prd") {
app.use(morgan("dev"));
}
app.use("/docs", swaggerUI.serve, swaggerUI.setup(specs));
console.log(specs);
// Remove this later
app.get("/", (_req, res) => {
res.send("Hello");
});
app.use("/api/v1", routes);
app.use(async (_req, __res, next) => {
next(createHttpError.NotFound("This route does not exist"));
});
app.use(async (err, _req, res,_next) => {
res.status(err.status || 500);
res.send({
error: {
status: err.status || 500,
message: err.message,
},
});
});
export default app;
The folder structure is the following:
-src
-controllers
-user.controller.js
-routes
-index.route.js
-user.route.js
-utils
-swagger.utils.js
-app.js
-index.js
The problem is that console.log(specs); from app.js returns a json that has the components field empty (empty object) for some reason. But it should return something since I have that JSDoc comment in the user.route.js right?
And as I said, the doc is empty too:
Do you know which might be the cause/problem? Or how can I fix this? Thank you!
I believe your issue is where you run the command from. If you're running the command from the root of the project, your relative path is incorrect.
const options = {
definition: {
openapi: '3.0.3',
info: {
title: 'test',
version: '1.0.0'
},
servers: [
{ url: 'https://www.example.com/v1' }
]
},
apis: [
'src/routes/user.route.js' // <<< this can't use a relative ref.
]
}
Here's a simple repo
📦src
┣ 📂routes
┃ ┗ 📜user.route.js
┣ 📂utils
┃ ┗ 📜openapi.util.js
┗ 📜index.js
// index.js
import express from 'express'
import swaggerUI from 'swagger-ui-express'
import OASdescription from './utils/openapi.util.js'
const app = express()
app.use(express.json())
const PORT = process.env.PORT || 3000
app.use('/api/docs', swaggerUI.serve, swaggerUI.setup(OASdescription))
app.listen(PORT, () => console.log(`listening on port ${PORT}`))
// openapi.util.js
import swaggerJsdoc from 'swagger-jsdoc'
const options = {
definition: {
openapi: '3.0.3',
info: {
title: 'test',
version: '1.0.0'
},
servers: [
{ url: 'https://www.example.com/v1' }
]
},
apis: [
'src/routes/user.route.js'
]
}
const OASDescription = swaggerJsdoc(options)
export default OASDescription
// user.route.js
import { Router } from 'express'
const router = Router()
/**
* @openapi
* '/user':
* get:
* description: my hosted api docs
* summary: api docs
* responses:
* '200':
* description: OK
* content:
* 'application/json':
* schema:
* type: object
* properties:
* thing:
* $ref: '#/components/schemas/UserAccount'
* components:
* schemas:
* UserAccount:
* type: object
* required:
* - username
* properties:
* username:
* type: string
*/
router.get('/user', (req, res) => {
res.status('200').json({ "username": "test" })
})
export default router