From bc4a8a556c15812ea919c0da5a4f25be38fbb982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20My=C5=9Bliwiec?= Date: Fri, 30 Nov 2018 16:48:04 +0100 Subject: [PATCH] feature(core/common) extract HTTP adapters (express/fastify) --- packages/common/index.ts | 2 - .../interfaces/http/http-server.interface.ts | 3 + packages/common/interfaces/index.ts | 2 - packages/core/adapters/express-adapter.ts | 152 ------------------ packages/core/adapters/express-factory.ts | 8 - packages/core/adapters/http-adapter.ts | 90 +++++++++++ packages/core/adapters/index.ts | 2 +- packages/core/exceptions/index.ts | 1 + packages/core/helpers/index.ts | 1 + packages/core/index.ts | 9 +- packages/core/injector/index.ts | 1 + packages/core/middleware/index.ts | 1 + packages/core/nest-application.ts | 147 +++-------------- packages/core/nest-factory.ts | 78 +++++---- packages/core/package.json | 2 - packages/platform-express/Readme.md | 76 +++++++++ .../adapters/express-adapter.ts | 123 ++++++++++++++ packages/platform-express/adapters/index.ts | 1 + packages/platform-express/index.ts | 9 ++ .../platform-express/interceptors/index.ts | 0 packages/platform-express/interfaces/index.ts | 1 + .../nest-express-application.interface.ts | 4 +- .../serve-static-options.interface.ts | 0 packages/platform-express/package.json | 18 +++ packages/platform-express/tsconfig.json | 7 + packages/platform-fastify/Readme.md | 76 +++++++++ .../adapters/fastify-adapter.ts | 11 +- packages/platform-fastify/adapters/index.ts | 1 + packages/platform-fastify/index.ts | 9 ++ .../platform-fastify/interceptors/index.ts | 0 packages/platform-fastify/interfaces/index.ts | 1 + .../nest-fastify-application.interface.ts | 0 packages/platform-fastify/package.json | 18 +++ packages/platform-fastify/tsconfig.json | 7 + packages/testing/testing-module.ts | 61 ++++--- 35 files changed, 548 insertions(+), 374 deletions(-) delete mode 100644 packages/core/adapters/express-adapter.ts delete mode 100644 packages/core/adapters/express-factory.ts create mode 100644 packages/core/adapters/http-adapter.ts create mode 100644 packages/core/exceptions/index.ts create mode 100644 packages/core/helpers/index.ts create mode 100644 packages/core/middleware/index.ts create mode 100644 packages/platform-express/Readme.md create mode 100644 packages/platform-express/adapters/express-adapter.ts create mode 100644 packages/platform-express/adapters/index.ts create mode 100644 packages/platform-express/index.ts create mode 100644 packages/platform-express/interceptors/index.ts create mode 100644 packages/platform-express/interfaces/index.ts rename packages/{common => platform-express}/interfaces/nest-express-application.interface.ts (91%) rename packages/{common/interfaces/external => platform-express/interfaces}/serve-static-options.interface.ts (100%) create mode 100644 packages/platform-express/package.json create mode 100644 packages/platform-express/tsconfig.json create mode 100644 packages/platform-fastify/Readme.md rename packages/{core => platform-fastify}/adapters/fastify-adapter.ts (91%) create mode 100644 packages/platform-fastify/adapters/index.ts create mode 100644 packages/platform-fastify/index.ts create mode 100644 packages/platform-fastify/interceptors/index.ts create mode 100644 packages/platform-fastify/interfaces/index.ts rename packages/{common => platform-fastify}/interfaces/nest-fastify-application.interface.ts (100%) create mode 100644 packages/platform-fastify/package.json create mode 100644 packages/platform-fastify/tsconfig.json diff --git a/packages/common/index.ts b/packages/common/index.ts index 15248806b..eb90eb3d7 100644 --- a/packages/common/index.ts +++ b/packages/common/index.ts @@ -23,8 +23,6 @@ export { HttpServer, INestApplication, INestApplicationContext, - INestExpressApplication, - INestFastifyApplication, INestMicroservice, MiddlewareConsumer, MiddlewareFunction, diff --git a/packages/common/interfaces/http/http-server.interface.ts b/packages/common/interfaces/http/http-server.interface.ts index 7ae5a6b2a..bbc88e05c 100644 --- a/packages/common/interfaces/http/http-server.interface.ts +++ b/packages/common/interfaces/http/http-server.interface.ts @@ -1,4 +1,5 @@ import { RequestMethod } from '../../enums'; +import { NestApplicationOptions } from './../../interfaces/nest-application-options.interface'; export type ErrorHandler = ( error: any, @@ -54,6 +55,8 @@ export interface HttpServer { getRequestMethod?(request: TRequest): string; getRequestUrl?(request: TResponse): string; getInstance(): any; + registerParserMiddleware(): any; getHttpServer(): any; + initHttpServer(options: NestApplicationOptions): void; close(): any; } diff --git a/packages/common/interfaces/index.ts b/packages/common/interfaces/index.ts index 6d7686992..857c82258 100644 --- a/packages/common/interfaces/index.ts +++ b/packages/common/interfaces/index.ts @@ -23,8 +23,6 @@ export * from './modules/on-init.interface'; export * from './modules/provider.interface'; export * from './nest-application-context.interface'; export * from './nest-application.interface'; -export * from './nest-express-application.interface'; -export * from './nest-fastify-application.interface'; export * from './nest-microservice.interface'; export * from './on-application-bootstrap.interface'; export * from './request-mapping-metadata.interface'; diff --git a/packages/core/adapters/express-adapter.ts b/packages/core/adapters/express-adapter.ts deleted file mode 100644 index 9823ac136..000000000 --- a/packages/core/adapters/express-adapter.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { RequestMethod } from '@nestjs/common'; -import { HttpServer, RequestHandler } from '@nestjs/common/interfaces'; -import { ServeStaticOptions } from '@nestjs/common/interfaces/external/serve-static-options.interface'; -import { isNil, isObject } from '@nestjs/common/utils/shared.utils'; -import * as express from 'express'; -import { RouterMethodFactory } from '../helpers/router-method-factory'; - -export class ExpressAdapter implements HttpServer { - private readonly routerMethodFactory = new RouterMethodFactory(); - private httpServer: any = null; - - constructor(private readonly instance: any) {} - - use(...args: any[]) { - return this.instance.use(...args); - } - - get(handler: RequestHandler); - get(path: any, handler: RequestHandler); - get(...args: any[]) { - return this.instance.get(...args); - } - - post(handler: RequestHandler); - post(path: any, handler: RequestHandler); - post(...args: any[]) { - return this.instance.post(...args); - } - - head(handler: RequestHandler); - head(path: any, handler: RequestHandler); - head(...args: any[]) { - return this.instance.head(...args); - } - - delete(handler: RequestHandler); - delete(path: any, handler: RequestHandler); - delete(...args: any[]) { - return this.instance.delete(...args); - } - - put(handler: RequestHandler); - put(path: any, handler: RequestHandler); - put(...args: any[]) { - return this.instance.put(...args); - } - - patch(handler: RequestHandler); - patch(path: any, handler: RequestHandler); - patch(...args: any[]) { - return this.instance.patch(...args); - } - - options(handler: RequestHandler); - options(path: any, handler: RequestHandler); - options(...args: any[]) { - return this.instance.options(...args); - } - - listen(port: string | number, callback?: () => void); - listen(port: string | number, hostname: string, callback?: () => void); - listen(port: any, hostname?: any, callback?: any) { - return this.instance.listen(port, hostname, callback); - } - - reply(response, body: any, statusCode: number) { - const res = response.status(statusCode); - if (isNil(body)) { - return res.send(); - } - return isObject(body) ? res.json(body) : res.send(String(body)); - } - - render(response, view: string, options: any) { - return response.render(view, options); - } - - setErrorHandler(handler: Function) { - return this.use(handler); - } - - setNotFoundHandler(handler: Function) { - return this.use(handler); - } - - setHeader(response, name: string, value: string) { - return response.set(name, value); - } - - getHttpServer(): T { - return this.httpServer as T; - } - - setHttpServer(httpServer) { - this.httpServer = httpServer; - } - - getInstance(): T { - return this.instance as T; - } - - close() { - return this.instance.close(); - } - - set(...args: any[]) { - return this.instance.set(...args); - } - - enable(...args: any[]) { - return this.instance.enable(...args); - } - - disable(...args: any[]) { - return this.instance.disable(...args); - } - - engine(...args: any[]) { - return this.instance.engine(...args); - } - - useStaticAssets(path: string, options: ServeStaticOptions) { - if (options && options.prefix) { - return this.use(options.prefix, express.static(path, options)); - } - return this.use(express.static(path, options)); - } - - setBaseViewsDir(path: string) { - return this.set('views', path); - } - - setViewEngine(engine: string) { - return this.set('view engine', engine); - } - - getRequestMethod(request): string { - return request.method; - } - - getRequestUrl(request): string { - return request.url; - } - - createMiddlewareFactory( - requestMethod: RequestMethod, - ): (path: string, callback: Function) => any { - return this.routerMethodFactory - .get(this.instance, requestMethod) - .bind(this.instance); - } -} diff --git a/packages/core/adapters/express-factory.ts b/packages/core/adapters/express-factory.ts deleted file mode 100644 index c5a5cbcb1..000000000 --- a/packages/core/adapters/express-factory.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as express from 'express'; -import { ExpressAdapter } from './express-adapter'; - -export class ExpressFactory { - public static create(): any { - return new ExpressAdapter(express()); - } -} diff --git a/packages/core/adapters/http-adapter.ts b/packages/core/adapters/http-adapter.ts new file mode 100644 index 000000000..b7d04806e --- /dev/null +++ b/packages/core/adapters/http-adapter.ts @@ -0,0 +1,90 @@ +import { HttpServer, RequestMethod } from '@nestjs/common'; +import { RequestHandler } from '@nestjs/common/interfaces'; +import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; + +export abstract class AbstractHttpAdapter implements HttpServer { + protected httpServer: T; + + constructor(protected readonly instance: any) {} + + public use(...args: any[]) { + return this.instance.use(...args); + } + + public get(handler: RequestHandler); + public get(path: any, handler: RequestHandler); + public get(...args: any[]) { + return this.instance.get(...args); + } + + public post(handler: RequestHandler); + public post(path: any, handler: RequestHandler); + public post(...args: any[]) { + return this.instance.post(...args); + } + + public head(handler: RequestHandler); + public head(path: any, handler: RequestHandler); + public head(...args: any[]) { + return this.instance.head(...args); + } + + public delete(handler: RequestHandler); + public delete(path: any, handler: RequestHandler); + public delete(...args: any[]) { + return this.instance.delete(...args); + } + + public put(handler: RequestHandler); + public put(path: any, handler: RequestHandler); + public put(...args: any[]) { + return this.instance.put(...args); + } + + public patch(handler: RequestHandler); + public patch(path: any, handler: RequestHandler); + public patch(...args: any[]) { + return this.instance.patch(...args); + } + + public options(handler: RequestHandler); + public options(path: any, handler: RequestHandler); + public options(...args: any[]) { + return this.instance.options(...args); + } + + public listen(port: string | number, callback?: () => void); + public listen(port: string | number, hostname: string, callback?: () => void); + public listen(port: any, hostname?: any, callback?: any) { + return this.instance.listen(port, hostname, callback); + } + + public getHttpServer(): T { + return this.httpServer as T; + } + + public setHttpServer(httpServer: T) { + this.httpServer = httpServer; + } + + public getInstance(): any { + return this.instance; + } + + abstract close(); + abstract initHttpServer(options: NestApplicationOptions); + abstract useStaticAssets(path: string, options: any); + abstract setBaseViewsDir(path: string); + abstract setViewEngine(engine: string); + abstract getRequestMethod(request); + abstract getRequestUrl(request); + abstract reply(response, body: any, statusCode: number); + abstract render(response, view: string, options: any); + abstract setErrorHandler(handler: Function); + abstract setNotFoundHandler(handler: Function); + abstract setHeader(response, name: string, value: string); + abstract registerParserMiddleware(); + abstract createMiddlewareFactory( + requestMethod: RequestMethod, + ): (path: string, callback: Function) => any; +} diff --git a/packages/core/adapters/index.ts b/packages/core/adapters/index.ts index 6a8c5d1f2..8063987a0 100644 --- a/packages/core/adapters/index.ts +++ b/packages/core/adapters/index.ts @@ -1 +1 @@ -export * from './fastify-adapter'; \ No newline at end of file +export * from './http-adapter'; diff --git a/packages/core/exceptions/index.ts b/packages/core/exceptions/index.ts new file mode 100644 index 000000000..cc19dee05 --- /dev/null +++ b/packages/core/exceptions/index.ts @@ -0,0 +1 @@ +export * from './base-exception-filter'; diff --git a/packages/core/helpers/index.ts b/packages/core/helpers/index.ts new file mode 100644 index 000000000..52e37cb71 --- /dev/null +++ b/packages/core/helpers/index.ts @@ -0,0 +1 @@ +export * from './application-ref-host'; diff --git a/packages/core/index.ts b/packages/core/index.ts index 351e18097..295903e1d 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -8,11 +8,10 @@ import 'reflect-metadata'; export * from './adapters'; export { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants'; -export { BaseExceptionFilter } from './exceptions/base-exception-filter'; -export { ApplicationReferenceHost } from './helpers/application-ref-host'; -export { ModuleRef } from './injector/module-ref'; -export { HTTP_SERVER_REF } from './injector/tokens'; -export { MiddlewareBuilder } from './middleware/builder'; +export * from './exceptions'; +export * from './helpers'; +export * from './injector'; +export * from './middleware'; export * from './nest-application'; export * from './nest-application-context'; export { NestFactory } from './nest-factory'; diff --git a/packages/core/injector/index.ts b/packages/core/injector/index.ts index d0377ada4..6b76e079f 100644 --- a/packages/core/injector/index.ts +++ b/packages/core/injector/index.ts @@ -1,2 +1,3 @@ +export * from './module-ref'; export * from './modules-container'; export * from './tokens'; diff --git a/packages/core/middleware/index.ts b/packages/core/middleware/index.ts new file mode 100644 index 000000000..ecea700bc --- /dev/null +++ b/packages/core/middleware/index.ts @@ -0,0 +1 @@ +export * from './builder'; diff --git a/packages/core/nest-application.ts b/packages/core/nest-application.ts index b838bfa8f..1bd7f5348 100644 --- a/packages/core/nest-application.ts +++ b/packages/core/nest-application.ts @@ -9,26 +9,16 @@ import { } from '@nestjs/common'; import { HttpServer } from '@nestjs/common/interfaces'; import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface'; -import { ServeStaticOptions } from '@nestjs/common/interfaces/external/serve-static-options.interface'; import { MicroserviceOptions } from '@nestjs/common/interfaces/microservices/microservice-configuration.interface'; import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; -import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface'; -import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface'; import { Logger } from '@nestjs/common/services/logger.service'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; -import { - isFunction, - isObject, - validatePath, -} from '@nestjs/common/utils/shared.utils'; -import * as bodyParser from 'body-parser'; +import { isObject, validatePath } from '@nestjs/common/utils/shared.utils'; import * as cors from 'cors'; -import * as http from 'http'; -import * as https from 'https'; +import { Server } from 'http'; +import { Server as HttpsServer } from 'https'; import iterate from 'iterare'; import * as optional from 'optional'; -import { ExpressAdapter } from './adapters/express-adapter'; -import { FastifyAdapter } from './adapters/fastify-adapter'; import { ApplicationConfig } from './application-config'; import { MESSAGES } from './constants'; import { NestContainer } from './injector/container'; @@ -46,9 +36,7 @@ const { IoAdapter } = optional('@nestjs/websockets/adapters/io-adapter') || ({} as any); export class NestApplication extends NestApplicationContext - implements INestApplication, - INestExpressApplication, - INestFastifyApplication { + implements INestApplication { private readonly logger = new Logger(NestApplication.name, true); private readonly middlewareModule = new MiddlewareModule(); private readonly middlewareContainer = new MiddlewareContainer(); @@ -58,7 +46,7 @@ export class NestApplication extends NestApplicationContext private readonly socketModule = SocketModule ? new SocketModule() : null; private readonly routesResolver: Resolver; private readonly microservices: any[] = []; - private httpServer: http.Server; + private httpServer: Server | HttpsServer; private isInitialized = false; constructor( @@ -83,11 +71,14 @@ export class NestApplication extends NestApplicationContext public registerHttpServer() { this.httpServer = this.createServer(); - const server = this.getUnderlyingHttpServer(); - const ioAdapter = IoAdapter ? new IoAdapter(server) : null; + const ioAdapter = IoAdapter ? new IoAdapter(this.httpServer) : null; this.config.setIoAdapter(ioAdapter); } + public getUnderlyingHttpServer(): T { + return this.httpAdapter.getHttpServer(); + } + public applyOptions() { if (!this.appOptions || !this.appOptions.cors) { return undefined; @@ -100,29 +91,8 @@ export class NestApplication extends NestApplicationContext } public createServer(): any { - const isHttpsEnabled = this.appOptions && this.appOptions.httpsOptions; - const isExpress = this.isExpress(); - - if (isHttpsEnabled && isExpress) { - const server = https.createServer( - this.appOptions.httpsOptions, - this.httpAdapter.getInstance(), - ); - (this.httpAdapter as ExpressAdapter).setHttpServer(server); - return server; - } - if (isExpress) { - const server = http.createServer(this.httpAdapter.getInstance()); - (this.httpAdapter as ExpressAdapter).setHttpServer(server); - return server; - } - return this.httpAdapter; - } - - public getUnderlyingHttpServer(): any { - return this.isExpress() - ? this.httpServer - : this.httpAdapter.getHttpServer(); + this.httpAdapter.initHttpServer(this.appOptions); + return this.httpAdapter.getHttpServer(); } public async registerModules() { @@ -157,37 +127,12 @@ export class NestApplication extends NestApplicationContext } public registerParserMiddleware() { - if (this.httpAdapter instanceof FastifyAdapter) { - return this.httpAdapter.register( - this.loadPackage('fastify-formbody', 'FastifyAdapter'), - ); - } - if (!this.isExpress()) { - return undefined; - } - const parserMiddleware = { - jsonParser: bodyParser.json(), - urlencodedParser: bodyParser.urlencoded({ extended: true }), - }; - Object.keys(parserMiddleware) - .filter(parser => !this.isMiddlewareApplied(this.httpAdapter, parser)) - .forEach(parserKey => this.httpAdapter.use(parserMiddleware[parserKey])); - } - - public isMiddlewareApplied(httpAdapter: HttpServer, name: string): boolean { - const app = httpAdapter.getInstance(); - return ( - !!app._router && - !!app._router.stack && - isFunction(app._router.stack.filter) && - app._router.stack.some( - (layer: any) => layer && layer.handle && layer.handle.name === name, - ) - ); + this.httpAdapter.registerParserMiddleware(); } public async registerRouter() { await this.registerMiddleware(this.httpAdapter); + const prefix = this.config.getGlobalPrefix(); const basePath = prefix ? validatePath(prefix) : ''; this.routesResolver.resolve(this.httpAdapter, basePath); @@ -199,7 +144,7 @@ export class NestApplication extends NestApplicationContext } public connectMicroservice(options: MicroserviceOptions): INestMicroservice { - const { NestMicroservice } = loadPackage( + const { NestMicroservice } = this.loadPackage( '@nestjs/microservices', 'NestFactory', ); @@ -237,54 +182,11 @@ export class NestApplication extends NestApplicationContext return new Promise(resolve => this.startAllMicroservices(resolve)); } - public use(...args: any[]): this { - (this.httpAdapter as any).use(...args); + public use(...args: [any, any?]): this { + this.httpAdapter.use(...args); return this; } - public engine(...args: any[]): this { - if (!this.isExpress()) { - return this; - } - (this.httpAdapter as ExpressAdapter).engine(...args); - return this; - } - - public set(...args: any[]): this { - if (!this.isExpress()) { - return this; - } - (this.httpAdapter as ExpressAdapter).set(...args); - return this; - } - - public disable(...args: any[]): this { - if (!this.isExpress()) { - return this; - } - (this.httpAdapter as ExpressAdapter).disable(...args); - return this; - } - - public enable(...args: any[]): this { - if (!this.isExpress()) { - return this; - } - (this.httpAdapter as ExpressAdapter).enable(...args); - return this; - } - - public register(...args: any[]): this { - const adapter = this.httpAdapter as FastifyAdapter; - adapter.register && adapter.register(...args); - return this; - } - - public inject(...args: any[]) { - const adapter = this.httpAdapter as FastifyAdapter; - return adapter.inject && adapter.inject(...args); - } - public enableCors(options?: CorsOptions): this { this.httpAdapter.use(cors(options) as any); return this; @@ -356,11 +258,8 @@ export class NestApplication extends NestApplicationContext } public useStaticAssets(options: any): this; - public useStaticAssets(path: string, options?: ServeStaticOptions): this; - public useStaticAssets( - pathOrOptions: any, - options?: ServeStaticOptions, - ): this { + public useStaticAssets(path: string, options?: any): this; + public useStaticAssets(pathOrOptions: any, options?: any): this { this.httpAdapter.useStaticAssets && this.httpAdapter.useStaticAssets(pathOrOptions, options); return this; @@ -388,14 +287,6 @@ export class NestApplication extends NestApplicationContext ); } - private isExpress(): boolean { - const isExpress = !this.httpAdapter.getHttpServer; - if (isExpress) { - return isExpress; - } - return this.httpAdapter instanceof ExpressAdapter; - } - private listenToPromise(microservice: INestMicroservice) { return new Promise(async (resolve, reject) => { await microservice.listen(resolve); diff --git a/packages/core/nest-factory.ts b/packages/core/nest-factory.ts index 1656b2beb..5e5756ba2 100644 --- a/packages/core/nest-factory.ts +++ b/packages/core/nest-factory.ts @@ -8,14 +8,9 @@ import { MicroserviceOptions } from '@nestjs/common/interfaces/microservices/mic import { NestMicroserviceOptions } from '@nestjs/common/interfaces/microservices/nest-microservice-options.interface'; import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; -import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface'; -import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface'; import { Logger } from '@nestjs/common/services/logger.service'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; -import { ExpressAdapter } from './adapters/express-adapter'; -import { ExpressFactory } from './adapters/express-factory'; -import { FastifyAdapter } from './adapters/fastify-adapter'; import { ApplicationConfig } from './application-config'; import { MESSAGES } from './constants'; import { ExceptionsZone } from './errors/exceptions-zone'; @@ -32,42 +27,39 @@ export class NestFactoryStatic { * Creates an instance of the NestApplication * @returns {Promise} */ - public async create( + public async create( module: any, options?: NestApplicationOptions, - ): Promise; - public async create( + ): Promise; + public async create( module: any, - httpServer: FastifyAdapter, + httpServer: HttpServer, options?: NestApplicationOptions, - ): Promise; - public async create( + ): Promise; + public async create( module: any, - httpServer: HttpServer | any, + serverOrOptions?: HttpServer | NestApplicationOptions, options?: NestApplicationOptions, - ): Promise; - public async create( - module: any, - serverOrOptions?: any, - options?: NestApplicationOptions, - ): Promise< - INestApplication & (INestExpressApplication | INestFastifyApplication) - > { - const isHttpServer = serverOrOptions && serverOrOptions.patch; + ): Promise { // tslint:disable-next-line:prefer-const - let [httpServer, appOptions] = isHttpServer + let [httpServer, appOptions] = this.isHttpServer(serverOrOptions) ? [serverOrOptions, options] - : [ExpressFactory.create(), serverOrOptions]; + : [this.createHttpAdapter(), serverOrOptions]; const applicationConfig = new ApplicationConfig(); const container = new NestContainer(applicationConfig); - httpServer = this.applyExpressAdapter(httpServer); this.applyLogger(appOptions); await this.initialize(module, container, applicationConfig, httpServer); - return this.createNestInstance( - new NestApplication(container, httpServer, applicationConfig, appOptions), + + const instance = new NestApplication( + container, + httpServer, + applicationConfig, + appOptions, ); + const target = this.createAdapterProxy(instance, httpServer); + return this.createNestInstance(target); } /** @@ -159,8 +151,9 @@ export class NestFactoryStatic { private createExceptionProxy() { return (receiver: Record, prop: string) => { - if (!(prop in receiver)) return; - + if (!(prop in receiver)) { + return; + } if (isFunction(receiver[prop])) { return (...args: any[]) => { let result; @@ -181,12 +174,29 @@ export class NestFactoryStatic { !isNil(options.logger) && Logger.overrideLogger(options.logger); } - private applyExpressAdapter(httpAdapter: HttpServer): HttpServer { - const isAdapter = httpAdapter.getHttpServer; - if (isAdapter) { - return httpAdapter; - } - return new ExpressAdapter(httpAdapter); + private createHttpAdapter(httpServer?: T): HttpServer { + const { ExpressAdapter } = loadPackage( + '@nestjs/platform-express', + 'NestFactory', + ); + return new ExpressAdapter(httpServer); + } + + private isHttpServer( + serverOrOptions: HttpServer | NestApplicationOptions, + ): serverOrOptions is HttpServer { + return !!(serverOrOptions && (serverOrOptions as HttpServer).patch); + } + + private createAdapterProxy(app: NestApplication, adapter: HttpServer): T { + return (new Proxy(app, { + get: (receiver: Record, prop: string) => { + if (!(prop in receiver) && prop in adapter) { + return adapter[prop]; + } + return receiver[prop]; + }, + }) as any) as T; } } diff --git a/packages/core/package.json b/packages/core/package.json index 5f08d82b4..e03ee9a87 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,9 +20,7 @@ }, "dependencies": { "@nuxtjs/opencollective": "0.1.0", - "body-parser": "1.18.3", "cors": "2.8.4", - "express": "4.16.3", "fast-safe-stringify": "1.2.0", "iterare": "0.0.8", "object-hash": "1.3.0", diff --git a/packages/platform-express/Readme.md b/packages/platform-express/Readme.md new file mode 100644 index 000000000..2b9c99ca1 --- /dev/null +++ b/packages/platform-express/Readme.md @@ -0,0 +1,76 @@ +

+ Nest Logo +

+ +[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master +[travis-url]: https://travis-ci.org/nestjs/nest +[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux +[linux-url]: https://travis-ci.org/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

+

+NPM Version +Package License +NPM Downloads +Travis +Linux +Coverage +Gitter +Backers on Open Collective +Sponsors on Open Collective + + +

+ + +## Description + +

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses modern JavaScript, is built with TypeScript (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

+ +

Under the hood, Nest makes use of Express, but also, provides compatibility with a wide range of other libraries, like e.g. Fastify, allowing for easy use of the myriad third-party plugins which are available.

+ +## Philosophy + +

In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like Angular, React and Vue which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.

+

Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications.

+ +## Getting started + +* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books: +* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books: + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +#### Principal Sponsor + + + +#### Gold Sponsors + + + +#### Silver Sponsors +   +   + +#### Sponsors + +           + + +## Backers + + + +## Stay in touch + +* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) +* Website - [https://nestjs.com](https://nestjs.com/) +* Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](LICENSE). diff --git a/packages/platform-express/adapters/express-adapter.ts b/packages/platform-express/adapters/express-adapter.ts new file mode 100644 index 000000000..9e842075d --- /dev/null +++ b/packages/platform-express/adapters/express-adapter.ts @@ -0,0 +1,123 @@ +import { RequestMethod } from '@nestjs/common'; +import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; +import { isFunction, isNil, isObject } from '@nestjs/common/utils/shared.utils'; +import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter'; +import { RouterMethodFactory } from '@nestjs/core/helpers/router-method-factory'; +import * as bodyParser from 'body-parser'; +import * as express from 'express'; +import * as http from 'http'; +import * as https from 'https'; +import { ServeStaticOptions } from './../interfaces/serve-static-options.interface'; + +export class ExpressAdapter extends AbstractHttpAdapter { + private readonly routerMethodFactory = new RouterMethodFactory(); + + public reply(response, body: any, statusCode: number) { + const res = response.status(statusCode); + if (isNil(body)) { + return res.send(); + } + return isObject(body) ? res.json(body) : res.send(String(body)); + } + + public render(response, view: string, options: any) { + return response.render(view, options); + } + + public setErrorHandler(handler: Function) { + return this.use(handler); + } + + public setNotFoundHandler(handler: Function) { + return this.use(handler); + } + + public setHeader(response, name: string, value: string) { + return response.set(name, value); + } + + public close() { + return this.instance.close(); + } + + public set(...args: any[]) { + return this.instance.set(...args); + } + + public enable(...args: any[]) { + return this.instance.enable(...args); + } + + public disable(...args: any[]) { + return this.instance.disable(...args); + } + + public engine(...args: any[]) { + return this.instance.engine(...args); + } + + public useStaticAssets(path: string, options: ServeStaticOptions) { + if (options && options.prefix) { + return this.use(options.prefix, express.static(path, options)); + } + return this.use(express.static(path, options)); + } + + public setBaseViewsDir(path: string) { + return this.set('views', path); + } + + public setViewEngine(engine: string) { + return this.set('view engine', engine); + } + + public getRequestMethod(request): string { + return request.method; + } + + public getRequestUrl(request): string { + return request.url; + } + + public createMiddlewareFactory( + requestMethod: RequestMethod, + ): (path: string, callback: Function) => any { + return this.routerMethodFactory + .get(this.instance, requestMethod) + .bind(this.instance); + } + + public initHttpServer(options: NestApplicationOptions) { + const isHttpsEnabled = options && options.httpsOptions; + if (isHttpsEnabled) { + this.httpServer = https.createServer( + options.httpsOptions, + this.getInstance(), + ); + return; + } + this.httpServer = http.createServer(this.getInstance()); + } + + public registerParserMiddleware() { + const parserMiddleware = { + jsonParser: bodyParser.json(), + urlencodedParser: bodyParser.urlencoded({ extended: true }), + }; + Object.keys(parserMiddleware) + .filter(parser => !this.isMiddlewareApplied(parser)) + .forEach(parserKey => this.use(parserMiddleware[parserKey])); + } + + private isMiddlewareApplied(name: string); : boolean; { + const app = this.getInstance(); + return ( + !!app._router && + !!app._router.stack && + isFunction(app._router.stack.filter) && + app._router.stack.some( + (layer: any) => layer && layer.handle && layer.handle.name === name, + ) + ); + } +} diff --git a/packages/platform-express/adapters/index.ts b/packages/platform-express/adapters/index.ts new file mode 100644 index 000000000..3644487c6 --- /dev/null +++ b/packages/platform-express/adapters/index.ts @@ -0,0 +1 @@ +export * from './express-adapter'; diff --git a/packages/platform-express/index.ts b/packages/platform-express/index.ts new file mode 100644 index 000000000..97f7bfe13 --- /dev/null +++ b/packages/platform-express/index.ts @@ -0,0 +1,9 @@ +/* + * Nest @platform-express + * Copyright(c) 2017 - 2018 Kamil Mysliwiec + * https://nestjs.com + * MIT Licensed + */ + +export * from './adapters'; +export * from './interfaces'; diff --git a/packages/platform-express/interceptors/index.ts b/packages/platform-express/interceptors/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/platform-express/interfaces/index.ts b/packages/platform-express/interfaces/index.ts new file mode 100644 index 000000000..3238b1c16 --- /dev/null +++ b/packages/platform-express/interfaces/index.ts @@ -0,0 +1 @@ +export * from './nest-express-application.interface'; diff --git a/packages/common/interfaces/nest-express-application.interface.ts b/packages/platform-express/interfaces/nest-express-application.interface.ts similarity index 91% rename from packages/common/interfaces/nest-express-application.interface.ts rename to packages/platform-express/interfaces/nest-express-application.interface.ts index ed0f48ab8..7f7a5e10a 100644 --- a/packages/common/interfaces/nest-express-application.interface.ts +++ b/packages/platform-express/interfaces/nest-express-application.interface.ts @@ -1,4 +1,4 @@ -import { ServeStaticOptions } from './external/serve-static-options.interface'; +import { ServeStaticOptions } from './serve-static-options.interface'; export interface INestExpressApplication { /** @@ -39,7 +39,7 @@ export interface INestExpressApplication { * * @returns {this} */ - useStaticAssets(options: any): this; + useStaticAssets(options: ServeStaticOptions): this; useStaticAssets(path: string, options?: ServeStaticOptions): this; /** diff --git a/packages/common/interfaces/external/serve-static-options.interface.ts b/packages/platform-express/interfaces/serve-static-options.interface.ts similarity index 100% rename from packages/common/interfaces/external/serve-static-options.interface.ts rename to packages/platform-express/interfaces/serve-static-options.interface.ts diff --git a/packages/platform-express/package.json b/packages/platform-express/package.json new file mode 100644 index 000000000..14f82eb33 --- /dev/null +++ b/packages/platform-express/package.json @@ -0,0 +1,18 @@ +{ + "name": "@nestjs/platform-express", + "version": "5.4.1", + "description": + "Nest - modern, fast, powerful node.js web framework (@platform-express)", + "author": "Kamil Mysliwiec", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/nestjs/nest" + }, + "dependencies": { + "@types/express": "^4.0.39", + "body-parser": "1.18.3", + "express": "4.16.3" + }, + "peerDependencies": {} +} diff --git a/packages/platform-express/tsconfig.json b/packages/platform-express/tsconfig.json new file mode 100644 index 000000000..b238080a4 --- /dev/null +++ b/packages/platform-express/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["*.ts", "**/*.ts"] +} diff --git a/packages/platform-fastify/Readme.md b/packages/platform-fastify/Readme.md new file mode 100644 index 000000000..2b9c99ca1 --- /dev/null +++ b/packages/platform-fastify/Readme.md @@ -0,0 +1,76 @@ +

+ Nest Logo +

+ +[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master +[travis-url]: https://travis-ci.org/nestjs/nest +[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux +[linux-url]: https://travis-ci.org/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

+

+NPM Version +Package License +NPM Downloads +Travis +Linux +Coverage +Gitter +Backers on Open Collective +Sponsors on Open Collective + + +

+ + +## Description + +

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses modern JavaScript, is built with TypeScript (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

+ +

Under the hood, Nest makes use of Express, but also, provides compatibility with a wide range of other libraries, like e.g. Fastify, allowing for easy use of the myriad third-party plugins which are available.

+ +## Philosophy + +

In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like Angular, React and Vue which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.

+

Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications.

+ +## Getting started + +* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books: +* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books: + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +#### Principal Sponsor + + + +#### Gold Sponsors + + + +#### Silver Sponsors +   +   + +#### Sponsors + +           + + +## Backers + + + +## Stay in touch + +* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) +* Website - [https://nestjs.com](https://nestjs.com/) +* Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](LICENSE). diff --git a/packages/core/adapters/fastify-adapter.ts b/packages/platform-fastify/adapters/fastify-adapter.ts similarity index 91% rename from packages/core/adapters/fastify-adapter.ts rename to packages/platform-fastify/adapters/fastify-adapter.ts index b26b3988b..4e521f8df 100644 --- a/packages/core/adapters/fastify-adapter.ts +++ b/packages/platform-fastify/adapters/fastify-adapter.ts @@ -1,13 +1,16 @@ import { RequestMethod } from '@nestjs/common'; import { ErrorHandler, RequestHandler } from '@nestjs/common/interfaces'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; +import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter'; import * as pathToRegexp from 'path-to-regexp'; -export class FastifyAdapter { - protected readonly instance: any; - +export class FastifyAdapter extends AbstractHttpAdapter { + setBaseViewsDir(path: string) {} + registerParserMiddleware() { + this.register(loadPackage('fastify-formbody', 'FastifyAdapter')); + } constructor(options?: any) { - this.instance = loadPackage('fastify', 'FastifyAdapter')(options); + super(loadPackage('fastify', 'FastifyAdapter')(options)); } use(handler: RequestHandler | ErrorHandler); diff --git a/packages/platform-fastify/adapters/index.ts b/packages/platform-fastify/adapters/index.ts new file mode 100644 index 000000000..109d506ff --- /dev/null +++ b/packages/platform-fastify/adapters/index.ts @@ -0,0 +1 @@ +export * from './fastify-adapter'; diff --git a/packages/platform-fastify/index.ts b/packages/platform-fastify/index.ts new file mode 100644 index 000000000..97f7bfe13 --- /dev/null +++ b/packages/platform-fastify/index.ts @@ -0,0 +1,9 @@ +/* + * Nest @platform-express + * Copyright(c) 2017 - 2018 Kamil Mysliwiec + * https://nestjs.com + * MIT Licensed + */ + +export * from './adapters'; +export * from './interfaces'; diff --git a/packages/platform-fastify/interceptors/index.ts b/packages/platform-fastify/interceptors/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/platform-fastify/interfaces/index.ts b/packages/platform-fastify/interfaces/index.ts new file mode 100644 index 000000000..3238b1c16 --- /dev/null +++ b/packages/platform-fastify/interfaces/index.ts @@ -0,0 +1 @@ +export * from './nest-express-application.interface'; diff --git a/packages/common/interfaces/nest-fastify-application.interface.ts b/packages/platform-fastify/interfaces/nest-fastify-application.interface.ts similarity index 100% rename from packages/common/interfaces/nest-fastify-application.interface.ts rename to packages/platform-fastify/interfaces/nest-fastify-application.interface.ts diff --git a/packages/platform-fastify/package.json b/packages/platform-fastify/package.json new file mode 100644 index 000000000..6786a370c --- /dev/null +++ b/packages/platform-fastify/package.json @@ -0,0 +1,18 @@ +{ + "name": "@nestjs/platform-fastify", + "version": "5.4.1", + "description": + "Nest - modern, fast, powerful node.js web framework (@platform-fastify)", + "author": "Kamil Mysliwiec", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/nestjs/nest" + }, + "dependencies": { + "@types/express": "^4.0.39", + "body-parser": "1.18.3", + "express": "4.16.3" + }, + "peerDependencies": {} +} diff --git a/packages/platform-fastify/tsconfig.json b/packages/platform-fastify/tsconfig.json new file mode 100644 index 000000000..b238080a4 --- /dev/null +++ b/packages/platform-fastify/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["*.ts", "**/*.ts"] +} diff --git a/packages/testing/testing-module.ts b/packages/testing/testing-module.ts index 5e6c45f24..e3d983e6a 100644 --- a/packages/testing/testing-module.ts +++ b/packages/testing/testing-module.ts @@ -8,14 +8,9 @@ import { MicroserviceOptions } from '@nestjs/common/interfaces/microservices/mic import { NestMicroserviceOptions } from '@nestjs/common/interfaces/microservices/nest-microservice-options.interface'; import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface'; import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface'; -import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface'; -import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface'; import { Type } from '@nestjs/common/interfaces/type.interface'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { NestApplication, NestApplicationContext } from '@nestjs/core'; -import { ExpressAdapter } from '@nestjs/core/adapters/express-adapter'; -import { ExpressFactory } from '@nestjs/core/adapters/express-factory'; -import { FastifyAdapter } from '@nestjs/core/adapters/fastify-adapter'; import { ApplicationConfig } from '@nestjs/core/application-config'; import { NestContainer } from '@nestjs/core/injector/container'; import { Module } from '@nestjs/core/injector/module'; @@ -30,33 +25,22 @@ export class TestingModule extends NestApplicationContext { super(container, scope, contextModule); } - public createNestApplication( - httpServer?: HttpServer, + public createNestApplication( + httpAdapter?: HttpServer, options?: NestApplicationOptions, - ): INestApplication & INestExpressApplication; - public createNestApplication( - httpServer?: FastifyAdapter, - options?: NestApplicationOptions, - ): INestApplication & INestFastifyApplication; - public createNestApplication( - httpServer?: any, - options?: NestApplicationOptions, - ): INestApplication & INestExpressApplication; - public createNestApplication( - httpServer: any = ExpressFactory.create(), - options?: NestApplicationOptions, - ): INestApplication & (INestExpressApplication | INestFastifyApplication) { - httpServer = this.applyExpressAdapter(httpServer); + ): T { + httpAdapter = httpAdapter || this.createHttpAdapter(); this.applyLogger(options); - this.container.setApplicationRef(httpServer); + this.container.setApplicationRef(httpAdapter); - return new NestApplication( + const instance = new NestApplication( this.container, - httpServer, + httpAdapter, this.applicationConfig, options, ); + return this.createAdapterProxy(instance, httpAdapter); } public createNestMicroservice( @@ -74,20 +58,29 @@ export class TestingModule extends NestApplicationContext { ); } - private applyExpressAdapter(httpAdapter: HttpServer): HttpServer { - const isAdapter = httpAdapter.getHttpServer; - if (isAdapter) { - return httpAdapter; - } - return new ExpressAdapter(httpAdapter); + private createHttpAdapter(httpServer?: T): HttpServer { + const { ExpressAdapter } = loadPackage( + '@nestjs/platform-express', + 'NestFactory', + ); + return new ExpressAdapter(httpServer); } - private applyLogger( - options: NestApplicationContextOptions | undefined, - ): void { + private applyLogger(options: NestApplicationContextOptions | undefined) { if (!options || !options.logger) { - return undefined; + return; } Logger.overrideLogger(options.logger); } + + private createAdapterProxy(app: NestApplication, adapter: HttpServer): T { + return (new Proxy(app, { + get: (receiver: Record, prop: string) => { + if (!(prop in receiver) && prop in adapter) { + return adapter[prop]; + } + return receiver[prop]; + }, + }) as any) as T; + } }