I have a Typescripted nodejs server and i'm trying to define diffrent swagger paths for seperated controllers, but the swagger-ui-express module seems to only show the last defined doc in the specific route.
index.ts for X group of controllers
import express from 'express';
import passport from 'passport';
const router = express.Router();
// import all bot routes
import { authRoute } from './auth';
import { botCrudRoute } from './bot-crud';
import { aiRoutes } from './ai';
import { categoryCrudRoute } from './categories-crud';
const swaggerUi = require('swagger-ui-express');
import { botSwaggerDoc } from './swagger';
const swaggerDoc = botSwaggerDoc;
const swaggerUiOpts = {
explorer: false
};
// Swagger setup
router.use('/api-docs', swaggerUi.serve);
router.get('/api-docs', swaggerUi.setup(swaggerDoc, swaggerUiOpts));
index.ts for Y group of controllers
import express from 'express';
const router = express.Router();
// import all bot routes
const swaggerUi = require('swagger-ui-express');
import { adminSwaggerDoc } from './swagger';
const swaggerDoc = adminSwaggerDoc;
const swaggerUiOpts = {
explorer: false
};
// Swagger setup
router.use('/api-docs', swaggerUi.serve);
router.get('/api-docs', swaggerUi.setup(swaggerDoc, swaggerUiOpts));
export const adminRoutes = router;
api.ts grouping all groups of controllers
'use strict';
import express from 'express';
import { Response, Request, NextFunction } from 'express';
import { adminRoutes } from './admin';
import { botRoutes } from './bot';
// import { onboardRoutes } from './onboard';
const router = express.Router();
// router.use('/onboard', onboardRoutes);
router.use('/bot', botRoutes);
router.use('/admin', adminRoutes);
export const apiRoutes = router;
server.ts
/**
* Primary app routes.
*/
app.use('/api', apiRoutes);
example of one of the swaggerDoc's
export const botSwaggerDoc = {
'swagger': '2.0',
'info': {
'version': '1.0.0',
'title': 'Cupo embed chat bot API',
'license': {
'name': 'Internal use only'
}
the swagger-ui-express module only use the last defined document as if the server keeps reference to that document...
I was able to get around this by serving up the HTML directly for each individual api. See below:
// index.ts for X group of controllers
const apiV1Html = swaggerUi.generateHTML(
v1SwaggerDocument,
);
router.use(
'/docs',
swaggerUi.serveFiles(v1SwaggerDocument),
);
router.get('/docs', (req: any, res: any) => {
res.send(apiV1Html);
});
and for the Y group of controllers:
// index.ts for y group of controllers
const apiV2Html = swaggerUi.generateHTML(
v2SwaggerDocument,
);
router.use(
'/docs',
swaggerUi.serveFiles(v2SwaggerDocument),
);
router.get('/docs', (req: any, res: any) => {
res.send(apiV2Html);
});
Sources: https://github.com/scottie1984/swagger-ui-express/issues/65
Related
I am trying to read all routes in my app.js file from './routes' folder,but
gettingerror:"TypeError: Cannot read property 'forEach' of undefined"
import express from "express";
import fs from "fs";
const app = express();
fs.readdir('./routes', (err, fs) => {
fs.forEach(file => {
app.use('/', require('./routes/' + file))
});
})
export default app
This worked for me:
sync
fs.readdirSync('/some/path/routes').forEach(function(file) {
app.use('/', require(`/some/path/routes/` + file));
});
async
fs.readdir('/some/path/routes', function(err, files){
files.forEach(function(file){
app.use('/', require(`/some/path/routes/` + file));
});
});
You can do using fs.readdirSync because the server is not yet started.
fs.readdir(`${__dirname}/routes`, (err, files) => {
if (err)
console.log(err);
else {
files.forEach(file => {
app.use('/', require(`${__dirname}/routes` + file));
})
}
})
You can use fspromise and import to handle using promise. 🚀🚀🚀
import { promises as fsPromises } from 'fs';
export const init = async () => {
const files = await fsPromises.readdir(`${__dirname}/routes`);
const router = Router();
const createRoute = async (file) => {
const route = await import(`./routes/${file}`);
router.use("/", route.default);
};
await Promise.all(files.map(createRoute));
return router;
}
// app.js
import {init} from "./routes";
const app = express();
(async() => {
await init();
// Start server
})();
I would suggest doing like below. You will have more control over routes. You can do customization on routes bases if needed.
ex. /v1 and /v2 etc..
// routes/index.js
import { Router } from "express";
import route1 from './route1';
import route2 from './route2';
const router = Router();
[route1, route2].forEach((route) => router.use("/", route));
export default router;
// app.js
import express from "express";
import routes from "./routes";
const app = express();
app.use("/", routes);
export default app;
I make simple instagram api application and I have problem with my routes, export and import. i get error TypeError: Router.use() requires a middleware function but got a undefined
at Function.use (C:\Documents\Desktop\digi.me\node_modules\express\lib\router\index.js:458:13)
at Object. (C:\Documents\Desktop\digi.me\lib\routes\routes.js:12:8)
I have two routes userRouter that go on localhost:3000
and mediaRouter that go on localhost:3000/media
my userRouter.ts
import request, { post } from "request"
import express from "express"
import { Router } from 'express';
const app = express()
const token: any = process.env.INSTAGRAM_TOKEN
export const userRouter = Router()
userRouter.get("/", (req: any, res: any) => {
const url = 'https://graph.instagram.com/17841403377847296/?access_token='+token+'&fields=account_type,media_count,username'
request({ url: url, json: true }, (error: string, response: any) => {
if(error){
res.send("unable to connect to service")
}else{
res.send("USER " + response.body.username + " HAVE " + response.body.media_count + " POSTS")
}
})
})
my mediaRouter
import request, { post } from "request"
import express from "express"
import {Post} from "../models/Post"
import { Router } from 'express';
const token: any = process.env.INSTAGRAM_TOKEN
export const mediaRouter = Router()
mediaRouter.get("/media", (req: any, res: any) => {
console.log(0);
const url = 'https://graph.instagram.com/17841403377847296/media?fields=id,media_url,timestamp&access_token='+token+''
request({ url: url, json: true }, (error:string, response: any) => {
if(error){
console.log('error');
res.send("unable to connect to service")
} else {
const array = response.body.data
var Posts: Post[] = [];
array.forEach(function (obj: any) {
Posts.push(new Post(obj.media_url, obj.timestamp))
});
res.send(Posts);
}
})
})
my routes.ts
import { Router } from 'express';
import userRouter from "./routes"
import mediaRouter from "./routes"
import express from 'express';
const app = express();
const routes = Router();
routes.use('/', userRouter);
routes.use('/media', mediaRouter);
export default routes
and my main app.ts
import express from "express"
import * as dotenv from "dotenv"
import routes from "./routes/routes"
const app = express()
dotenv.config();
//envirements variable for port
const port: any = process.env.PORT
app.use(routes);
app.use(express.json());
//Adding server to port
app.listen(port, () => {
console.log("server is listen on port 3000")
})
I was trying to use koa-views as my renderer, but I kept getting the message from TS:
Property 'render' does not exist on type 'ParameterizedContext<any, IRouterParamContext<any, {}>>'.ts(2339)
My app.js
import Koa from "Koa";
import views from "koa-views";
import indexRouter from "./routes/index"
const app = new Koa();
app.use(views(`${__dirname}/views`, { autoRender: true, extension: "swig" }));
app.use(indexRouter.routes());
index.js - IndexRouter:
import Router from "koa-router";
const router = new Router();
router.get("/", async (ctx, next) => {
ctx.render(); // not done yet tho
await next();
})
export = router;
This is because argument ctx type doesn't have method render(), but in types lib #types/koa-views declared module (see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/koa-views/index.d.ts#L55).
And you can make this:
import { Context, DefaultState } from "koa";
import * as Router from "koa-router";
const router = new Router<DefaultState, Context>();
router.get("/", async (ctx: Context, next) => {
await ctx.render("/path/");
await next();
});
i am making the webside , here is some of my code
in app.ts
import * as express from 'express';
import knex from './init/knex';
// Router
import indexRouter from './routers/index-router';
// Service
import gameService from './service/game-service';
const app = express();
var server = app.listen(8080, () => {
console.log('listen to 8080');
})
app.use('/', new indexRouter(new gameService(knex)).router());
in index-router.ts
import * as express from 'express';
export default class indexRouter {
private gameService: any;
constructor(gameService: any) {
this.gameService = gameService;
}
router() {
let router = express.Router();
router.get("/", this.get.bind(this));
router.post("/test", this.test.bind(this));
return router;
}
test(req: any, res: any) {
// console.log("indexRouter get()");
console.log(req.body);
res.render("gameboard", { pin: "1", username: "1", player: 1 });
}
get(req: any, res: any, next:any) {
req.body = {3:"3"};
// something i want to do here
}
}
When user link to "http://localhost:8080/" and "get" method is called
How can i change:
the url from "http://localhost:8080/" to "http://localhost:8080/test"
call "test" method with the new request body
I have spent a lot of time google how to do i but i can't find the best way to do the above two things.
Do you mean something like:
get(req: any, res: any, next:any) {
test(req.body);
res.redirect('/test');
}
?
I have structure like this:
server.ts
routes/
index.ts
homeRoute.ts
In server.ts:
let app = Express();
app.use(router);
In routes/index.ts:
const routes = Router();
export default function router() {
routes.use('/home', homeRoute);
}
In routes/homeRoutes.ts
let homeRouter = Express.Router();
export default function homeRoute(req: Request, res: Response, next: NextFunction) {
console.log('home route');
homeRouter.get('/home', function);
next();
}
My problem is when i call http://localhost:3000, it run to index.ts file ( I console.log some things so I know that), however it don't execute my routes.use('/home', homeRoute).
I don't know why it is. Please help me fix it.
Understand how import and export works. Ideally your code should be something like this.
server.ts
import * as express from 'express';
import {routes} from './routes';
const app = express();
routes(app);
routes/index.ts
import {homeRoute} from './homeRoutes';
export const routes = (app) => {
homeRoute(app);
}
routes/homeRoutes.ts
export const homeRoute = (app) => {
app.get('/home', function (req, res, next) {
res.render('home');
});
}