Using koa-views with TypeScript - node.js

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();
});

Related

How to read all routes dynamically in node.js

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;

how to serve assets from Nest.js and add middleware to detect image request

I am trying to serve image from Nest.js server and add middleware to track all request but the only way I could make it work was with express
import { NestFactory } from '#nestjs/core';
import * as bodyParser from "body-parser";
import {AppModule} from "./app.module";
import * as path from "path";
import * as express from 'express';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(bodyParser.json({limit: '50mb'}));
app.use(function(req, res, next){
next();
});
//
app.use('/api/track/',express.static(path.join(__dirname, '/public'))); //Serves resources from public folder
app.use('/api/track/:img', function (req, res, next) {
console.log('do something');
next();
});
await app.listen(3333);
}
bootstrap();
How can I implement it with using the controller or middleware?
The nestjs doc tells you, how to serve static files. In short, here is how you do it:
Specify root directory of your assets in you main.ts
app.useStaticAssets(path.join(__dirname, '/../public'));
Use the #Res annotation to be able to use the sendFile method of express framework
#Get('track/:imgId')
test(#Param('imgId') imgId, #Res() res) {
const imgPath = getImgPath(imgId);
return res.sendFile(imgPath, { root: 'public' });
}
This solution assumes that your nestjs installation uses express under the hood.
Sources:
https://github.com/nestjs/nest/issues/402
https://docs.nestjs.com/techniques/mvc
Want to add a point here, please make sure you're using:
const app = await NestFactory.create<NestExpressApplication>(AppModule);
Instead of this:
const app = await NestFactory.create(AppModule);
in your main.ts file.
It worked for me. Just inject this controller in app module.
import { Controller, Get, Req, Res } from '#nestjs/common';
#Controller('uploads')
export class ServeFilesController {
#Get('products/images/:imageName')
invoke(#Req() req, #Res() res) {
return res.sendFile(req.path, { root: './' });
}
}

Swagger-ui-express module, instantiates only the last defined document

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

Routing in express

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');
});
}

nodejs combime with typescript

I try to use typescript with nodejs.
I don't understand why it show me error. Please help me fix it.
Here is my code (assum that I Import all module needed)
This is my index.ts file:
import routes from "./routes/routes";
let app = express();
app.use(routes);
This is my routes/routes.ts:
import * as homeRoute from "../apps/home/home.route";
let app = express();
export default function routes() {
app.use("./home", homeRoute); // It show error in path: "./home"
return app;
}
This is my home.route.ts
let router = express.Router();
router.get("/", HomeController.hello); // it show error that property "hello" not exist
// }
module.exports = "homeRoute";
This is my home.controller.ts:
exports.hello = function (req, res, next) {
console.log("Hello word");
}
In your home.controller.ts use an export statement instead:
export const hello = (req, res, next) => {
console.log("Hello word");
}
Then in your home.route.ts you have two ways of importing your controller method:
import * as HomeController from '../path/to/home.controller';
let router = express.Router();
router.get("/", HomeController.hello);
or
import {hello} from '../path/to/home.controller';
let router = express.Router();
router.get("/", hello);

Resources