feature(core/common) extract HTTP adapters (express/fastify)

This commit is contained in:
Kamil Myśliwiec
2018-11-30 16:48:04 +01:00
parent ead8d55538
commit bc4a8a556c
35 changed files with 548 additions and 374 deletions

View File

@@ -23,8 +23,6 @@ export {
HttpServer,
INestApplication,
INestApplicationContext,
INestExpressApplication,
INestFastifyApplication,
INestMicroservice,
MiddlewareConsumer,
MiddlewareFunction,

View File

@@ -1,4 +1,5 @@
import { RequestMethod } from '../../enums';
import { NestApplicationOptions } from './../../interfaces/nest-application-options.interface';
export type ErrorHandler<TRequest = any, TResponse = any> = (
error: any,
@@ -54,6 +55,8 @@ export interface HttpServer<TRequest = any, TResponse = any> {
getRequestMethod?(request: TRequest): string;
getRequestUrl?(request: TResponse): string;
getInstance(): any;
registerParserMiddleware(): any;
getHttpServer(): any;
initHttpServer(options: NestApplicationOptions): void;
close(): any;
}

View File

@@ -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';

View File

@@ -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 = any>(): T {
return this.httpServer as T;
}
setHttpServer(httpServer) {
this.httpServer = httpServer;
}
getInstance<T = any>(): 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);
}
}

View File

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

View File

@@ -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<T = any> 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;
}

View File

@@ -1 +1 @@
export * from './fastify-adapter';
export * from './http-adapter';

View File

@@ -0,0 +1 @@
export * from './base-exception-filter';

View File

@@ -0,0 +1 @@
export * from './application-ref-host';

View File

@@ -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';

View File

@@ -1,2 +1,3 @@
export * from './module-ref';
export * from './modules-container';
export * from './tokens';

View File

@@ -0,0 +1 @@
export * from './builder';

View File

@@ -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>(): 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);

View File

@@ -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<T extends INestApplication = INestApplication>(
module: any,
options?: NestApplicationOptions,
): Promise<INestApplication & INestExpressApplication>;
public async create(
): Promise<T>;
public async create<T extends INestApplication = INestApplication>(
module: any,
httpServer: FastifyAdapter,
httpServer: HttpServer,
options?: NestApplicationOptions,
): Promise<INestApplication & INestFastifyApplication>;
public async create(
): Promise<T>;
public async create<T extends INestApplication = INestApplication>(
module: any,
httpServer: HttpServer | any,
serverOrOptions?: HttpServer | NestApplicationOptions,
options?: NestApplicationOptions,
): Promise<INestApplication & INestExpressApplication>;
public async create(
module: any,
serverOrOptions?: any,
options?: NestApplicationOptions,
): Promise<
INestApplication & (INestExpressApplication | INestFastifyApplication)
> {
const isHttpServer = serverOrOptions && serverOrOptions.patch;
): Promise<T> {
// 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<NestApplication>(
new NestApplication(container, httpServer, applicationConfig, appOptions),
const instance = new NestApplication(
container,
httpServer,
applicationConfig,
appOptions,
);
const target = this.createAdapterProxy<T>(instance, httpServer);
return this.createNestInstance<T>(target);
}
/**
@@ -159,8 +151,9 @@ export class NestFactoryStatic {
private createExceptionProxy() {
return (receiver: Record<string, any>, 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<T = any>(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<T>(app: NestApplication, adapter: HttpServer): T {
return (new Proxy(app, {
get: (receiver: Record<string, any>, prop: string) => {
if (!(prop in receiver) && prop in adapter) {
return adapter[prop];
}
return receiver[prop];
},
}) as any) as T;
}
}

View File

@@ -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",

View File

@@ -0,0 +1,76 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>
[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
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
<p>Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).</p>
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
## Philosophy
<p>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 <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> 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.</p>
<p>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.</p>
## 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
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
#### Gold Sponsors
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="220" /></a>
#### Silver Sponsors
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" /></a> &nbsp;
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> &nbsp; <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a>
#### Sponsors
<a href="https://scal.io"><img src="https://nestjs.com/img/scalio-logo.svg" width="110" /></a> &nbsp; <a href="http://angularity.io"><img src="http://angularity.io/media/logo.svg" height="30" /></a> &nbsp; <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> &nbsp;--> <a href="https://hostpresto.com"><img src="https://nestjs.com/img/hostpresto.png" height="30" /></a> &nbsp; <a href="https://genuinebee.com/"><img src="https://nestjs.com/img/genuinebee.svg" height="38" /></a> &nbsp; <a href="http://architectnow.net/"><img src="https://nestjs.com/img/architectnow.png" height="24" /></a> &nbsp; <a href="https://quander.io/"><img src="https://nestjs.com/img/quander.png" height="28" /></a>
## Backers
<a href="https://opencollective.com/nest"><img src="https://opencollective.com/nest/backers.svg?width=890"></a>
## 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).

View File

@@ -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,
)
);
}
}

View File

@@ -0,0 +1 @@
export * from './express-adapter';

View File

@@ -0,0 +1,9 @@
/*
* Nest @platform-express
* Copyright(c) 2017 - 2018 Kamil Mysliwiec
* https://nestjs.com
* MIT Licensed
*/
export * from './adapters';
export * from './interfaces';

View File

@@ -0,0 +1 @@
export * from './nest-express-application.interface';

View File

@@ -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;
/**

View File

@@ -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": {}
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./"
},
"include": ["*.ts", "**/*.ts"]
}

View File

@@ -0,0 +1,76 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>
[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
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
<p>Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).</p>
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
## Philosophy
<p>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 <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> 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.</p>
<p>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.</p>
## 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
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
#### Gold Sponsors
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="220" /></a>
#### Silver Sponsors
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" /></a> &nbsp;
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> &nbsp; <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a>
#### Sponsors
<a href="https://scal.io"><img src="https://nestjs.com/img/scalio-logo.svg" width="110" /></a> &nbsp; <a href="http://angularity.io"><img src="http://angularity.io/media/logo.svg" height="30" /></a> &nbsp; <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> &nbsp;--> <a href="https://hostpresto.com"><img src="https://nestjs.com/img/hostpresto.png" height="30" /></a> &nbsp; <a href="https://genuinebee.com/"><img src="https://nestjs.com/img/genuinebee.svg" height="38" /></a> &nbsp; <a href="http://architectnow.net/"><img src="https://nestjs.com/img/architectnow.png" height="24" /></a> &nbsp; <a href="https://quander.io/"><img src="https://nestjs.com/img/quander.png" height="28" /></a>
## Backers
<a href="https://opencollective.com/nest"><img src="https://opencollective.com/nest/backers.svg?width=890"></a>
## 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).

View File

@@ -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);

View File

@@ -0,0 +1 @@
export * from './fastify-adapter';

View File

@@ -0,0 +1,9 @@
/*
* Nest @platform-express
* Copyright(c) 2017 - 2018 Kamil Mysliwiec
* https://nestjs.com
* MIT Licensed
*/
export * from './adapters';
export * from './interfaces';

View File

@@ -0,0 +1 @@
export * from './nest-express-application.interface';

View File

@@ -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": {}
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./"
},
"include": ["*.ts", "**/*.ts"]
}

View File

@@ -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<T extends INestApplication = INestApplication>(
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<T>(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<T = any>(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<T>(app: NestApplication, adapter: HttpServer): T {
return (new Proxy(app, {
get: (receiver: Record<string, any>, prop: string) => {
if (!(prop in receiver) && prop in adapter) {
return adapter[prop];
}
return receiver[prop];
},
}) as any) as T;
}
}