How to declare variable of type Express in TypeScript without importing express? - node.js

I have main file called main.ts where I imported express with
import express from 'express';
Then I have another class in separate file where I want to create method "init" which has one parameter named "app" of type Express. But somehow i can't say app:Express without importing express.
My goal is to import express only once and keep it in a main.ts file, and then in a main.ts file I will call "init" method ( from a separate file) where I will pass that imported express.
Main.ts file
import express from 'express';
import { FriendsRouter } from './routes/friends.router';
const app = express();
FriendsRouter.init(app);
const PORT:number = 3000;
app.listen(PORT,()=>{
console.log('Listening at '+PORT);
})
Spearete file (friends router in my case)
export class FriendsRouter {
private constructor(){
}
public static init(app:Express): void{
app.get('/friends',someMethod);
}
}
Problem is, in FriendsRouter file, I can't say app: Express.
How can I fix this ?

The usual thing is to import the type Express from the express package:
import { Express } from "express";
Note that that's just importing the type, not the function. The rest of your code is then fine as-is (including the type on app).
If you don't have that type, install the types for express like this:
npm install --save-dev #types/express
...but you've probably already done that.

Related

When using (require/import) axios, Cannot seem to use a JS custom function within Angular (.ts) component

I am trying to embed a JS custom function within my Angular (.ts) component.
The proccess i have gone through:
created and added a .js file with a function of my need
added the reference to this file on my angular.json scripts array
defined the function in my .ts component file
When i do this proccess with a .js file without require('axios') etc. it seems to work perfectly. but my .js file has some libraries (axios, express, cheerio) and when i use 'ng serve' i get an error at my console:
"Uncaught ReferenceError: require is not defined" - when using require
"Uncaught SyntaxError: Cannot use import statement outside a module" - when changing it to import
my .js file beginning:
// const axios = require('axios')
// const cheerio = require('cheerio')
// const express = require('express')
import axios from "axios"
import { Cheerio } from "cheerio"
import { Express } from "express"
const app = Express()
const axi = axios()
const cheer = cheerio()
function getScheduleFromUrl() {}
my .ts component references to the function:
declare function getScheduleFromUrl(): void;
constructor(private tvScheduleService: TvScheduleService) {
getScheduleFromUrl()
}
finally the reference in angular.json:
"src/assets/js/web-scrape-tv-schedule.js"]
Couldnt find a working example of this.
again: using .js function within my .ts component, while the .js file contains some exports

TypeORM Connection "default" was not found when using a routes file and repository pattern

I have a TS project where I'm using TypeORM. I'm using Repository pattern so I have layers: Controller -> Service -> Repository.
The Controller constructor initialize the Service class which initialize Repository class, where the constructor is:
constructor(){
this.ormRepository = getRepository(TypeormEntity)
}
This works fine when I create the routes in the index.ts like this:
createConnection().then(() => {
const controller = new MyController()
app.get('/', controller.getSomething);
})
The connection is created and then I initialize the class using new MyController().
The problem is when I want to get the routes in another file. I want to get this structure folder:
src
|--> index.ts
|--> routes
|--> v1
|--> router.ts
|--> value
|--> value.route.ts
|--> v2
|--> ...
So, to achieve this, the index.ts import the routes. But the problem is, as the import is done, the MyController() constructor is executed and the execution fails.
So I want to have something like this in index.ts
import * as express from 'express'
import { createConnection } from "typeorm";
import Router from './routes/v1/router'
const app = express()
createConnection().then(() => {
app.use('/', Router)
app.listen(port)
})
Also, following the import call, the file router.ts in routes/v1 is:
import { json, Router } from 'express'
import valueRouter from './value/value.route'
const router = Router()
router.use(json())
router.use('/value', valueRouter)
export default router
This is to add a "prefix" to all routes depending the resource they call.
And value.route.ts is where the controller is initialized and fails.
import { Router } from "express";
import ValueController from '../../../controller/value.controller'
const router = Router()
const vc = new ValueController()
router.get('/',vc.getSomething)
export default router
I've tested deleting the constructor and the project intializes ok, so the problem is the repository initialization accross Controller & Service.
How can I achieve this?
Thanks in advance.
Finally I solved my own issue using dynamic imports.
Now my index.ts looks like this:
createConnection().then(() => {
import('./routes/v1/router').then((router: any) => {
app.use('/', router.default)
app.listen(port)
})
})
Which I think is much cleaner than having all routes added in this file. Calling routes in an external file I can manage a huge amount and maintain a clean structure.

Can we use all node npm packages in Nestjs

In current, I am learning Nestjs, I found that Nestjs have a list of its own npm package like #nestjs/cqrs, #nestjs/jwt etc. Full list of all packages is https://www.npmjs.com/org/nestjs.
Now I have a doubt that can we use all npm packages in nestjs that we use in any Node.js application like morgan, windston etc.
Or we can only use the packages that mention in nestjs documentation list.
First, node.js packages can be used in Nestjs with its custom provider. So see the current docs.
Second, Brunner on the Nestjs core team helped me with this on the Nestjs_framework Reddit. He made a nice brief example for us on Github. I asked him to change the name from reddit to node so that will break this link, but you can find it on his Github site. He deserves the credit.
We aren't supposed to link to a solution on SO. So let's get started.
Brunner changed my architecture and I like his approach. I wanted to use the Cloudinary Node SDK which doesn't have a Nestjs module. This should work for importing any Node package, at least that is my understanding.
Note: When you write this.cloudinary. you expect to see a list of methods after that '.' . You won't, so just use whatever code the SDK or package docs tells you to use. A little weirdness.
The Cloudinary components live in a folder inside my members directory, which has a bunch of modules imported into it.
cloudinary.module.ts
import { Module } from '#nestjs/common';
import { cloudinaryProvider } from './cloudinary.provider';
import { CloudinaryService } from './cloudinary.service';
#Module({
providers: [cloudinaryProvider, CloudinaryService],
exports: [cloudinaryProvider],
})
export class CloudinaryModule {}
cloudinary.provider.ts
import { Provider } from '#nestjs/common';
import * as CloudinaryLib from 'cloudinary';
export const Cloudinary = 'lib:cloudinary';
export const cloudinaryProvider: Provider = {
provide: Cloudinary,
useValue: CloudinaryLib,
};
cloudinary.service.ts
import { Injectable, Inject } from '#nestjs/common';
import { Cloudinary } from './cloudinary.provider';
#Injectable()
export class CloudinaryService {
constructor(
#Inject(Cloudinary) private cloudinary
) {
// console.log('This is the cloudinary instance:');
// console.log(this.cloudinary);
}
}
And finally the parent module:
members.module.ts
import { CloudinaryModule } from './cloudinary/cloudinary.module'
#Module({
imports: [
CloudinaryModule,
...
Nest will expose the http adapter so that you can hook into it, here is an example using the morgan & body-parser npm package:
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from './config/config.service';
import { ConfigModule } from './config/config.module';
import bodyParser from 'body-parser';
import morgan from 'morgan';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.select(ConfigModule).get(ConfigService);
app.use(bodyParser.json());
app.use(morgan('dev'));
app.enableCors();
await app.listen(configService.PORT, () => console.log(`Server listening on port ${configService.PORT}`));
}
bootstrap();
In this instance above appis an express instance.
Nest is a framework that runs on top of an HTTP adapter (the main two that I know of being Fastify and Express with Express being the default adapter). If the package you are wanting to work with works with express (such as Morgan) then it's no problem. If the package you are wanting to use is platform agnostic (like Pino or Winston) then there are no problems. If the package you want to use is for a different HTTP adapter (like koa-router) then you have a problem (the counter example being something like #hapi/joi which is maintained by the hapi framework maintainers, but is actually adaptable to work with anything). There are some typescript specific packages class-validator and class-transformer come to mind, but overall if the package works on any node environment, it can work for Nest. If you have any questions about packages and implementation you could always check out the discord and ask your question there.

Set "basedir" option for Pug in NestJS

I'm trying to use pug layouts in NestJS, however when extending a layout from an absolute path, pug requires the basedir option to be set.
In ExpressJS you would use app.locals.basedir = ..., what would be the equivalent in NestJS?
const server = await NestFactory.create<NestExpressApplication>(AppModule);
server.setViewEngine('pug');
server.setBaseViewsDir(join(__dirname, 'templates', 'views'));
await server.listen(config.server.port);
Using extends /layouts/index in a view would throw the following; the "basedir" option is required to use includes and extends with "absolute" paths.
I'm not looking to use relative paths, since this quickly becomes very messy. E.g. extends ../../../layouts/index
From what I can tell, you can achieve the same functionality as /layouts/index with just using layout/index so long as layout is a folder in your templates/views directory.
I've set up a git repo as a working example so you can test it out yourself and see if I need to go in more depth about anything.
EDIT 6/27/2019:
Thank you, I misunderstood your initial question.
With creating and express based application, you can send an express server to the NestFactory to use that server instance instead of having Nest create a plain instance for you. From here you can set up the express server as you normally would and get the desired functionality. I've modified the git repo to be able to test the scenario better and believe this is what you are looking for.
My main.ts
import { NestFactory } from '#nestjs/core';
import { NestExpressApplication, ExpressAdapter } from '#nestjs/platform-express';
import * as express from 'express';
import { AppModule } from './app.module';
import { join } from 'path';
async function bootstrap() {
// Creating and setting up the express instanced server
const server = express();
server.locals.basedir = join(__dirname, '..', 'views');
// Using the express server instance in the nest factory
const app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter(server));
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('pug');
await app.listen(3000);
}
bootstrap();
Overall the folder set up is like so
src
|-app.controller.ts
|-app.module.ts
|-app.service.ts
|-main.ts
views
|-hello
|-home.pug
|-message
|-message.pug
|-templates
|-layout.pug
And the beginning of my home.pug and message.pug files is extends /templates/layout
After looking around through the documentation, NestJS uses an express under the hood, and gives you access to the underlying instance with getHttpAdapter().getInstance().
Keeping that in mind, we can set the basedir as follows;
const express = server.getHttpAdapter().getInstance();
express.locals.basedir = join(__dirname, 'templates');

How to import a default export of webpack entry file from outside?

I think I can best explain it with code. I have a file in webpack like the following:
import ReactDOMServer from 'react-dom/server';
import Server from './server';
import templateFn from './template';
export default (req, res) => {
const reactString = ReactDOMServer.renderToString(<Server />);
const template = templateFn(html);
res.send(template);
};
I also have an express application where I want to have access to the default exported function. If it makes any difference, this file is the webpack entry file. Here is what I tried in my express app:
const handleRequest = require(path.resolve(webpackConfig.output.path, webpackConfig.output.filename));
app.get('*', (req, res) => {
console.log(handleRequest);
});
I was trying to import the webpack generated file with the hope that I will be able to access the entry file's default export. Well, I was wrong as the output of the import was {}.
Is there a webpack plugin or some kind of a technique to do what I am trying to build? I don't want the express application to be part of the webpack build. That was the main reason I separated the code in this way.
I was able to access contents of webpack using library parameter (webpack.config.js):
output: {
path: ...,
filename: ...,
library: 'myapp',
libraryTarget: 'commonjs'
}
Then access it in the code:
const output = require(path.resolve(webpackConfig.output.path, webpackConfig.output.filename));
const defaultExportFunction = output.myapp.default;

Resources