├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── karma.conf.js ├── package-lock.json ├── package.json ├── server.ts ├── server ├── app.controller.ts ├── app.module.ts └── main.ts ├── src ├── app │ ├── app.component.ts │ ├── app.module.ts │ ├── app.server.module.ts │ ├── components │ │ └── home │ │ │ └── home.component.ts │ ├── interfaces │ │ └── speakers.interface.ts │ └── modules │ │ ├── shared │ │ ├── interceptors │ │ │ └── universal-interceptor.service.ts │ │ └── shared.module.ts │ │ └── speakers │ │ ├── speakers-list │ │ ├── speakers-list.component.ts │ │ └── speakers.service.ts │ │ └── speakers.module.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.server.ts ├── main.ts ├── polyfills.ts └── styles.css ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.server.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | .vscode 4 | 5 | # Built # 6 | /__build__/ 7 | /__server_build__/ 8 | /node_modules/ 9 | /typings/ 10 | /tsd_typings/ 11 | /dist/ 12 | /dist-server/ 13 | /compiled/ 14 | 15 | # Node # 16 | *.log 17 | /npm-debug.log.* 18 | 19 | # Webpack # 20 | webpack.records.json 21 | 22 | # Angular # 23 | *.ngfactory.ts 24 | *.css.shim.ts 25 | *.ngsummary.json 26 | *.metadata.json 27 | *.shim.ngstyle.ts 28 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nest & Angular Universal Starter 2 | 3 | A minimal [**Nest**](https://github.com/nestjs/nest) and Angular starter for Universal using the 4 | [Angular CLI](https://github.com/angular/angular-cli). If you're looking for the Angular Universal repo go to 5 | [angular/universal](https://github.com/angular/universal). 6 | 7 | --- 8 | 9 |

10 | 11 | Trilon.io - Angular Universal, NestJS, JavaScript Application Consulting Development and Training 12 | 13 |

14 | 15 | 16 |

Made with :heart: by Trilon.io

17 | 18 | --- 19 | 20 | ## Getting Started 21 | 22 | This demo is built following the [Angular-CLI Wiki guide](https://github.com/angular/angular-cli/wiki/stories-universal-rendering). 23 | 24 | ### Installation 25 | 26 | - `npm i` 27 | 28 | ### Development (Client-side only rendering) 29 | 30 | - `npm start` which will run `ng serve`. 31 | 32 | ### Development (Server-side rendering) 33 | 34 | - `npm run dev:ssr`. 35 | 36 | ### Production 37 | 38 | \*`npm run build:ssr && npm run serve:ssr` 39 | 40 | - Compiles your application and spins up a Nest server to serve your Universal application on `http://localhost:4000`. 41 | 42 | \*`npm run prerender` 43 | 44 | - Compiles your application and prerenders your applications files 45 | 46 | # License 47 | 48 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](/LICENSE) 49 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "universal-starter-v9": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/universal-starter-v9/browser", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "aot": true, 26 | "assets": ["src/favicon.ico", "src/assets"], 27 | "styles": ["src/styles.css"], 28 | "scripts": [] 29 | }, 30 | "configurations": { 31 | "production": { 32 | "fileReplacements": [ 33 | { 34 | "replace": "src/environments/environment.ts", 35 | "with": "src/environments/environment.prod.ts" 36 | } 37 | ], 38 | "optimization": true, 39 | "outputHashing": "all", 40 | "sourceMap": false, 41 | "extractCss": true, 42 | "namedChunks": false, 43 | "extractLicenses": true, 44 | "vendorChunk": false, 45 | "buildOptimizer": true, 46 | "budgets": [ 47 | { 48 | "type": "initial", 49 | "maximumWarning": "2mb", 50 | "maximumError": "5mb" 51 | }, 52 | { 53 | "type": "anyComponentStyle", 54 | "maximumWarning": "6kb", 55 | "maximumError": "10kb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-devkit/build-angular:dev-server", 63 | "options": { 64 | "browserTarget": "universal-starter-v9:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "universal-starter-v9:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "universal-starter-v9:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": ["src/favicon.ico", "src/assets"], 86 | "styles": ["src/styles.css"], 87 | "scripts": [] 88 | } 89 | }, 90 | "lint": { 91 | "builder": "@angular-devkit/build-angular:tslint", 92 | "options": { 93 | "tsConfig": [ 94 | "tsconfig.app.json", 95 | "tsconfig.spec.json", 96 | "e2e/tsconfig.json" 97 | ], 98 | "exclude": ["**/node_modules/**"] 99 | } 100 | }, 101 | "e2e": { 102 | "builder": "@angular-devkit/build-angular:protractor", 103 | "options": { 104 | "protractorConfig": "e2e/protractor.conf.js", 105 | "devServerTarget": "universal-starter-v9:serve" 106 | }, 107 | "configurations": { 108 | "production": { 109 | "devServerTarget": "universal-starter-v9:serve:production" 110 | } 111 | } 112 | }, 113 | "server": { 114 | "builder": "@angular-devkit/build-angular:server", 115 | "options": { 116 | "outputPath": "dist/universal-starter-v9/server", 117 | "main": "server.ts", 118 | "tsConfig": "tsconfig.server.json", 119 | "externalDependencies": [ 120 | "@nestjs/microservices", 121 | "@nestjs/microservices/microservices-module", 122 | "@nestjs/websockets", 123 | "@nestjs/websockets/socket-module", 124 | "cache-manager" 125 | ] 126 | }, 127 | "configurations": { 128 | "production": { 129 | "outputHashing": "media", 130 | "fileReplacements": [ 131 | { 132 | "replace": "src/environments/environment.ts", 133 | "with": "src/environments/environment.prod.ts" 134 | } 135 | ], 136 | "sourceMap": false, 137 | "optimization": false 138 | } 139 | } 140 | }, 141 | "serve-ssr": { 142 | "builder": "@nguniversal/builders:ssr-dev-server", 143 | "options": { 144 | "browserTarget": "universal-starter-v9:build", 145 | "serverTarget": "universal-starter-v9:server" 146 | }, 147 | "configurations": { 148 | "production": { 149 | "browserTarget": "universal-starter-v9:build:production", 150 | "serverTarget": "universal-starter-v9:server:production" 151 | } 152 | } 153 | }, 154 | "prerender": { 155 | "builder": "@nguniversal/builders:prerender", 156 | "options": { 157 | "browserTarget": "universal-starter-v9:build:production", 158 | "serverTarget": "universal-starter-v9:server:production", 159 | "routes": ["/"] 160 | }, 161 | "configurations": { 162 | "production": {} 163 | } 164 | } 165 | } 166 | } 167 | }, 168 | "defaultProject": "universal-starter-v9", 169 | "cli": { 170 | "analytics": false 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/universal-starter-v9'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "universal-starter-v9", 3 | "version": "0.1.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/angular/universal-starter.git" 8 | }, 9 | "contributors": [ 10 | "AngularClass ", 11 | "PatrickJS ", 12 | "Jeff Whelpley ", 13 | "Jeff Cross ", 14 | "Mark Pieszak ", 15 | "Jason Jean ", 16 | "Fabian Wiles ", 17 | "Kamil Mysliwiec ", 18 | "Michael Prentice " 19 | ], 20 | "scripts": { 21 | "ng": "ng", 22 | "start": "ng serve", 23 | "build": "ng build", 24 | "test": "ng test", 25 | "lint": "ng lint", 26 | "e2e": "ng e2e", 27 | "dev:ssr": "ng run universal-starter-v9:serve-ssr", 28 | "serve:ssr": "node dist/universal-starter-v9/server/main.js", 29 | "prebuild:ssr": "ngcc", 30 | "build:ssr": "ng build --prod && ng run universal-starter-v9:server:production", 31 | "prerender": "ng run universal-starter-v9:prerender" 32 | }, 33 | "pre-commit": [], 34 | "private": true, 35 | "dependencies": { 36 | "@angular/animations": "~9.0.0", 37 | "@angular/common": "~9.0.0", 38 | "@angular/compiler": "~9.0.0", 39 | "@angular/core": "~9.0.0", 40 | "@angular/forms": "~9.0.0", 41 | "@angular/platform-browser": "~9.0.0", 42 | "@angular/platform-browser-dynamic": "~9.0.0", 43 | "@angular/platform-server": "~9.0.0", 44 | "@angular/router": "~9.0.0", 45 | "@nestjs/common": "^6.11.6", 46 | "@nestjs/core": "^6.11.6", 47 | "@nestjs/ng-universal": "^3.0.0", 48 | "@nestjs/platform-express": "^6.11.6", 49 | "@nguniversal/common": "9.0.0", 50 | "@nguniversal/express-engine": "9.0.0", 51 | "class-transformer": "^0.2.3", 52 | "class-validator": "^0.9.1", 53 | "classlist.js": "1.1.20150312", 54 | "core-js": "^2.6.5", 55 | "rxjs": "~6.5.2", 56 | "terser": "^4.1.2", 57 | "tsconfig-paths": "^3.8.0", 58 | "tslib": "^1.10.0", 59 | "webpack-cli": "3.3.6", 60 | "zone.js": "~0.9.1" 61 | }, 62 | "devDependencies": { 63 | "@angular-devkit/build-angular": "~0.900.1", 64 | "@angular/cli": "~9.0.1", 65 | "@angular/compiler-cli": "~9.0.0", 66 | "@angular/language-service": "~9.0.0", 67 | "@nguniversal/builders": "^9.0.0", 68 | "@types/jasmine": "~3.5.3", 69 | "@types/jasminewd2": "~2.0.8", 70 | "@types/node": "~13.7.0", 71 | "codelyzer": "^5.2.1", 72 | "cpy-cli": "2.0.0", 73 | "express": "4.17.1", 74 | "http-server": "0.12.1", 75 | "jasmine-core": "~3.4.0", 76 | "jasmine-spec-reporter": "~4.2.1", 77 | "karma": "~4.4.1", 78 | "karma-chrome-launcher": "~3.1.0", 79 | "karma-coverage-istanbul-reporter": "~2.1.1", 80 | "karma-jasmine": "~3.1.0", 81 | "karma-jasmine-html-reporter": "^1.5.2", 82 | "mini-css-extract-plugin": "^0.9.0", 83 | "nodemon": "^2.0.2", 84 | "pre-commit": "1.2.2", 85 | "protractor": "~5.4.3", 86 | "reflect-metadata": "0.1.13", 87 | "ts-loader": "6.2.1", 88 | "ts-node": "~8.6.2", 89 | "tslint": "~6.0.0", 90 | "typescript": "^3.7.5", 91 | "wait-on": "^4.0.0" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/dist/zone-node'; 2 | import './server/main'; 3 | export * from './src/main.server'; 4 | -------------------------------------------------------------------------------- /server/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | 3 | @Controller() 4 | export class AppController { 5 | @Get('api/speakers') 6 | findAllSpeakers(): any[] { 7 | return [ 8 | { 9 | name: 'Name Dudeman', 10 | talk: 'Angular for your face', 11 | image: 'http://via.placeholder.com/50x50' 12 | }, 13 | { 14 | name: 'Some Person', 15 | talk: 'High-five typescript', 16 | image: 'http://via.placeholder.com/50x50' 17 | }, 18 | { 19 | name: 'Samwise Gamgee', 20 | talk: 'Lord of the Angular', 21 | image: 'http://via.placeholder.com/50x50' 22 | } 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AngularUniversalModule } from '@nestjs/ng-universal'; 3 | import { join } from 'path'; 4 | import { AppServerModule } from '../src/main.server'; 5 | import { AppController } from './app.controller'; 6 | 7 | @Module({ 8 | imports: [ 9 | AngularUniversalModule.forRoot({ 10 | bootstrap: AppServerModule, 11 | viewsPath: join(process.cwd(), 'dist/universal-starter-v9/browser') 12 | }) 13 | ], 14 | controllers: [AppController] 15 | }) 16 | export class ApplicationModule {} 17 | -------------------------------------------------------------------------------- /server/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { ApplicationModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(ApplicationModule); 6 | app.enableCors({ 7 | methods: 'GET', 8 | maxAge: 3600 9 | }); 10 | await app.listen(process.env.PORT || 4000); 11 | } 12 | 13 | // Webpack will replace 'require' with '__webpack_require__' 14 | // '__non_webpack_require__' is a proxy to Node 'require' 15 | // The below code is to ensure that the server is run only when not requiring the bundle. 16 | declare const __non_webpack_require__: NodeRequire; 17 | const mainModule = __non_webpack_require__.main; 18 | const moduleFilename = (mainModule && mainModule.filename) || ''; 19 | if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { 20 | bootstrap().catch(err => console.error(err)); 21 | } 22 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 |
7 |

NestJS + Angular Universal

8 | 9 | 13 |
14 | 15 |
16 |
17 | `, 18 | styles: [ 19 | ` 20 | #navigation { 21 | margin: 20px 0; 22 | font-weight: bold; 23 | font-size: 18px; 24 | } 25 | #navigation a { 26 | color: #5279a4; 27 | margin-right: 20px; 28 | } 29 | #outlet-wrapper { 30 | padding: 20px; 31 | border: 1px #ccc solid; 32 | } 33 | ` 34 | ] 35 | }) 36 | export class AppComponent {} 37 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule } from '@angular/common/http'; 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { RouterModule } from '@angular/router'; 5 | import { TransferHttpCacheModule } from '@nguniversal/common'; 6 | import { AppComponent } from './app.component'; 7 | import { HomeComponent } from './components/home/home.component'; 8 | import { SharedModule } from './modules/shared/shared.module'; 9 | 10 | @NgModule({ 11 | declarations: [AppComponent, HomeComponent], 12 | imports: [ 13 | HttpClientModule, 14 | // Add .withServerTransition() to support Universal rendering. 15 | // The application ID can be any identifier which is unique on 16 | // the page. 17 | BrowserModule.withServerTransition({ appId: 'my-app' }), 18 | TransferHttpCacheModule, 19 | 20 | RouterModule.forRoot([ 21 | { path: '', component: HomeComponent, pathMatch: 'full' }, 22 | { 23 | path: 'speakers', 24 | loadChildren: () => 25 | import('./modules/speakers/speakers.module').then( 26 | m => m.SpeakersModule 27 | ) 28 | } 29 | ]), 30 | SharedModule 31 | ], 32 | bootstrap: [AppComponent] 33 | }) 34 | export class AppModule {} 35 | -------------------------------------------------------------------------------- /src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from "@angular/common/http"; 2 | import { NgModule } from "@angular/core"; 3 | import { 4 | ServerModule, 5 | ServerTransferStateModule 6 | } from "@angular/platform-server"; 7 | import { AppComponent } from "./app.component"; 8 | import { AppModule } from "./app.module"; 9 | import { UniversalInterceptorService } from "./modules/shared/interceptors/universal-interceptor.service"; 10 | 11 | @NgModule({ 12 | imports: [ 13 | // The AppServerModule should import your AppModule followed 14 | // by the ServerModule from @angular/platform-server. 15 | AppModule, 16 | ServerModule, 17 | ServerTransferStateModule 18 | ], 19 | // Since the bootstrapped component is not inherited from your 20 | // imported AppModule, it needs to be repeated here. 21 | bootstrap: [AppComponent], 22 | providers: [ 23 | { 24 | provide: HTTP_INTERCEPTORS, 25 | useClass: UniversalInterceptorService, 26 | multi: true // <-- important (you can have many interceptors) 27 | } 28 | ] 29 | }) 30 | export class AppServerModule {} 31 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { isPlatformBrowser } from '@angular/common'; 2 | import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'home', 6 | template: ` 7 |

Example

8 |
9 |

Homepage (show if browser render)

10 |
11 | `, 12 | styles: [ 13 | ` 14 | .container { 15 | margin: 16px; 16 | padding: 16px; 17 | border: 1px black solid; 18 | } 19 | ` 20 | ] 21 | }) 22 | export class HomeComponent implements OnInit { 23 | public title: string; 24 | public isBrowser: boolean = isPlatformBrowser(this.platformId); 25 | 26 | constructor(@Inject(PLATFORM_ID) private platformId: Object) {} 27 | 28 | ngOnInit() { 29 | this.title = `This is the Homepage!`; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/interfaces/speakers.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Speaker { 2 | name: string; 3 | talk: string; 4 | image: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/modules/shared/interceptors/universal-interceptor.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpHandler, 3 | HttpInterceptor, 4 | HttpRequest 5 | } from '@angular/common/http'; 6 | import { Inject, Injectable, Optional } from '@angular/core'; 7 | 8 | @Injectable() 9 | export class UniversalInterceptorService implements HttpInterceptor { 10 | constructor(@Optional() @Inject('serverUrl') protected serverUrl: string) {} 11 | 12 | intercept(req: HttpRequest, next: HttpHandler) { 13 | console.log(`intercepting the url ${req.url}`); 14 | const serverReq = !this.serverUrl 15 | ? req 16 | : req.clone({ 17 | url: `${this.serverUrl}${req.url}` 18 | }); 19 | 20 | return next.handle(serverReq); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/modules/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { UniversalInterceptorService } from './interceptors/universal-interceptor.service'; 4 | 5 | @NgModule({ 6 | imports: [CommonModule], 7 | providers: [UniversalInterceptorService], 8 | declarations: [] 9 | }) 10 | export class SharedModule {} 11 | -------------------------------------------------------------------------------- /src/app/modules/speakers/speakers-list/speakers-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { Speaker } from '../../../interfaces/speakers.interface'; 4 | import { SpeakersService } from './speakers.service'; 5 | 6 | @Component({ 7 | selector: 'app-speakers-list', 8 | template: ` 9 |

Speakers

10 |
    11 |
  • 12 | 13 | {{ speaker.name }} - {{ speaker.talk }} 14 |
  • 15 |
16 | `, 17 | styles: [ 18 | ` 19 | ul { 20 | margin: 16px; 21 | } 22 | li { 23 | display: flex; 24 | flex-direction: row; 25 | align-items: center; 26 | list-style-type: none; 27 | margin-bottom: 8px; 28 | } 29 | span { 30 | margin-left: 8px; 31 | } 32 | ` 33 | ] 34 | }) 35 | export class SpeakersListComponent implements OnInit { 36 | speakers: Observable; 37 | 38 | constructor(private speakersService: SpeakersService) { 39 | this.speakers = this.speakersService.getSpeakers(); 40 | } 41 | 42 | ngOnInit() {} 43 | } 44 | -------------------------------------------------------------------------------- /src/app/modules/speakers/speakers-list/speakers.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Speaker } from '../../../interfaces/speakers.interface'; 4 | 5 | @Injectable() 6 | export class SpeakersService { 7 | constructor(private http: HttpClient) {} 8 | 9 | getSpeakers() { 10 | return this.http.get(`/api/speakers`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/modules/speakers/speakers.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { RouterModule } from '@angular/router'; 4 | import { SharedModule } from '../shared/shared.module'; 5 | import { SpeakersListComponent } from './speakers-list/speakers-list.component'; 6 | import { SpeakersService } from './speakers-list/speakers.service'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | RouterModule.forChild([ 12 | { path: '', component: SpeakersListComponent, pathMatch: 'full' } 13 | ]), 14 | SharedModule 15 | ], 16 | providers: [SpeakersService], 17 | declarations: [SpeakersListComponent] 18 | }) 19 | export class SpeakersModule {} 20 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrilonIO/universal-nest/1677e6a580709e077536a106c2d287f7f3f1c365/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | prerenderUrl: 'http://localhost:4000' 4 | }; 5 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | prerenderUrl: 'http://localhost:4000' 9 | }; 10 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrilonIO/universal-nest/1677e6a580709e077536a106c2d287f7f3f1c365/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Nest + NgUniversal Demo 6 | 7 | 8 | 9 | 10 | 11 | 83 | 84 | 85 | 86 |
Loading...
87 |
88 | 89 | 90 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { environment } from './environments/environment'; 3 | 4 | if (environment.production) { 5 | enableProdMode(); 6 | } 7 | 8 | export { renderModule, renderModuleFactory } from '@angular/platform-server'; 9 | export { AppServerModule } from './app/app.server.module'; 10 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | import { AppModule } from './app/app.module'; 4 | import { environment } from './environments/environment'; 5 | 6 | if (environment.production) { 7 | enableProdMode(); 8 | } 9 | 10 | document.addEventListener('DOMContentLoaded', () => { 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | }); 15 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0 4 | } 5 | body { 6 | font-family: Helvetica; 7 | } 8 | #container { 9 | max-width: 800px; 10 | margin: 0 auto; 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": ["src/main.ts", "src/polyfills.ts"], 8 | "include": ["src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": ["node_modules/@types"], 15 | "lib": ["es2018", "dom"] 16 | }, 17 | "angularCompilerOptions": { 18 | "fullTemplateTypeCheck": true, 19 | "strictInjectionParameters": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app-server", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true 9 | }, 10 | "files": ["src/main.server.ts", "server.ts"], 11 | "angularCompilerOptions": { 12 | "entryModule": "./src/app/app.server.module#AppServerModule" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-var-requires": false, 64 | "object-literal-key-quotes": [ 65 | true, 66 | "as-needed" 67 | ], 68 | "object-literal-sort-keys": false, 69 | "ordered-imports": false, 70 | "quotemark": [ 71 | true, 72 | "single" 73 | ], 74 | "trailing-comma": false, 75 | "no-conflicting-lifecycle": true, 76 | "no-host-metadata-property": true, 77 | "no-input-rename": true, 78 | "no-inputs-metadata-property": true, 79 | "no-output-native": true, 80 | "no-output-on-prefix": true, 81 | "no-output-rename": true, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "use-lifecycle-interface": true, 86 | "use-pipe-transform-interface": true 87 | }, 88 | "rulesDirectory": [ 89 | "codelyzer" 90 | ] 91 | } --------------------------------------------------------------------------------