{"id":15663597,"url":"https://github.com/nartc/nest-demo","last_synced_at":"2025-05-06T15:28:51.173Z","repository":{"id":66345330,"uuid":"135370464","full_name":"nartc/nest-demo","owner":"nartc","description":"NestJS demo for Modern MEAN stack","archived":false,"fork":false,"pushed_at":"2018-06-16T16:00:43.000Z","size":319,"stargazers_count":17,"open_issues_count":1,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T01:31:41.248Z","etag":null,"topics":["angular","mean-stack","nestjs"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nartc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-30T01:17:13.000Z","updated_at":"2022-10-18T15:45:10.000Z","dependencies_parsed_at":"2023-02-22T02:16:09.548Z","dependency_job_id":null,"html_url":"https://github.com/nartc/nest-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fnest-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fnest-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fnest-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fnest-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nartc","download_url":"https://codeload.github.com/nartc/nest-demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252712610,"owners_count":21792344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","mean-stack","nestjs"],"created_at":"2024-10-03T13:38:36.178Z","updated_at":"2025-05-06T15:28:51.157Z","avatar_url":"https://github.com/nartc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Modern MEAN with NestJS\n\n# Server\n\nServer is written using NestJS. The following technologies are included in the demo:\n1. Typescript\n2. Swagger/OpenAPI\n3. JWT/Passport\n4. `config` package for Configuration\n5. `lodash`\n6. Mongoose with `@nestjs/mongoose`\n7. Socket.IO with `@nestjs/websockets`\n8. AutoMapper with `automapper-ts`\n\n- ####Architecture:\nNestJS uses Angular's approach on **Module-Components**. Hence, NestJS application is comprised of multiple `modules` and manage communication between `modules` through **Dependencies Injection**\n\n- ###Structure:\nThis specific demo uses `Repository Pattern` approach to abstract the logic with `Persistence Model (Database)` out to a separate **Services** layer. This could be done by naming `Repositories` but for small applications such as this demo, additional `Repositories` layer is skipped and utilizes `Services` instead.\n```markdown\n// Application Folder Structure\n-root\n|-- NestDemo.Client // Angular Frontend\n    |-- ..regular Angular frontend structures here // As this demo purpose is to showcase NestJS, I simplify the frontend for the sake of simplification.\n|-- NestDemo.Server // NestJS Backend\n    |-- config\n        |-- default-example.ts // Specific to this demo, please replace with your own default.ts\n        |-- default.ts // Read up \"config\" package documentations to have more understanding about the uses of this package. This file contains our configuration variables/API keys/Mongo ConnectionString for the application, hence is included in .gitignore\n  |-- dist // compiled folder from src\n  |-- src // Backend sources\n        |-- auth // AuthModule\n            |-- strategies // Our Auth strategies\n                |-- jwt.strategy.ts // JWT Strategy\n            |-- auth.module.ts\n            |-- auth.service.ts\n            |-- jwt-payload.model.ts // Payload interface for jwt.sign() method\n        |-- shared // SharedModule\n            |-- config // This directory houses actual ConfigService that will deal with our Application's Keys/Variables\n                |-- config.enum.ts // Prevent magic-string of our application's variable names\n                |-- config.service.ts // This will be injected where it's needed to get the variables\n            |-- decorators // Custom Decorators\n            |-- filters // Custom Exception Filters\n            |-- guards // Custom Guards\n            |-- mapping // This app utilizes AutoMapper\n                | -- mapper.service.ts // Same as ConfigService\n            |-- shared.model.ts // BaseModel for all other models to include the extension of Mongoose's Document\n            |-- shared.module.ts\n            |-- shared.service.ts // BaseService for All other services to include base CRUD operations\n        |-- socket // SocketModule\n        |-- todo // TodoModule\n            |-- ... same as UserModule\n        |-- user // UserModule\n            |-- models\n            |-- schema\n            |-- user.controller.ts\n            |-- user.module.ts\n            |-- user.service.ts\n        |-- app.controller.spec.ts\n        |-- app.controller.ts\n        |-- app.module.ts // Main ApplicationModule\n        |-- app.service.ts\n        |-- app-routing.module.ts // RoutingModule utilizing nest-router\n        |-- main.ts // Bootstrap our backend\n    |-- test // Will not be covered in here\n    |-- .gitignore\n    |-- .prettierc\n    |-- NestDemo.nswag // NSwag file for Client-side API Generations\n    |-- nodemon.json\n    |-- package.json\n    |-- package-lock.json\n    |-- README.md\n    |-- tsconfig.json\n    |-- tslint.json\n    |-- webpack.config.js\n|-- README.md // this README\n|-- .gitignore\n``` \nThe above structure might look overwhelming for a simple Todo backend but NestJS aims for Scalability and Modernity of backend development. Hence, the Separation of Concern (SoC) is heavily *forced* and utilized here.\n\n- ####Customs:\n1. ConfigService:\n```typescript\nimport { Injectable } from '@nestjs/common';\nimport { get } from 'config';\nimport { ConfigVar } from './config.enum';\n\n@Injectable()\nexport class ConfigService {\n    /**\n     * MongoConnectionString is made static to pass to MongooseModule.forRoot() dynamically\n     * @type {string}\n     */\n    static connectionString: string = process.env[ConfigVar.MONGO_URI] || get(ConfigVar.MONGO_URI);\n    private devEnvironment: string = process.env.NODE_ENV ? 'production' : 'development';\n\n    /**\n     * Method to return specific config variable using the constants from config.constant.ts.\n     * Using this will help us get rid of: process.env.\u003cVAR_NAME\u003e || config.get('\u003cvar_name_path\u003e') all over our code.\n     * @param {string} name\n     * @returns {string | number}\n     */\n    getConfigVariable(name: string): string {\n        return process.env[name] || get(name);\n    }\n\n    get isDevelopment(): boolean {\n        return this.devEnvironment === 'development';\n    }\n}\n```\nI do have comments in the file but might as well explain it here. I made `connectionString` a static variable because there's no way for me, in the `AppModule`, to call the `getConfigVariable` method in `ConfigService` to get the` MONGO_URI` from my `config/default.ts`.\n\n`getConfigVariable` is a method that takes in a `variableName` and will return a value from either `process.env` or your `default.ts` file. This is a nice-r way to get rid of all `process.env.\u003csome_thing\u003e || \u003csome_thing\u003e` in your codebase.\n\nA **getters** `isDevelopment` is to return the `DevelopmentEnvironment` in which the Application is running as a `boolean`.\n\n2. HttpExceptionFilters:\n```typescript\nimport { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common';\n\n@Catch(HttpException)\nexport class HttpExceptionFilter implements ExceptionFilter {\n    catch(error: any, host: ArgumentsHost) {\n        const ctx = host.switchToHttp();\n        const res = ctx.getResponse();\n        const req = ctx.getRequest();\n\n        if (error.getStatus() === HttpStatus.UNAUTHORIZED) {\n            if (typeof(error.response) !== 'string') {\n                error.response['message'] = error.response.message || 'You do not have permission to access this resource';\n            }\n        }\n\n        res.status(error.getStatus())\n            .json({\n                statusCode: error.getStatus(),\n                error: error.response.name || error.name,\n                message: error.response.message || error.message,\n                errors: error.response.errors || null,\n                timestamp: new Date().toISOString(),\n                path: req ? req.url : null,\n            });\n    }\n}\n```\nThis is NestJS's Filter. I have this `HttpExceptionFilter` to catch all `HttpException` thrown from the `Service` or `Controller` and modify it before it actually gets returned to the Client side.\n\nAs you can see from the code, I intentionally return some additional information from the `Request` itself (as Nest's Filter can have access to the **ExecutionContext**) before responding back to the client with the *familiar* `Response` object. Ironically, the returned object here looks exactly the same as my `ApiException` class in `shared.model.ts`.\n\n3. Swagger Integration:\n\nNestJS provides `SwaggerModule` through `@nestjs/swagger` package. In the code, you'll see decorators with prefix `@Api` all over the place as these decorators signify `SwaggerModule` to look at this class/method to include them in the `SwaggerUI` view and the `swagger.json` specification file.\n\n4. AutoMapper:\n\nThe usage of `AutoMapper` in this demo simply to test out `automapper-ts` and to actually implement`Model` on the Backend and `ViewModel` for the Frontend.\n\nFor example, `User` on the backend has every fields including `password` but `UserVm` does not include `password` to prevent `password` to be EVER exposed to the Frontend.\n\n5. NSwag:\n\n`NSwag` is used to generate Client-side API code. This is a package which you can install via: `npm install -g nswag`. \n\nThis `nswag` uses the `swagger.json` that you expose via `SwaggerUI` to generate `HTTP Calls` for your Frontend, including all calls and models. This is nice because it can help keeping the models consistent front-to-back.\n\nYou'll need an `.nswag` file. More information can be accessed at `nswag.org`.\n\n# Client\n\nThis is just a simple Angular frontend application to connect to the NestJS backend.\n\n# Steps to run\n1. For **NestDemo.Server**:\n- Run `mongod` to start a local Mongo service.\n- Make a `default.ts` and put it in your `config` directory under `root`\n- Mimic the format in `default-example.ts`\n- `cd ./NestDemo.Server` and run `npm install`\n- `npm start` will start up the server given you've done everything right\n- Go to `http://localhost:8080/api/docs` for the `SwaggerUI`\n\n2. For **NestDemo.Client**:\n- Run normal like an Angular application with `ng serve`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnartc%2Fnest-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnartc%2Fnest-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnartc%2Fnest-demo/lists"}