├── static ├── .nojekyll ├── CNAME └── img │ ├── favicon.ico │ ├── docusaurus.png │ ├── docusaurus-social-card.jpg │ ├── logo.svg │ ├── image1.svg │ ├── image2.svg │ └── image3.svg ├── babel.config.js ├── page.ts ├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── modules.xml └── opensource-docs.iml ├── src ├── pages │ ├── markdown-page.md │ ├── index.module.css │ └── index.tsx ├── components │ └── HomepageFeatures │ │ ├── styles.module.css │ │ └── index.tsx ├── theme │ └── NavbarItem │ │ └── DocsVersionDropdownNavbarItem.tsx └── css │ └── custom.css ├── tsconfig.json ├── .prettierrc ├── .gitignore ├── .page ├── sidebars.ts ├── README.md ├── pages ├── nestjs-prettier-config │ └── index.md ├── env-parser │ └── index.md ├── nestjs-testing │ └── index.md ├── nestjs-any-guard │ └── index.md ├── vite-config-builder │ └── index.md ├── reactjs-codelint-config │ └── index.md ├── nestjs-swagger-helper │ └── index.md ├── nestjs-cls-translation │ └── index.md ├── nestjs-eslint-config │ └── index.md ├── nestjs-validation │ └── index.md ├── nestjs-storage │ └── index.md ├── nestjs-seeder │ └── index.md ├── nestjs-exception │ └── index.md ├── nestjs-command │ └── index.md ├── nestjs-response │ └── index.md ├── typeorm-migrations │ └── index.md ├── nestjs-oidc │ └── index.md ├── nestjs-mailer │ └── index.md ├── nestjs-transaction │ └── index.md ├── nestjs-api-gateway │ └── index.md ├── typeorm-helper │ └── index.md ├── nestjs-grpc-helper │ └── index.md └── nestjs-base-decorator │ └── index.md ├── .github └── workflows │ └── deploy.yml ├── docs └── index.md ├── package.json └── docusaurus.config.ts /static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/CNAME: -------------------------------------------------------------------------------- 1 | opensource.hodfords.uk 2 | -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hodfords-solutions/docs/HEAD/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hodfords-solutions/docs/HEAD/static/img/docusaurus.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /page.ts: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | export const pages = fs.readFileSync('./.page').toString().split('\n').filter(Boolean); 3 | -------------------------------------------------------------------------------- /static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hodfords-solutions/docs/HEAD/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "proseWrap": "always", 5 | "tabWidth": 4, 6 | "useTabs": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "semi": true, 11 | "endOfLine": "auto" 12 | } 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /.page: -------------------------------------------------------------------------------- 1 | env-parser 2 | nestjs-eslint-config 3 | nestjs-prettier-config 4 | reactjs-codelint-config 5 | nestjs-oidc 6 | nestjs-api-gateway 7 | vite-config-builder 8 | nestjs-cls-translation 9 | nestjs-validation 10 | nestjs-base-decorator 11 | nestjs-transaction 12 | typeorm-helper 13 | nestjs-storage 14 | nestjs-grpc-helper 15 | nestjs-response 16 | nestjs-swagger-helper 17 | nestjs-mailer 18 | nestjs-exception 19 | nestjs-command 20 | nestjs-seeder 21 | nestjs-testing 22 | typeorm-migrations 23 | nestjs-any-guard 24 | -------------------------------------------------------------------------------- /.idea/opensource-docs.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useRouteContext from '@docusaurus/useRouteContext'; 3 | import DocsVersionDropdownNavbarItem from '@theme-original/NavbarItem/DocsVersionDropdownNavbarItem'; 4 | import {useLocation} from "@docusaurus/router"; 5 | 6 | export default function DocsVersionDropdownNavbarItemWrapper(props) { 7 | const location = useLocation(); 8 | if (location.pathname.startsWith(`/${props.docsPluginId}`)) { 9 | return ; 10 | } 11 | 12 | return null; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; 2 | import { SidebarConfig } from '@docusaurus/plugin-content-docs/src/sidebars/types'; 3 | import { pages } from './page'; 4 | 5 | const items: SidebarConfig = [ 6 | { 7 | type: 'link', 8 | label: 'Introduction', 9 | href: 'docs' 10 | }, 11 | ...pages.map((lib) => ({ 12 | type: 'link', 13 | label: lib, 14 | href: '../' + lib 15 | })) 16 | ]; 17 | 18 | const sidebars: SidebarsConfig = { 19 | // By default, Docusaurus generates a sidebar from the docs folder structure 20 | docs: items 21 | }; 22 | 23 | export default sidebars; 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #020303; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #fff; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /pages/nestjs-prettier-config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-prettier-config" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 | # PRETTIER CONFIGURATION FOR NESTJS PROJECTS 10 | Prettier default configuration for Nestjs project. It will help you to maintain the code quality of your project. 11 | 12 | ## Installation 🤖 13 | To begin using it, we first install the required dependencies. 14 | ``` 15 | npm install @hodfords/nestjs-prettier-config 16 | ``` 17 | 18 | ## Configuration 🚀 19 | To activate prettier, create a `prettier.config.js` file in the root of your project and add the following configuration: 20 | ```javascript 21 | module.exports = require('@hodfords/nestjs-prettier-config'); 22 | ``` 23 | 24 | ### Prettier Config 25 | ```javascript 26 | { 27 | singleQuote: true, 28 | printWidth: 120, 29 | proseWrap: 'always', 30 | tabWidth: 4, 31 | useTabs: false, 32 | trailingComma: 'none', 33 | bracketSpacing: true, 34 | jsxBracketSameLine: false, 35 | semi: true, 36 | endOfLine: 'auto' 37 | } 38 | ``` 39 | 40 | ## License 41 | This project is licensed under the MIT License 42 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Build Docusaurus 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: 18 19 | 20 | - name: Install dependencies 21 | run: npm install 22 | - name: Build website 23 | run: npm run build 24 | 25 | - name: Upload Build Artifact 26 | uses: actions/upload-pages-artifact@v3 27 | with: 28 | path: build 29 | 30 | deploy: 31 | name: Deploy to GitHub Pages 32 | needs: build 33 | 34 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 35 | permissions: 36 | pages: write # to deploy to Pages 37 | id-token: write # to verify the deployment originates from an appropriate source 38 | 39 | # Deploy to the github-pages environment 40 | environment: 41 | name: github-pages 42 | url: ${{ steps.deployment.outputs.page_url }} 43 | 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Deploy to GitHub Pages 47 | id: deployment 48 | uses: actions/deploy-pages@v4 49 | -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | --- 4 | 5 | # Hodfords OpenSource 6 | 7 | Let's discover **Hodfords OpenSource**. 8 | 9 | | Library | Description | 10 | |----------|-------------------------------------------------------| 11 | | nestjs-cls-translation | ... | 12 | | nestjs-logger | ... | 13 | | nestjs-base-decorator | ... | 14 | | nestjs-transaction | ... | 15 | | typeorm-helper | Provide functions for relational handling in Typeorm. | 16 | | nestjs-oidc | ... | 17 | | nestjs-storage | ... | 18 | | nestjs-grpc-helper | ... | 19 | | nestjs-response | ... | 20 | | nestjs-swagger-helper | ... | 21 | | nestjs-mailer | ... | 22 | | nestjs-exception | ... | 23 | | nestjs-command | ... | 24 | | nestjs-seeder | ... | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opensource-docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "format": "prettier --write \"**/**/*.ts\"", 15 | "write-heading-ids": "docusaurus write-heading-ids", 16 | "typecheck": "tsc", 17 | "build-page": "ts-node --esm build-page.ts" 18 | }, 19 | "dependencies": { 20 | "@docusaurus/core": "3.5.2", 21 | "@docusaurus/preset-classic": "3.5.2", 22 | "@mdx-js/react": "^3.0.0", 23 | "clsx": "^2.0.0", 24 | "prism-react-renderer": "^2.3.0", 25 | "react": "^18.0.0", 26 | "react-dom": "^18.0.0" 27 | }, 28 | "devDependencies": { 29 | "@docusaurus/module-type-aliases": "3.5.2", 30 | "@docusaurus/tsconfig": "3.5.2", 31 | "@docusaurus/types": "3.5.2", 32 | "prettier": "3.3.3", 33 | "ts-node": "10.9.2", 34 | "typescript": "~5.5.2" 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.5%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 3 chrome version", 44 | "last 3 firefox version", 45 | "last 5 safari version" 46 | ] 47 | }, 48 | "engines": { 49 | "node": ">=18.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Link from '@docusaurus/Link'; 3 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 4 | import Layout from '@theme/Layout'; 5 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 6 | import Heading from '@theme/Heading'; 7 | 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const {siteConfig} = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 | 16 | {siteConfig.title} 17 | 18 |

{siteConfig.tagline}

19 |
20 | 23 | Get Started 24 | 25 |
26 |
27 |
28 | ); 29 | } 30 | 31 | export default function Home(): JSX.Element { 32 | const {siteConfig} = useDocusaurusContext(); 33 | return ( 34 | 37 | 38 |
39 | 40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /pages/env-parser/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/env-parser" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Env-Parser is a simple and lightweight library for parsing environment variables in Node.js applications. Inspired by znv but simpler and lighter. 11 |

12 | 13 | ## Installation 🤖 14 | To begin using it, we first install the required dependencies. 15 | ``` 16 | npm install @hodfords/env-parser 17 | ``` 18 | 19 | ## Usage 🚀 20 | 21 | Create `.env` file, you can also pass it via environment variable without creating a file. 22 | 23 | ```env 24 | APP_PORT=3000 25 | REDIS_PORT=6379 26 | REDIS_HOST=localhost 27 | ALLOW_SEND_MAIL=true 28 | ``` 29 | 30 | Create env file like `env.ts` and import `parse` and `z` from `@hodfords/env-parser`. 31 | ```typescript 32 | import { parse, z } from '@hodfords/env-parser'; 33 | 34 | export const env = parse(process.env, { 35 | APP_PORT: z.number().min(1000).max(9999), 36 | REDIS: { 37 | PORT: z.number().default(6379), 38 | HOST: z.string().default('localhost') 39 | }, 40 | ALLOW_SEND_MAIL: z.boolean().default(true) 41 | }); 42 | ``` 43 | 44 | > Note: env-parser supports multiple parser levels, but we recommend using up to 2 levels to ensure easier code readability. 45 | 46 | 47 | More validation function here: [Zod Validation](https://zod.dev/?id=basic-usage) 48 | 49 | ## License 50 | This project is licensed under the MIT License 51 | -------------------------------------------------------------------------------- /pages/nestjs-testing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-testing" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Nestjs-Testing is a testing library for Nestjs applications. It provides a set of utilities to help you test your Nestjs application. 11 |

12 | 13 | ## Installation 🤖 14 | To begin using it, we first install the required dependencies. 15 | ``` 16 | npm install @hodfords/nestjs-testing 17 | ``` 18 | 19 | ## Configuration 🚀 20 | To easily customize the configuration, let's create an object that extends the `BaseTestHelper` class. This object will be used to configure the test environment. 21 | 22 | ```typescript 23 | export class TestHelper extends BaseTestHelper { 24 | getSupertestConfig(): SupertestConfig { 25 | return { 26 | isUseBearerAuth: true, 27 | authenticationHeader: 'Authorization' 28 | }; 29 | } 30 | 31 | getTestModuleBuilder(): TestingModuleBuilder { 32 | return Test.createTestingModule({ 33 | imports: [AppModule] 34 | }); 35 | } 36 | } 37 | ``` 38 | 39 | ## Usage 🚀 40 | 41 | Write your test cases using the `TestHelper` class. 42 | 43 | ```typescript 44 | describe('AppController (e2e)', () => { 45 | let testHelper = new TestHelper(); 46 | 47 | beforeAll(async () => { 48 | await testHelper.initialize(); 49 | }); 50 | 51 | it('Get index success', async () => { 52 | return testHelper.get('/').isOk().expect('Hello World!'); 53 | }); 54 | }); 55 | ``` 56 | 57 | ## License 58 | This project is licensed under the MIT License 59 | -------------------------------------------------------------------------------- /pages/nestjs-any-guard/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-any-guard" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Nestjs-AnyGuard is a custom guard for NestJS that allows you to apply multiple guards to a single route. It will pass the request to the first guard that returns true, and if none of the guards return true, it will throw a ForbiddenException. 11 |

12 | 13 | ## Installation 🤖 14 | 15 | To begin using it, we first install the required dependencies. 16 | 17 | ``` 18 | npm install @hodfords/nestjs-any-guard 19 | ``` 20 | ## Usage 📖 21 | 22 | ### AnyGuard Decorator 23 | **AnyGuard** is a custom decorator that allows you to apply multiple guards to a single route. It will pass the request to the first guard that returns true, and if none of the guards return true, it will throw an ForbiddenException. 24 | ```typescript 25 | @Post('example') 26 | @AnyGuard(Guard1, Guard2, Guard3) 27 | async example(@Body() body: any) { 28 | return body; 29 | } 30 | ``` 31 | 32 | ### Custom Guards 33 | **FirstSuccessGuard** is a custom guard that checks if any of the provided guards return true. If any guard returns true, it allows the request to proceed; otherwise, it throws a ForbiddenException. 34 | 35 | ```typescript 36 | 37 | @Post('example') 38 | @UseGuards(FirstSuccessGuard(Guard1, Guard2, Guard3)) 39 | async example(@Body() body: any) { 40 | return body; 41 | } 42 | 43 | ``` 44 | 45 | **HasHeaderGuard** checks if a specific header is present in the request. If the header is present and its value is 'true', it allows the request to proceed. Otherwise, it denies access. 46 | It is useful when you use with API-Gateway 47 | 48 | ```typescript 49 | @Post('example') 50 | @UseGuards(HasHeaderGuard('permission-passed')) 51 | async example(@Body() body: any) { 52 | return body; 53 | } 54 | ``` 55 | 56 | ## Limitations 🚧 57 | You have to register the guards in your module providers, otherwise it will not work. 58 | 59 | 60 | ## License 61 | 62 | This project is licensed under the MIT License 63 | -------------------------------------------------------------------------------- /pages/vite-config-builder/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/vite-config-builder" 4 | --- 5 | # Hodfords Vite Config Builder 6 | 7 |

8 | Hodfords Logo 9 |

10 | 11 | ## Installation 🤖 12 | 13 | * Install [PNPM](https://pnpm.io/) latest version 14 | * Install Nodejs >= 20 ( Should be use [NVM](https://github.com/nvm-sh/nvm) for install NodeJS ) 15 | 16 | * With NPM: 17 | ``` 18 | npm install @hodfords/vite-config-builder --save 19 | ``` 20 | 21 | * with PNPM: 22 | ``` 23 | pnpm install @hodfords/vite-config-builder 24 | ``` 25 | 26 | ## Usage 🚀 27 | ```typescript 28 | import createViteConfig from '@hodfords/vite-config-builder'; 29 | 30 | export default createViteConfig({ 31 | isUsePWA: true, 32 | isUseGzip: true, 33 | defineEnv: ['API_URL', 'APP_URL'], 34 | aliasPath: ['components', 'containers', 'config', 'api'], 35 | autoImport: { 36 | config: { 37 | eslintrc: { 38 | enabled: true, 39 | }, 40 | dirs: ['src/shared'], 41 | dts: 'src/types/auto-imports.d.ts', 42 | }, 43 | libs: [ 44 | { 45 | from: 'antd', 46 | imports: [ 47 | 'CollapseProps', 48 | 'ButtonProps', 49 | ], 50 | type: true, 51 | }, 52 | '@ant-design/plots': [ 53 | ['Pie', 'PieChart'], 54 | ['Column', 'ColumnChart'], 55 | ], 56 | ], 57 | } 58 | }); 59 | ``` 60 | 61 | ## Options 62 | 63 | ### `isUsePWA` - This option enable Vite PWA. 64 | - Type: `boolean` 65 | - Required: `true` 66 | 67 | ### `isUseGzip` - This option enable Gzip. 68 | - Type: `boolean` 69 | - Required: `true` 70 | 71 | ### `defineEnv` - This option define list Environments. 72 | - Type: `Array(string)` 73 | - Required: `true` 74 | 75 | ### `aliasPath` - This option define alias path. 76 | - Type: `Array(string)` 77 | - Required: `true` 78 | 79 | ### `autoImport` - This option define auto import libraries for Components, Functions, Utils, Types... 80 | - Type: `Object(array)` 81 | - Required: `true` 82 | 83 | ## License 📝 84 | 85 | This project is licensed under the MIT License 86 | -------------------------------------------------------------------------------- /pages/reactjs-codelint-config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/reactjs-codelint-config" 4 | --- 5 | # Hodfords ReactJS Codelint Config 6 | 7 |

8 | Hodfords Logo 9 |

10 | 11 |

reactjs-codelint-config include eslint, stylelint, tslint configs help you to maintain the code quality of your project.

12 | 13 | ## Installation 🤖 14 | 15 | * Install [PNPM](https://pnpm.io/) latest version 16 | * Install Nodejs >= 20 ( Should be use [NVM](https://github.com/nvm-sh/nvm) for install NodeJS ) 17 | 18 | * With NPM: 19 | ``` 20 | npm install @hodfords/reactjs-codelint-config --save 21 | ``` 22 | 23 | * with PNPM: 24 | ``` 25 | pnpm install @hodfords/reactjs-codelint-config 26 | ``` 27 | 28 | ## Configuration 🚀 29 | 30 | * To activate eslint, create a `eslint.config.js` file in the root of your project and add the following configuration: 31 | 32 | ```typescript 33 | import { eslintConfig } from '@hodfords/reactjs-codelint-config'; 34 | 35 | export default eslintConfig; 36 | ``` 37 | 38 | * To activate stylelint, create a `.stylelintrc.js` file in the root of your project and add the following configuration: 39 | 40 | ```typescript 41 | import { stylelintConfig } from '@hodfords/reactjs-codelint-config'; 42 | 43 | export default stylelintConfig; 44 | ``` 45 | 46 | ## Usage 🚀 47 | Run the following command to lint your project: 48 | ``` 49 | npx eslint 50 | ``` 51 | 52 | ## Eslint Config Details 53 | This configuration extends the `@hodfords/reactjs-codelint-config` package. It includes the following plugins: 54 | - `@typescript-eslint/parser` 55 | - `eslint-plugin-prettier` 56 | - `eslint-plugin-react` 57 | - `eslint-plugin-react-hooks` 58 | - `@typescript-eslint/eslint-plugin` 59 | - `eslint-plugin-unused-imports` 60 | - `eslint-plugin-import` 61 | - `eslint-plugin-react-func` 62 | - `@tanstack/eslint-plugin-query` 63 | 64 | ## Stylelint Config Details 65 | This configuration extends the `@hodfords/reactjs-codelint-config` package. It includes the following plugins: 66 | - `postcss-styled-syntax` 67 | - `stylelint-config-standard`, 68 | - `stylelint-config-recommended` 69 | 70 | ## License 📝 71 | 72 | This project is licensed under the MIT License 73 | -------------------------------------------------------------------------------- /pages/nestjs-swagger-helper/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-swagger-helper" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-swagger-helper streamlines the integration of Swagger documentation in NestJS applications. It provides utilities and decorators to simplify the creation and management of API documentation, making it easier to keep your API specs up-to-date and accessible.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-swagger-helper` package with: 14 | 15 | ``` 16 | npm install @hodfords/nestjs-swagger-helper --save 17 | ``` 18 | 19 | Next, create a file named `swagger-helper.config.ts` and add the following code. Then, include it in your `app.module.ts` file: 20 | 21 | ```typescript 22 | // APP_PREFIX is optional, if your application doesn't have this one you can skip 23 | export const swaggerConfig = SwaggerHelperModule.forRoot(APP_PREFIX); 24 | ``` 25 | 26 | Import the `SwaggerHelper` and use it to initialize Swagger 27 | 28 | ```typescript 29 | import { SwaggerHelper } from '@hodfords/nestjs-swagger-helper'; 30 | 31 | type SwaggerInitialization = { 32 | app: NestExpressApplication; 33 | appEnv: string; 34 | path: string; 35 | title: string; 36 | description: string; 37 | version: string; 38 | disablePrivateDocument: boolean; 39 | }; 40 | 41 | buildSwagger() { 42 | new SwaggerHelper({ 43 | app: this.app, 44 | appEnv: env.APP_ENV, 45 | path: `${env.APP_PREFIX}/documents`, 46 | title: 'Document for usdol', 47 | description: 'The usdol API description', 48 | version: '1.0', 49 | disablePrivateDocument: env.APP_ENV === 'production', 50 | }).buildDocuments(); 51 | } 52 | ``` 53 | 54 | ## Usage 🚀 55 | 56 | ### Decorators 57 | 58 | The library provides two decorators you can use: 59 | 60 | - `@ApiPublic`: Marks APIs as public. 61 | - `@ApiSetValue`: Typically used for login/signin APIs to automatically add a JWT token to Swagger when the request is 62 | successful. 63 | - `@ApiPermissions`: Used to document the permissions required for accessing specific API endpoints. 64 | 65 | ## License 📝 66 | 67 | This project is licensed under the MIT License 68 | -------------------------------------------------------------------------------- /src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import Heading from '@theme/Heading'; 3 | import styles from './styles.module.css'; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: 'Diverse Collection of Libraries', 14 | Svg: require('@site/static/img/image2.svg').default, 15 | description: ( 16 | <> 17 | Our libraries are designed to be lightweight, modular, and easy to integrate into your projects. 18 | It was designed from the ground up to be easily installed and used in any project, regardless of its size or complexity. 19 | 20 | ), 21 | }, 22 | { 23 | title: 'Comprehensive Documentation', 24 | Svg: require('@site/static/img/image1.svg').default, 25 | description: ( 26 | <> 27 | Every library in our collection is thoroughly documented to ensure easy understanding and usage, even for beginners. You'll find detailed guides, API references, and example use cases. 28 | 29 | ), 30 | }, 31 | { 32 | title: 'Community Contributions', 33 | Svg: require('@site/static/img/image3.svg').default, 34 | description: ( 35 | <> 36 | Each library is open for contributions from the community. We encourage developers of all skill levels to participate by reporting issues, suggesting features, or submitting code improvements. Collaboration is key to making these libraries even more powerful. 37 | 38 | ), 39 | }, 40 | ]; 41 | 42 | function Feature({title, Svg, description}: FeatureItem) { 43 | return ( 44 |
45 |
46 | 47 |
48 |
49 | {title} 50 |

{description}

51 |
52 |
53 | ); 54 | } 55 | 56 | export default function HomepageFeatures(): JSX.Element { 57 | return ( 58 |
59 |
60 |
61 | {FeatureList.map((props, idx) => ( 62 | 63 | ))} 64 |
65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 17 | 18 | 26 | 27 | 30 | 31 | 36 | 37 | 39 | 40 | 42 | 43 | 49 | 50 | -------------------------------------------------------------------------------- /pages/nestjs-cls-translation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-cls-translation" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-cls-translation provides context-aware translations in NestJS applications using Context-Local Storage (CLS), making it easier to manage and access locale-specific data across different parts of your application.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-cls-translation` package with: 14 | 15 | ```bash 16 | npm install @hodfords/nestjs-cls-translation --save 17 | ``` 18 | 19 | You'll need to configure the translation module by adding it to your NestJS app's module setup. Here’s how you can configure it: 20 | 21 | ```typescript 22 | TranslationModule.forRoot({ 23 | fallbackLanguage: 'en', 24 | parser: I18nJsonParser, 25 | parserOptions: { 26 | path: path.join(env.ROOT_PATH, 'i18n/'), 27 | watch: true 28 | }, 29 | resolvers: [new HeaderResolver(['language'])] 30 | }); 31 | ``` 32 | 33 | ## Usage 🚀 34 | 35 | #### Translation Functions 36 | 37 | To translate a specific key, use the trans function, passing the key for the translation string you wish to fetch: 38 | 39 | ```typescript 40 | const translatedText = trans('error.an_error_occurred') 41 | ``` 42 | 43 | This will return the translated string based on the user's current language, or the fallback language if no specific translation exists for the user's language. 44 | 45 | #### Get Current Language 46 | 47 | To retrieve the language currently being used in the context of a request, use the `currentLanguage()` function: 48 | 49 | ```typescript 50 | const currentLang = currentLanguage() 51 | ``` 52 | 53 | #### Get Default Language 54 | 55 | If you need to access the application's default or fallback language (set in the module configuration), use the `defaultLanguage()` function: 56 | 57 | ```typescript 58 | const defaultLang = defaultLanguage() 59 | ``` 60 | 61 | #### Run with a Specific Language Context 62 | 63 | You may want to execute certain parts of your code in a specific language context. The runInLanguage() function allows you to run a block of code under a designated language context, overriding the current language: 64 | 65 | ```typescript 66 | await runInLanguage('en', () => {...}); 67 | ``` 68 | 69 | ## License 📝 70 | 71 | This project is licensed under the MIT License 72 | -------------------------------------------------------------------------------- /pages/nestjs-eslint-config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-eslint-config" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 | # ESLINT DEFAULT CONFIGURATION 10 | Eslint default configuration for Nestjs project. It will help you to maintain the code quality of your project. 11 | 12 | ## Installation 🤖 13 | To begin using it, we first install the required dependencies. 14 | ``` 15 | npm install @hodfords/nestjs-eslint-config 16 | ``` 17 | 18 | ## Configuration 🚀 19 | To activate eslint, create a `eslint.config.js` file in the root of your project and add the following configuration: 20 | ```javascript 21 | module.exports = require('@hodfords/nestjs-eslint-config'); 22 | ``` 23 | 24 | ## Usage 🚀 25 | Run the following command to lint your project: 26 | ``` 27 | npx eslint 28 | ``` 29 | 30 | ## Eslint Config Details 31 | This configuration extends the `@hodfords/eslint-config` package. It includes the following plugins: 32 | - `@typescript-eslint/eslint-plugin` 33 | - `@typescript-eslint/parser` 34 | - `eslint-plugin-prettier/recommended` 35 | 36 | Custom rules are also included in this configuration. 37 | 38 | ### Prettier Config 39 | ```javascript 40 | { 41 | useTabs: false, 42 | tabWidth: 4, 43 | printWidth: 120, 44 | singleQuote: true, 45 | trailingComma: 'none' 46 | } 47 | ``` 48 | 49 | ### Typescript Config 50 | 51 | #### Naming Conventions 52 | - `@typescript-eslint/naming-convention` 53 | - `enumMember`: `UPPER_CASE` 54 | - `objectLiteralProperty`: `['camelCase', 'PascalCase', 'UPPER_CASE']` 55 | - `['class', 'interface', 'enum']`: `PascalCase` 56 | - `variable`: `['PascalCase', 'camelCase', 'UPPER_CASE']` 57 | - `function`: `['PascalCase', 'camelCase']` 58 | - `'parameter','variable','function', 'classProperty','typeProperty','parameterProperty','classMethod','objectLiteralMethod','typeMethod'`: `'camelCase'` 59 | 60 | #### Typescript Custom Rules 61 | ``` 62 | '@typescript-eslint/interface-name-prefix': 'off', 63 | '@typescript-eslint/no-empty-function': 'off', 64 | '@typescript-eslint/explicit-function-return-type': 'off', 65 | '@typescript-eslint/explicit-module-boundary-types': 'off', 66 | '@typescript-eslint/no-explicit-any': 'off', 67 | '@typescript-eslint/no-inferrable-types': 'off', 68 | '@typescript-eslint/no-unused-vars': ['error', { args: 'none' }] 69 | ``` 70 | 71 | ### Js Rules 72 | - `max-line-per-function`: `50` 73 | - `max-lines`: `400` 74 | - `prefer-const` 75 | - `lines-between-class-members` 76 | - `indent`: `off` 77 | 78 | ## License 79 | This project is licensed under the MIT License 80 | -------------------------------------------------------------------------------- /pages/nestjs-validation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-validation" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-validation enhances validation in your NestJS projects by providing a customized ValidationPipe that returns custom error messages. This library simplifies error handling by offering localized and user-friendly responses

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-validation` package with: 14 | 15 | ```bash 16 | npm install @hodfords/nestjs-validation --save 17 | ``` 18 | 19 | ## Usage 🚀 20 | 21 | First, create an instance of `ValidationPipe` with the desired configuration: 22 | 23 | ```typescript 24 | import { ValidationPipe } from '@hodfords/nestjs-validation'; 25 | import { ValidateException } from '@hodfords/nestjs-exception'; 26 | 27 | export const validateConfig = new ValidationPipe({ 28 | whitelist: true, 29 | stopAtFirstError: true, 30 | forbidUnknownValues: false, 31 | exceptionFactory: (errors): ValidateException => new ValidateException(errors) 32 | }); 33 | ``` 34 | 35 | Next, set the validation configuration globally in your bootstrap function: 36 | 37 | ```typescript 38 | async function bootstrap() { 39 | const app = await NestFactory.create(AppModule); 40 | app.useGlobalPipes(validateConfig); 41 | await app.listen(3000); 42 | } 43 | ``` 44 | 45 | ### Customize Validation Error 46 | 47 | The original error message provides basic information but lacks detail. With **nestjs-validation**, you can enhance these errors by adding meaningful context, such as the field’s property name, value, and target object. 48 | 49 | **Original Validation Error** 50 | 51 | ```javascript 52 | ValidationError { 53 | target: AppDto { stringValue: undefined }, 54 | value: undefined, 55 | property: 'stringValue', 56 | children: [], 57 | constraints: { isString: 'stringValue must be a string' } 58 | } 59 | ``` 60 | 61 | **Customized Validation Error** 62 | 63 | ```javascript 64 | ValidationError { 65 | target: AppDto { stringValue: undefined }, 66 | value: undefined, 67 | property: 'stringValue', 68 | children: [], 69 | constraints: { 70 | isString: { 71 | message: '$property must be a string', 72 | detail: { property: 'stringValue', target: 'AppDto', value: undefined } 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | ### Exception 79 | 80 | When combined with [nestjs-exception](https://www.npmjs.com/package/@hodfords/nestjs-exception), errors are translated into localized messages: 81 | 82 | ```json 83 | { 84 | "message": "Validate Exception", 85 | "errors": { 86 | "stringValue": { 87 | "messages": ["String Value must be a string"] 88 | } 89 | } 90 | } 91 | ``` 92 | 93 | ## License 📝 94 | 95 | This project is licensed under the MIT License 96 | -------------------------------------------------------------------------------- /pages/nestjs-storage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-storage" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Nestjs-Storage provides a powerful filesystem abstraction. The Nestjs-Storage integration provides simple drivers for working with local filesystems, Amazon S3, Azure. Even better, it's amazingly simple to switch between these storage options between your local development machine and production server as the API remains the same for each system. 11 |

12 | 13 | ## Installation 🤖 14 | To begin using it, we first install the required dependencies. 15 | ``` 16 | npm install @hodfords/nestjs-storage 17 | ``` 18 | 19 | ## Configuration 🚀 20 | To activate storage, import the `StorageModule` into the root `AppModule` and run the `forRoot()` static method as shown below: 21 | 22 | Azure configuration: 23 | ```typescript 24 | import { Module } from '@nestjs/common'; 25 | import { StorageModule } from '@hodfords/nestjs-storage'; 26 | 27 | @Module({ 28 | imports: [ 29 | StorageModule.forRoot({ 30 | account: { 31 | name: env.AZURE.ACCOUNT_NAME, 32 | key: env.AZURE.ACCOUNT_KEY, 33 | containerName: env.AZURE.CONTAINER_NAME, 34 | expiredIn: env.AZURE.SAS_EXPIRED_IN 35 | }, 36 | disk: 'azure' 37 | }) 38 | ], 39 | }) 40 | export class AppModule {} 41 | ``` 42 | 43 | Aws S3 configuration: 44 | ```typescript 45 | import { Module } from '@nestjs/common'; 46 | import { StorageModule } from '@hodfords/nestjs-storage'; 47 | 48 | @Module({ 49 | imports: [ 50 | StorageModule.forRoot({ 51 | account: { 52 | name: env.AWS.API_KEY, 53 | key: env.AWS.API_SECRET, 54 | containerName: env.AWS.BUCKET, 55 | expiredIn: env.AZURE.SAS_EXPIRED_IN, 56 | region: env.AWS.REGION 57 | }, 58 | disk: 's3' 59 | }) 60 | ], 61 | }) 62 | export class AppModule {} 63 | ``` 64 | 65 | ### Driver Prerequisites: 66 | - **Azure**: `npm install @azure/storage-blob` 67 | - **Aws S3**: `npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner` 68 | 69 | ## Usage 🚀 70 | 71 | Inject storage instance into your service or controller and use it as shown below: 72 | 73 | ```typescript 74 | import { StorageService } from "@hodfords/nestjs-storage"; 75 | 76 | @Injectable() 77 | export class AppService implements OnModuleInit { 78 | 79 | constructor(private storageService: StorageService) { 80 | } 81 | } 82 | ``` 83 | 84 | ### Delete file 85 | The delete method accepts a single filename 86 | 87 | ```typescript 88 | await this.storageService.deleteFile('path/to/file'); 89 | ``` 90 | 91 | This method may throw an exception if the file does not exist. You can ignore this exception by using the `deleteIfExists` method. 92 | 93 | ```typescript 94 | await this.storageService.deleteIfExists('path/to/file'); 95 | ``` 96 | 97 | ## License 98 | This project is licensed under the MIT License 99 | -------------------------------------------------------------------------------- /docusaurus.config.ts: -------------------------------------------------------------------------------- 1 | import { themes as prismThemes } from 'prism-react-renderer'; 2 | import type { Config } from '@docusaurus/types'; 3 | import type * as Preset from '@docusaurus/preset-classic'; 4 | import { pages } from './page'; 5 | 6 | const config: Config = { 7 | title: 'Hodfords OpenSource', 8 | tagline: 9 | 'At Hodfords, we believe in the power of open-source collaboration. This hub serves as the central place for developers, designers, and enthusiasts to explore, contribute, and utilize a growing collection of open-source libraries. ', 10 | favicon: 'img/favicon.ico', 11 | 12 | // Set the production url of your site here 13 | url: 'https://opensource.hodfords.uk', 14 | // Set the // pathname under which your site is served 15 | // For GitHub pages deployment, it is often '//' 16 | baseUrl: '/', 17 | 18 | // GitHub pages deployment config. 19 | // If you aren't using GitHub pages, you don't need these. 20 | organizationName: 'hodfords-solutions', // Usually your GitHub org/user name. 21 | projectName: 'docs', // Usually your repo name. 22 | deploymentBranch: 'gh-pages', 23 | onBrokenLinks: 'ignore', 24 | onBrokenMarkdownLinks: 'warn', 25 | i18n: { 26 | defaultLocale: 'en', 27 | locales: ['en'] 28 | }, 29 | 30 | presets: [ 31 | [ 32 | '@docusaurus/preset-classic', 33 | { 34 | docs: { 35 | sidebarPath: './sidebars.ts' 36 | }, 37 | theme: { 38 | customCss: './src/css/custom.css' 39 | } 40 | } satisfies Preset.Options 41 | ] 42 | ], 43 | plugins: [ 44 | ...pages.map((page) => [ 45 | '@docusaurus/plugin-content-docs', 46 | { 47 | id: page, 48 | path: './pages/' + page, 49 | routeBasePath: page, 50 | sidebarPath: './sidebars.ts' 51 | // includeCurrentVersion: false, 52 | } 53 | ]) 54 | ], 55 | themeConfig: { 56 | // Replace with your project's social card 57 | image: 'img/logo.svg', 58 | navbar: { 59 | title: 'Hodfords', 60 | logo: { 61 | alt: 'Hodfords Logo', 62 | src: 'img/logo.svg' 63 | }, 64 | items: [ 65 | { href: 'docs', label: 'Documents', position: 'left' }, 66 | { href: 'https://www.hodfords.com/', label: 'Our Company', position: 'left' }, 67 | ...pages.map((page) => ({ 68 | type: 'docsVersionDropdown', 69 | position: 'right', 70 | docsPluginId: page 71 | })), 72 | { 73 | href: 'https://github.com/hodfords-solutions', 74 | label: 'GitHub', 75 | position: 'right' 76 | } 77 | ] 78 | }, 79 | footer: { 80 | style: 'dark', 81 | links: [], 82 | copyright: `Hodfords OpenSource Documentation` 83 | }, 84 | prism: { 85 | theme: prismThemes.github, 86 | darkTheme: prismThemes.dracula 87 | } 88 | } satisfies Preset.ThemeConfig 89 | }; 90 | 91 | export default config; 92 | -------------------------------------------------------------------------------- /pages/nestjs-seeder/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-seeder" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-seeder streamlines the process of populating your NestJS application with mock data. It makes it easy to generate and manage seed data, ideal for testing and simulating API responses.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-seeder` package with: 14 | 15 | ``` 16 | npm install @hodfords/nestjs-seeder --save 17 | ``` 18 | 19 | ## Usage 🚀 20 | 21 | To seed fake user data into your database, follow these 6 steps: 22 | 23 | #### 1. Define the Factory 24 | 25 | First, create a factory for UserEntity. This factory will generate fake data for user records. 26 | 27 | ##### user.factory.ts 28 | 29 | ```typescript 30 | import { define } from '@hodfords/nestjs-seeder'; 31 | 32 | interface SeedUserOptions { 33 | countryId: string; 34 | } 35 | 36 | class UserEntity { 37 | name: string; 38 | age: string; 39 | countryId: string; 40 | createdAt: Date; 41 | } 42 | 43 | define(UserEntity, (options: SeedUserOptions) => { 44 | const user = new UserEntity(); 45 | 46 | user.name = faker.name.title(); 47 | user.age = faker.datatype.number(100); 48 | user.createdAt = faker.date.future(); 49 | 50 | return plainToClassFromExist(user, options || {}); 51 | }); 52 | ``` 53 | 54 | #### 2. Create the BaseSeeder 55 | 56 | Create a base seeder class that will be used to configure and run your seeding logic. 57 | 58 | ##### base.seeder.ts 59 | 60 | ```typescript 61 | import { Test } from '@nestjs/testing'; 62 | import { AppModule } from '~app.module'; 63 | import { databaseConfig } from '~config/database.config'; 64 | import { BaseSeeder as AbstractSeeder } from '@hodfords/nestjs-seeder'; 65 | 66 | export abstract class BaseSeeder extends AbstractSeeder { 67 | createModule() { 68 | return Test.createTestingModule({ 69 | imports: [AppModule, databaseConfig] 70 | }).compile(); 71 | } 72 | 73 | abstract run(): Promise; 74 | } 75 | ``` 76 | 77 | #### 3. Create the UserSeed 78 | 79 | Implement a seeder class that extends BaseSeeder. Use the factory methods to generate and save data. 80 | 81 | **There are 3 methods to seed a fake data from factory method** 82 | 83 | ```typescript 84 | createOne(options?: any): Entity; 85 | saveOne(options?: any): Promise; 86 | saveMany(count: number, options?: any): Promise; 87 | ``` 88 | 89 | ##### user.seed.ts 90 | 91 | ```typescript 92 | import { BaseSeeder } from '~core/seeders/base-seeder'; 93 | import { factory } from '@hodfords/nestjs-seeder'; 94 | import faker from 'faker'; 95 | 96 | export class UserSeed extends BaseSeeder { 97 | async run() { 98 | const countryId = (await factory(CountryEntity)).id; 99 | 100 | await factory(UserEntity).saveOne({ countryId }); // 1 101 | factory(UserEntity).createOne({ countryId }); // 2 102 | await factory(UserEntity).saveMany(100, { countryId }); // 3 103 | } 104 | } 105 | ``` 106 | 107 | #### 4. Create the seedConfig 108 | 109 | Set up the seed configuration to include your seed classes. 110 | 111 | ```typescript 112 | import { SeederModule } from '@hodfords/nestjs-seeder'; 113 | export const seedConfig = SeederModule.forRoot([UserSeed]); 114 | ``` 115 | 116 | #### 5. Import seedConfig into AppModule 117 | 118 | Integrate the seed configuration into your main application module. 119 | 120 | ```typescript 121 | @Module({ 122 | imports: [seedConfig], 123 | controllers: [AppController], 124 | providers: [] 125 | }) 126 | export class AppModule {} 127 | ``` 128 | 129 | #### 6. Run the seeder 130 | 131 | Execute the seeder command to populate your database with the defined fake data. 132 | 133 | ```typescript 134 | wz-command seeder 135 | ``` 136 | 137 | ## License 📝 138 | 139 | This project is licensed under the MIT License 140 | -------------------------------------------------------------------------------- /pages/nestjs-exception/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-exception" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | This repository contains a set of custom exception filters and exceptions designed for use in a NestJS application. It enhances error handling for HTTP-based and microservice-based (gRPC, Kafka) applications by providing more meaningful error responses and localization support. 11 | 12 |

13 | 14 | ## Installation 🤖 15 | 16 | To begin using it, we first install the required dependencies. 17 | 18 | ``` 19 | npm install @hodfords/nestjs-exception 20 | ``` 21 | 22 | ## Exception Classes 23 | 24 | > **Note**: These exception classes only function when used alongside the `HttpExceptionFilter` or one of its child classes (`GrpcExceptionFilter`, `KafkaExceptionFilter`, etc.). Be sure to apply the appropriate filter in your application. 25 | 26 | **1\. UuidException** 27 | 28 | This exception is used to handle invalid UUID formats in requests. It returns a 400 `BAD_REQUEST` status. 29 | 30 | Parameters: 31 | 32 | - `field`: The name of the field that contains the invalid UUID. This value is passed to indicate which field caused the exception. 33 | 34 | **2\. ValidateException** 35 | 36 | Handles specific validation errors related to a particular field. Returns a 422 `UNPROCESSABLE_ENTITY` status. 37 | 38 | Parameters: 39 | 40 | - `property`: The field name that caused the validation error. 41 | - `message`: The detailed message for the validation error. 42 | - `constraint`: The validation constraint that was violated (e.g., notNull). 43 | - `detail`: Additional information for the validation error, if applicable. 44 | 45 | ## Exception Filters 46 | 47 | - **HttpExceptionFilter**: Handles various types of HTTP exceptions with localization support. 48 | 49 | - **GrpcExceptionFilter**: Handles exceptions for gRPC microservices, formatting errors for gRPC clients. 50 | 51 | - **KafkaExceptionFilter**: Manages exceptions in Kafka microservices, formatting errors for Kafka messaging. 52 | 53 | - **ValidatorExceptionFilter**: Catches validation errors (`ValidateException`), supporting nested object validation and localization. 54 | 55 | **Note on Translation**: These filters, especially `HttpExceptionFilter` and `ValidatorExceptionFilter`, rely on a translation service to provide localized error messages. Ensure that your application has translation support enabled (e.g., using `@hodfords/nestjs-cls-translation`). The filters use translation keys defined in your language files to dynamically translate error messages based on the request's language. 56 | 57 | ## Example of usage 58 | 59 | To use the exception classes and filters in your NestJS application, follow these steps: 60 | 61 | #### 1\. **Applying the `HttpExceptionFilter`** 62 | 63 | **Global Application:** 64 | 65 | ```typescript 66 | import { HttpExceptionFilter } from '@hodfords/nestjs-exception'; 67 | import { NestFactory } from '@nestjs/core'; 68 | import { AppModule } from './app.module'; 69 | 70 | async function bootstrap() { 71 | const app = await NestFactory.create(AppModule); 72 | app.useGlobalFilters(new HttpExceptionFilter()); 73 | await app.listen(3000); 74 | } 75 | bootstrap(); 76 | ``` 77 | 78 | **Controller-Level Application:** 79 | 80 | ```typescript 81 | import { Controller, UseFilters } from '@nestjs/common'; 82 | import { HttpExceptionFilter } from 'hodfords/nestjs-exception'; 83 | 84 | @Controller('users') 85 | @UseFilters(HttpExceptionFilter) 86 | export class UserController {} 87 | ``` 88 | 89 | #### 2\. **Throwing a Custom Exception** 90 | 91 | ```typescript 92 | import { UuidException } from '@hodfords/nestjs-exception'; 93 | 94 | @Controller('users') 95 | export class UserController { 96 | @Get(':id') 97 | getUser(@Param('id') id: string) { 98 | if (!isValidUUID(id)) { 99 | throw new UuidException('id'); // Translation key: 'error.field_malformed' 100 | } 101 | return { id }; 102 | } 103 | } 104 | ``` 105 | 106 | ## License 107 | 108 | This project is licensed under the MIT License 109 | -------------------------------------------------------------------------------- /pages/nestjs-command/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-command" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-command simplifies creating and managing CLI commands in NestJS applications. It offers an easy way to define and execute commands, streamlining CLI integration and boosting productivity with minimal configuration.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-command` package with: 14 | 15 | ``` 16 | npm install @hodfords/nestjs-command --save 17 | ``` 18 | 19 | Set up in your codebase: 20 | 21 | - `src/config/command.config.ts` 22 | 23 | ```javascript 24 | import { CommandModule } from '@hodfords/nestjs-command'; 25 | 26 | export const commandConfig = CommandModule.register(); 27 | 28 | // export const = CommandModule.register(false) if typeorm is disabled 29 | ``` 30 | 31 | - `src/app.module.ts` 32 | 33 | ```javascript 34 | import { Module } from '@nestjs/common'; 35 | import { commandConfig } from '~config/command.config'; 36 | 37 | @Module({ 38 | imports: [commandConfig], 39 | controllers: [], 40 | providers: [] 41 | }) 42 | export class AppModule {} 43 | ``` 44 | 45 | - `src/cli.ts` 46 | 47 | ```javascript 48 | import { NestFactory } from '@nestjs/core'; 49 | import { CommandService } from '@hodfords/nestjs-command'; 50 | import { commandConfig } from '~config/command.config'; 51 | 52 | async function bootstrap() { 53 | const app = await NestFactory.createApplicationContext(AppModule); 54 | const commandService: CommandService = app.select(commandConfig).get(CommandService, { strict: true }); 55 | await commandService.exec(); 56 | await app.close(); 57 | } 58 | 59 | bootstrap(); 60 | ``` 61 | 62 | - `package.json` 63 | 64 | ```json 65 | "wz-command": "wz-command" 66 | ``` 67 | 68 | ## Usage 🚀 69 | 70 | Here’s how you can use them. For each type of component, you can use one of the two available command formats: with `npm run` or directly with `wz-command` 71 | 72 | ### Make a command 73 | 74 | ```bash 75 | npm run wz-command make-command -- --module 76 | ``` 77 | 78 | ```bash 79 | wz-command make-command --module 80 | ``` 81 | 82 | ### Make a controller 83 | 84 | ```bash 85 | npm run wz-command make-controller -- --module 86 | ``` 87 | 88 | ```bash 89 | wz-command make-controller --module 90 | ``` 91 | 92 | ### Make a dto 93 | 94 | ```bash 95 | npm run wz-command make-dto -- --module 96 | ``` 97 | 98 | ```bash 99 | wz-command make-dto --module 100 | ``` 101 | 102 | ### Make an e2e test 103 | 104 | ```bash 105 | npm run wz-command make-e2e-test -- --module 106 | ``` 107 | 108 | ```bash 109 | wz-command make-e2e-test --module 110 | ``` 111 | 112 | ### Make an entity 113 | 114 | ```bash 115 | npm run wz-command make-entity -- --module 116 | ``` 117 | 118 | ```bash 119 | wz-command make-entity --module 120 | ``` 121 | 122 | ### Make a migration 123 | 124 | #### Create table 125 | 126 | ```bash 127 | npm run wz-command make-migration -- --module --create= 128 | ``` 129 | 130 | ```bash 131 | wz-command make-migration --module --create= 132 | ``` 133 | 134 | #### Update table 135 | 136 | ```bash 137 | npm run wz-command make-migration -- --module --update= 138 | ``` 139 | 140 | ```bash 141 | wz-command make-migration --module --update= 142 | ``` 143 | 144 | ### Make a module 145 | 146 | ```bash 147 | npm run wz-command make-module 148 | ``` 149 | 150 | ```bash 151 | wz-command make-module 152 | ``` 153 | 154 | ### Make a repository 155 | 156 | ```bash 157 | npm run wz-command make-repository -- --module 158 | ``` 159 | 160 | ```bash 161 | wz-command make-repository --module 162 | ``` 163 | 164 | ### Make a service 165 | 166 | ```bash 167 | npm run wz-command make-service -- --module 168 | ``` 169 | 170 | ```bash 171 | wz-command make-service --module 172 | ``` 173 | 174 | ### List all scheduled cron jobs 175 | 176 | ```bash 177 | npm run wz-command list-cron-jobs 178 | ``` 179 | 180 | ```bash 181 | wz-command list-cron-jobs 182 | ``` 183 | 184 | ### Run specific cron jobs 185 | 186 | ```bash 187 | npm run wz-command run-cron-jobs -- --jobs 188 | ``` 189 | 190 | ```bash 191 | wz-command run-cron-jobs --jobs 192 | ``` 193 | 194 | ## License 📝 195 | 196 | This project is licensed under the MIT License 197 | -------------------------------------------------------------------------------- /pages/nestjs-response/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-response" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Nestjs-Response is a simple yet powerful library for managing API responses in a NestJS application. It provides decorators to handle response models, allowing easy integration with Swagger for API documentation and validation. 11 |

12 | 13 | ## Installation 🤖 14 | 15 | To begin using it, we first install the required dependencies. 16 | 17 | ``` 18 | npm install @hodfords/nestjs-response 19 | ``` 20 | 21 | ## Interceptor Setup 🚀 22 | 23 | - `Global Interceptor (Recommended):` 24 | 25 | Global interceptors are applied across the entire application. To set up a global interceptor, you can register it in the providers array in your module. 26 | 27 | ```typescript 28 | import { APP_INTERCEPTOR } from '@nestjs/core'; 29 | import { ResponseInterceptor } from '@hodfords/nestjs-response'; 30 | 31 | @Module({ 32 | providers: [ 33 | { 34 | provide: APP_INTERCEPTOR, 35 | useClass: ResponseInterceptor 36 | } 37 | ] 38 | }) 39 | export class AppModule {} 40 | ``` 41 | 42 | - `Interceptor with Decorator:` 43 | 44 | For microservices or specific scenarios, use the @UseInterceptors decorator to apply interceptors at the controller or method level. However, it's generally recommended to use global interceptors. 45 | 46 | ```typescript 47 | import { Controller } from '@nestjs/common'; 48 | import { UseResponseInterceptor } from '@hodfords/nestjs-response'; 49 | 50 | @Controller() 51 | @UseResponseInterceptor() 52 | export class AppController {} 53 | ``` 54 | 55 | ## Usage 🚀 56 | 57 | `@ResponseModel()` 58 | 59 | Use the @ResponseModel decorator when an API return single response type. 60 | 61 | Parameter: 62 | 63 | - `responseClass`: The class that defines the response model. 64 | - `isArray` (optional): Set to `true` if the response is an array of `responseClass`. Defaults to `false`. 65 | - `isAllowEmpty` (optional): Set to true if the response can be empty. Defaults to `false`. 66 | 67 | Example of usage: 68 | 69 | ```typescript 70 | import { ResponseModel } from '@hodfords/nestjs-response'; 71 | import { Get } from '@nestjs/common'; 72 | import { IsNotEmpty, IsString } from 'class-validator'; 73 | 74 | class UserResponse { 75 | @IsNotEmpty() 76 | @IsString() 77 | name: string; 78 | } 79 | 80 | export class UserController { 81 | @Get() 82 | @ResponseModel(UserResponse, true) 83 | getAllUser() { 84 | return [{ name: 'John' }]; 85 | } 86 | } 87 | ``` 88 | 89 | `@ResponseModels()` 90 | 91 | Use the @ResponseModels decorator when an API might return multiple response types. 92 | 93 | Parameter: 94 | 95 | - `...responseClasses`: A list of response classes or arrays of response classes. 96 | 97 | Example of usage: 98 | 99 | ```typescript 100 | import { ResponseModels } from '@hodfords/nestjs-response'; 101 | import { Controller, Get, Param } from '@nestjs/common'; 102 | import { UserResponse } from './responses/user.response'; 103 | import { UserPaginationResponse } from './responses/user-pagination.response'; 104 | 105 | @Controller() 106 | export class AppController { 107 | @Get('list-models/:type') 108 | @ResponseModels(Number, [Number], UserPaginationResponse, [UserResponse], undefined, null) 109 | getModels(@Param('type') type: string) { 110 | if (type == 'undefined') { 111 | return undefined; 112 | } 113 | if (type == 'pagination') { 114 | return { 115 | items: [{ name: 'John' }, { name: 'Daniel' }], 116 | total: 2, 117 | lastPage: 1, 118 | perPage: 10, 119 | currentPage: 1 120 | }; 121 | } 122 | if (type == 'multiple') { 123 | return [{ name: 'John' }, { name: 'Daniel' }]; 124 | } 125 | if (type == 'list-number') { 126 | return [123, 456]; 127 | } 128 | if (type == 'number') { 129 | return 456; 130 | } 131 | return null; 132 | } 133 | } 134 | 135 | ``` 136 | 137 | ### Exception Handling 138 | 139 | When the response data does not match the expected model, a validation exception will be raised. This ensures that the API returns data conforming to the defined structure. 140 | 141 | Example Case: If a property is expected to be a string, but a number is returned, a validation error will occur. 142 | 143 | ```typescript 144 | import { ResponseModel } from '@hodfords/nestjs-response'; 145 | import { Get } from '@nestjs/common'; 146 | import { IsString } from 'class-validator'; 147 | 148 | class UserResponse { 149 | @IsString() 150 | name: string; 151 | } 152 | 153 | export class UserController { 154 | @Get() 155 | @ResponseModel(UserResponse) 156 | getUser() { 157 | return { name: 123 }; // Error: name must be a number ... 158 | } 159 | } 160 | 161 | ``` 162 | 163 | ## License 164 | 165 | This project is licensed under the MIT License 166 | -------------------------------------------------------------------------------- /pages/typeorm-migrations/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/typeorm-migrations" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 | ## Installation 🤖 10 | 11 | Install the `typeorm-migrations` package with: 12 | 13 | ```bash 14 | npm install @hodfords/typeorm-migrations --save 15 | ``` 16 | 17 | ### Usage 18 | 19 | We develop a class that abstracts the typeorm migration, making it easier to understand. For the update command, let's 20 | use pure queries for the time being. 21 | 22 | ### Example 23 | 24 | ```typescript 25 | export class CreateUserTable1626749239046 extends BaseMigration { 26 | async run(queryRunner: QueryRunner) { 27 | await this.create('User', (table) => { 28 | table.primaryUuid('id'); 29 | table.string('email').index(); 30 | table.string('firstName').nullable(); 31 | table.string('lastName').nullable(); 32 | table.string('password').nullable(); 33 | table.integer('role'); 34 | table.string('language').length(10).default("'en'"); 35 | table.timestamp('lastLoginAt').nullable(); 36 | table.uuid('enterpriseId').nullable().index().foreign('Enterprise'); 37 | table.createdAt().index(); 38 | table.updatedAt(); 39 | table.deletedAt(); 40 | }); 41 | } 42 | 43 | async rollback(queryRunner: QueryRunner) { 44 | await this.drop('User'); 45 | } 46 | } 47 | ``` 48 | 49 | ### Table methods 50 | 51 | The Table class provides various methods for defining columns in a database schema 52 | 53 | ```typescript 54 | string(name: string, length?: number, options?: Partial): BaseColumn; 55 | strings(name: string, options?: Partial): BaseColumn; 56 | uuid(name?: string, options?: Partial): BaseColumn; 57 | uuids(name: string, options?: Partial): BaseColumn; 58 | primaryUuid(name?: string, options?: Partial): BaseColumn; 59 | id(name: string = 'id', options: Partial = {}): BaseColumn 60 | integer(name: string, options?: Partial): BaseColumn; 61 | integers(name: string, options?: Partial): BaseColumn; 62 | timestamp(name: string, options?: Partial): BaseColumn; 63 | timestamptz(name: string, options: Partial = null): BaseColumn; 64 | boolean(name: string, options?: Partial): BaseColumn; 65 | jsonb(name: string, options?: Partial): BaseColumn; 66 | json(name: string, options?: Partial): BaseColumn; 67 | decimal(name: string,precision: number = 10,scale: number = 2,options?: Partial): BaseColumn; 68 | enum( 69 | name: string, 70 | enumName: string, 71 | enumValues: Record | string[], 72 | options: Partial = null 73 | ): BaseColumn; 74 | char(name: string, length: number, options: Partial = null): BaseColumn; 75 | increments(name: string, options: Partial = null): BaseColumn; 76 | smallIncrements(name: string, options: Partial = null): BaseColumn; 77 | bigIncrements(name: string, options: Partial = null): BaseColumn; 78 | real(name: string, options: Partial = null): BaseColumn; 79 | doublePrecision(name: string, options: Partial = null): BaseColumn; 80 | time(name: string, options: Partial = null): BaseColumn; 81 | baseTime(): void; 82 | timetz(name: string, options: Partial = null): BaseColumn; 83 | geography( 84 | name: string, 85 | spatialFeatureType: string, 86 | srid = 4326, 87 | options?: Partial 88 | ): BaseColumn; 89 | geometry( 90 | name: string, 91 | spatialFeatureType: string, 92 | srid = 0, 93 | options?: Partial 94 | ): BaseColumn; 95 | macaddr(name: string, options?: Partial): BaseColumn; 96 | inet(name: string, options?: Partial): BaseColumn; 97 | createdAt(): BaseColumn; 98 | updatedAt(): BaseColumn; 99 | deletedAt(): BaseColumn; 100 | ``` 101 | 102 | ### Column method 103 | 104 | The BaseColumn class provides methods that define and configure properties for a database column, including length, 105 | nullability, uniqueness, indexing, default values, and foreign key relationships. 106 | 107 | ```typescript 108 | length(length: number): this; 109 | nullable(): this; 110 | unique(): this; 111 | index(): this; 112 | default(value: any): this; 113 | autoIncrement(): this; 114 | useCurrent(): this; 115 | comment(): this; 116 | foreign(table: string, column?: string, onDelete?: string, onUpdate?: string): void; 117 | ``` 118 | 119 | ## License 📝 120 | 121 | This project is licensed under the MIT License 122 | -------------------------------------------------------------------------------- /pages/nestjs-oidc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-oidc" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 | NestJS-OIDC is easy way to turn our server as oidc provider with minimum configuratiohn 10 | 11 | ## Installation 12 | This package is using redis as adapter to store authentication session and relevant stuffs, so you need to have install redis first 13 | 14 | ### Register module 15 | This is setup to register essential configuration for OIDC provider such as (client, ttls, cookies,...). You can get more at [OIDC Provider](https://github.com/panva/node-oidc-provider/tree/main/docs) 16 | 17 | ```ts 18 | import { OidcModule as NestOidc, OIDC_ACCOUNT_SERVICE } from '@hodfords/nestjs-oidc'; 19 | 20 | @Module({ 21 | imports: [ 22 | NestOidc.forRootAsync({ 23 | configuration: { 24 | useFactory: async function () { 25 | return { 26 | issuer: `${env.BACKEND_URL}/oidc`, 27 | claims: { 28 | email: ['email', 'email_verified'], 29 | profile: ['name'], 30 | groups: ['groups'] 31 | }, 32 | ttl: { 33 | AccessToken: 5 * 60, // 5 minutes 34 | AuthorizationCode: 60 * 10, // 10 minutes 35 | IdToken: 60 * 60, // 1 hour 36 | RefreshToken: 60 * 60 * 24 * 1, // 1 day 37 | Interaction: 60 * 60, // 1 hour 38 | Session: 60 * 60 * 24 * 14, // 14 days 39 | Grant: 5 * 60 // 5 minutes 40 | }, 41 | jwks: { 42 | keys: [env.OIDC_PROVIDER.JWKS] 43 | }, 44 | conformIdTokenClaims: false, 45 | cookies: { 46 | keys: ['interaction', 'session', 'state'], 47 | long: { 48 | signed: true, 49 | httpOnly: true, 50 | secure: true, // this should be false at local 51 | sameSite: 'none', 52 | path: '/', 53 | domain: 'localhost' 54 | }, 55 | short: { 56 | signed: true, 57 | httpOnly: true, 58 | secure: true, // this should be false at local 59 | sameSite: 'none', 60 | path: '/', 61 | domain: 'localhost' 62 | }, 63 | names: { 64 | session: '_session', 65 | interaction: '_interaction', 66 | resume: '_resume', 67 | state: '_state' 68 | } 69 | } 70 | } 71 | }, 72 | imports: [], 73 | inject: [] 74 | }, 75 | redisHost: `redis://${env.REDIS_HOST}:${env.REDIS_PORT}/${env.REDIS_DB}`, 76 | customInteractionUrl: 'http://localhost:3000' 77 | }), 78 | ], 79 | providers: [ 80 | { 81 | provide: OIDC_ACCOUNT_SERVICE, 82 | useClass: OidcService 83 | }, 84 | ], 85 | }) 86 | ``` 87 | 88 | ### Define OidcService 89 | Basically this service will be responsible for retrieving account information that will be returned by OIDC Provider to 3rd party 90 | 91 | ```ts 92 | import { IAccount } from '@hodfords/nestjs-oidc'; 93 | import { AccountClaimsType } from '@hodfords/nestjs-oidc/types/account.type'; 94 | 95 | @Injectable() 96 | export class OidcService { 97 | async findAccount(ctx: any, id: string): Promise { 98 | const account = await this.userService.findById(id); 99 | return this.getAccountInfo(account); 100 | } 101 | 102 | private async getAccountInfo(account: UserEntity): Promise { 103 | return { 104 | accountId: account.id, 105 | async claims(): Promise { 106 | return snakecaseKeys({ 107 | sub: account.id, 108 | email: account.email, 109 | name: account.fullName, 110 | emailVerified: true, 111 | groups: [] 112 | }); 113 | } 114 | }; 115 | } 116 | } 117 | ``` 118 | 119 | That is all you need to do to setup OIDC provider with NestJS-OIDC, you can now start your server and access to `http://localhost:3000/oidc/.well-known/openid-configuration` to see the configuration of OIDC provider 120 | 121 | ## License 122 | 123 | NestJS-OIDC is [MIT licensed](LICENSE). 124 | -------------------------------------------------------------------------------- /pages/nestjs-mailer/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-mailer" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-mailer simplifies integrating and managing email functionalities in NestJS applications, making email operations easier and more efficient.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-mailer` package with: 14 | 15 | ``` 16 | npm install @hodfords/nestjs-mailer --save 17 | ``` 18 | 19 | To configure the mailer module dynamically, use `forRoot` to define your email template renderers, transport settings, and default sender email. 20 | 21 | ```typescript 22 | export const mailConfig = MailerModule.forRoot({ 23 | renders: { 24 | adapters: [ 25 | new HbsAdapter({ 26 | templateFolder: path.join(env.ROOT_PATH, `mails/templates/${getEmailFolder()}`), 27 | defaultVariable: async () => getMailConfigurations() 28 | }), 29 | new TranslateAdapter((text: string, options: any) => trans(text, options)), 30 | new MjmlAdapter() 31 | ], 32 | transport: env.MAILER_URL, 33 | defaultFrom: env.CONTACT_MAIL_ADDRESS 34 | }, 35 | ... 36 | }); 37 | ``` 38 | 39 | For more advanced use cases where additional services or repositories are required, you can register the module using `forRootAsync`. This allows injecting services, repositories, or even database connections for more complex setups 40 | 41 | ```typescript 42 | export const mailConfig = MailerModule.forRootAsync({ 43 | imports: [CoreModule], 44 | inject: [Connection, StorageService], 45 | useFactory: (connection: Connection, storageService: StorageService) => { 46 | const settingRepo = connection.getCustomRepository(SettingRepository); 47 | const hbsAdapter = new HbsAdapter({ 48 | templateFolder: path.join(env.ROOT_PATH, `mails/templates/${getEmailFolder()}`), 49 | defaultVariable: async (mail: BaseMail) => { 50 | const variables = getMailConfigurations(); 51 | if (mail.isWhitelabeled) { 52 | const setting = await settingRepo.findOne({ tenant: mail.tenantId }); 53 | variables.logoUrl = await storageService.generateBlobUrl(setting.blobLogo); 54 | } 55 | return variables; 56 | } 57 | }); 58 | return { 59 | renders: { 60 | adapters: [ 61 | hbsAdapter, 62 | new TranslateAdapter((text: string, options: any) => trans(text, options)), 63 | new MjmlAdapter() 64 | ] 65 | }, 66 | transport: env.MAILER_URL, 67 | defaultFrom: env.CONTACT_MAIL_ADDRESS 68 | }; 69 | } 70 | }); 71 | ``` 72 | 73 | ## Usage 🚀 74 | 75 | ### Adapters 76 | 77 | Currently, nestjs-mailer supports the following adapters: 78 | 79 | - `HbsAdapter`: For rendering Handlebars templates with dynamic variables and templates. 80 | - `TranslateAdapter`: For handling multi-language support and translations. 81 | - `MjmlAdapter`: For generating responsive HTML emails using MJML templates. 82 | 83 | ### Defining an Email 84 | 85 | To define a custom email, extend the BaseMail class and specify the email subject, template path, and data. 86 | 87 | Here's an example of how to define a `WelcomeEmail`: 88 | 89 | ```typescript 90 | import { BaseMail } from '@hodfords/nestjs-mailer'; 91 | 92 | export class WelcomeMail extends BaseMail { 93 | constructor(private email: string) { 94 | super(); 95 | } 96 | 97 | get subject(): string { 98 | return 'Welcome to Hodfords!'; 99 | } 100 | 101 | get template(): string { 102 | return path.join(env.ROOT_PATH, 'welcome-mail.mail.hbs'); 103 | } 104 | 105 | data(): Record { 106 | return { 107 | content: 108 | "Welcome to our system! We're excited to have you on board and look forward to providing you with a seamless and enjoyable experience." 109 | }; 110 | } 111 | 112 | get to(): string { 113 | return this.email; 114 | } 115 | } 116 | ``` 117 | 118 | ### Sending an Email 119 | 120 | To send an email, inject the `MailerService` into your service and utilize the appropriate method for sending emails 121 | 122 | ```typescript 123 | import { MailService } from '@hodfords/nestjs-mailer'; 124 | 125 | @Injectable() 126 | class YourService { 127 | constructor(private mailService: MailerService) {} 128 | } 129 | ``` 130 | 131 | You have two options for sending emails: 132 | 133 | - **Send Immediately**: Send a single email right away. 134 | 135 | ```typescript 136 | 1. const mail = new WelcomeMail(user.email); 137 | await this.mailService.send(mail); 138 | ``` 139 | 140 | - **Add to Queue**: Use this method when you need to send a large number of emails. Emails will be queued and sent 141 | asynchronously. 142 | 143 | ```typescript 144 | for (const user of users) { 145 | const mail = new WelcomeMail(user.email); 146 | await this.mailService.addToQueue(mail); 147 | } 148 | ``` 149 | 150 | ## License 📝 151 | 152 | This project is licensed under the MIT License 153 | -------------------------------------------------------------------------------- /pages/nestjs-transaction/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-transaction" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-transaction makes managing database transactions in NestJS easy and straightforward. It provides simple tools to start, commit, and roll back transactions, helping you ensure that multiple database operations are completed successfully or not at all. With `nestjs-transaction`, you can handle complex transaction scenarios with ease, making your data handling more reliable and error-free.

10 | 11 | ## Installation 🤖 12 | 13 | Install the `nestjs-transaction` package with: 14 | 15 | ```ts 16 | npm install @hodfords/nestjs-transaction --save 17 | ``` 18 | 19 | ## Usage 🚀 20 | 21 | First, you need to import the `TransactionModule` into your `AppModule` and 22 | 23 | ```typescript 24 | import { Module } from '@nestjs/common'; 25 | import { TransactionModule } from '@hodfords/nestjs-transaction'; 26 | 27 | @Module({ 28 | imports: [ 29 | TransactionModule.forRoot({ 30 | autoUseMasterNodeForChangeRequest: true 31 | }), 32 | // other modules 33 | ], 34 | controllers: [], 35 | providers: [], 36 | }) 37 | export class AppModule {} 38 | ``` 39 | #### Optional Configuration 40 | You can configure the `TransactionModule` with the following options: 41 | - `autoUseMasterNodeForChangeRequest`: If set to `true`, the module will automatically use the master node for change requests (except GET). This is useful for ensuring that write operations are always directed to the correct database node. 42 | 43 | ### How to use 44 | 45 | #### Transactional 46 | 47 | You can use the `@Transactional` decorator to mark a method as transactional. This means that all database operations within this method and child methods will be executed within a transaction context. If any operation fails, the entire transaction will be rolled back, ensuring data integrity. 48 | 49 | In any service, you can use the `@Transactional` decorator to ensure that the method is executed within a transaction context: 50 | 51 | > **Note**: We suggest using the `@Transactional` decorator on Controller/Task/Cron jobs methods, as it will automatically handle the transaction lifecycle for you. However, you can also use it in services if needed. 52 | 53 | ```typescript 54 | @Transactional() 55 | async myTransactionalMethod() { 56 | await this.myRepository.save(myEntity); 57 | } 58 | ``` 59 | 60 | In controllers, you can also use the `@Transactional` decorator to ensure that the entire request is wrapped in a transaction: 61 | ```typescript 62 | 63 | import { Controller, Post } from '@nestjs/common'; 64 | import { Transactional } from '@hodfords/nestjs-transaction'; 65 | import { MyService } from './my.service'; 66 | 67 | @Controller('my') 68 | export class MyController { 69 | constructor(private myService: MyService) {} 70 | 71 | @Post('create') 72 | @Transactional() 73 | async create() { 74 | return this.myService.create(); 75 | } 76 | } 77 | ``` 78 | 79 | You also use the `@Transactional` decorator with options to control the transaction behavior, such as isolation level. For example: 80 | 81 | ```typescript 82 | @Transactional({ isolationLevel: 'SERIALIZABLE'}) 83 | async myTransactionalMethod() { 84 | // This method will run in a transaction with SERIALIZABLE isolation level 85 | await this.myRepository.save(myEntity); 86 | } 87 | ``` 88 | 89 | #### Replication 90 | You can use the `@UseMasterNode`/`@UseSlaveNode` decorator to ensure that a method is executed on the master/slave node. This is useful for write operations that need to be directed to the master/slave database node. 91 | 92 | ```typescript 93 | import { Controller, Post } from '@nestjs/common'; 94 | import { UseMasterNode } from '@hodfords/nestjs-transaction'; 95 | import { MyService } from './my.service'; 96 | 97 | @Controller('my') 98 | export class MyController { 99 | constructor(private myService: MyService) {} 100 | 101 | @Post('create') 102 | @UseMasterNode() 103 | async create() { 104 | return this.myService.create(); 105 | } 106 | } 107 | ``` 108 | 109 | #### Hooks 110 | 111 | You can use the `@RunAfterTransactionCommit` decorator to run a method after a transaction has been successfully committed. This is useful for performing actions that should only occur if the transaction was successful. 112 | 113 | ```typescript 114 | import { Injectable } from '@nestjs/common'; 115 | import { RunAfterTransactionCommit } from '@hodfords/nestjs-transaction'; 116 | import { PostRepository } from './post.repository'; 117 | 118 | @Injectable() 119 | export class MyService { 120 | 121 | constructor(private postRepo: PostRepository) { 122 | } 123 | 124 | @Transactional() 125 | async create() { 126 | // Perform some database operations 127 | await this.postRepo.createPost({ title: 'New Post', content: 'This is a new post.' }); 128 | await this.emitEvent(); 129 | } 130 | 131 | @RunAfterTransactionCommit() 132 | async emitEvent() { 133 | // This method will be called after the transaction is committed 134 | console.log('Transaction committed successfully!'); 135 | } 136 | } 137 | ``` 138 | 139 | You also use it with method `runAfterTransactionCommit` without the decorator: 140 | 141 | ```typescript 142 | 143 | this.postRepo.create(...); 144 | runAfterTransactionCommit(() => { 145 | // This code will run after the transaction is committed 146 | console.log('Transaction committed successfully!'); 147 | }); 148 | ``` 149 | ## License 📝 150 | 151 | This project is licensed under the MIT License 152 | -------------------------------------------------------------------------------- /pages/nestjs-api-gateway/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-api-gateway" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 | # API Gateway 10 | 11 | The API Gateway houses the source code and documentation for the API Gateway - a powerful and versatile solution for managing and deploying APIs within a distributed and microservices-oriented architecture. This repository serves as the central hub for collaboration, version control, and issue tracking related to the development and enhancement of the API Gateway. 12 | 13 | ## Key Features: 14 | 15 | - **Centralized API Management**: The API Gateway streamlines API management by providing a central entry point for client applications. It handles API requests, directs traffic to appropriate microservices, and offers additional functionalities to developers and administrators. 16 | 17 | - **Security and Authentication**: Security is paramount, and the API Gateway offers robust authentication and authorization mechanisms to protect APIs from unauthorized access. It supports various authentication protocols, including API keys, JWT. 18 | 19 | - **Rate Limiting and Throttling**: To prevent abuse and ensure fair usage, the API Gateway allows administrators to set rate limits and throttling rules. This helps maintain API performance and prevents any single client from overwhelming the system. 20 | 21 | - **Logging and Monitoring**: The API Gateway provides comprehensive logging and monitoring capabilities, allowing developers and administrators to gain insights into API usage, performance, and errors in real-time. 22 | 23 | - **WebSocket Support**: Beyond traditional RESTful APIs, the API Gateway supports WebSocket communication for real-time interactions and push notifications. 24 | 25 | - **Error Handling and Fault Tolerance**: The API Gateway is designed with robust error handling and fault tolerance mechanisms to ensure high availability and reliability. 26 | 27 | ## Usage 28 | 29 | #### Install 30 | ```shell 31 | npm install @hodfords/api-gateway 32 | ``` 33 | 34 | #### Configuration 35 | Import the `ApiGatewayModule` and use the `forRoot` method to configure the API Gateway. The `forRoot` method accepts an options object with the following properties: 36 | ```typescript 37 | @Module({ 38 | imports: [ 39 | RedisModule.forRoot({ 40 | config: { 41 | host: env.REDIS.HOST, 42 | port: env.REDIS.PORT, 43 | db: env.REDIS.DB 44 | } 45 | }), // Required 46 | ScheduleModule.forRoot(), // Required 47 | ApiGatewayModule.forRoot({ 48 | apiServices: env.API_SERVICES, 49 | openApiSecurityKeys: ['auth-user-id'], 50 | openApiSecurityApiKeys: ['x-api-key'], 51 | excludeHeaders: ['auth-user-id'], 52 | swaggerOptions: { 53 | showExtensions: false, 54 | }, 55 | throttler: { 56 | globalRateLimit: 60, 57 | isEnable: true, 58 | globalRateLimitTTL: 60 59 | } 60 | }) 61 | ], 62 | controllers: [], 63 | providers: [] 64 | }) 65 | export class AppModule {} 66 | ``` 67 | 68 | #### Custom Authentication Header 69 | You can handle the authentication header by creating a custom authentication handler. The `handle` method will be called before the request is processed. The `handle` method accepts the incoming request object and should return a boolean value indicating whether the request is authenticated. 70 | ```typescript 71 | @ProxyMiddleware() 72 | export class AuthenticationMiddleware implements ProxyMiddlewareHandler { 73 | async handle(routerDetail: RouterDetail, request: IncomingMessage, proxyRequest: ProxyRequest): Promise { 74 | proxyRequest.addHeaders({ 'auth-user-id': '123' }); 75 | return true; 76 | } 77 | } 78 | ``` 79 | Similarly, you can create a WebSocket authentication handler by decorating the `@WsProxyMiddleware`. The `handle` method will be called before the request is processed. The `handle` method accepts the incoming request object and should return a boolean value indicating whether the request is authenticated. 80 | ```typescript 81 | @WsProxyMiddleware() 82 | export class WsAuthenticationMiddleware implements WsProxyMiddlewareHandler { 83 | async handle(request: IncomingMessage, proxyRequest: ProxyRequest): Promise { 84 | proxyRequest.addHeaders({ 'auth-user-id': '123' }); 85 | return true; 86 | } 87 | } 88 | ``` 89 | 90 | #### Static File 91 | You can create a static file handler by decorating the `@StaticRequestHandler`. The `isStaticRequest` method will be called before the request is processed. The `isStaticRequest` method accepts the incoming request object and should return a boolean value indicating whether the request is for a static file. 92 | 93 | ```typescript 94 | 95 | @ProxyValidation() 96 | export class StaticRequestMiddleware implements ProxyValidationHandler { 97 | isStaticRequest(request: IncomingMessage): boolean { 98 | return request.url.includes('/images/') || request.url.includes('/statics/'); 99 | } 100 | } 101 | ``` 102 | 103 | #### Document 104 | API Gateway will aggregate all subservices into one. You can access by the link `http://gateway/documents` 105 | 106 | #### Authenticate 107 | API Gateway will process the jwt tokens and remove the token from the header. It will then add a new header key to the request called `auth-user-id` 108 | > To define a request that requires authentication, simply use the decorator `Auth()`. This decorator includes a check header function and a function that adds information to OpenAPI. 109 | > 110 | > In subservices, getting user information is eliminated. Instead you can just get the userId with decorator `@CurrentUserId() id: string` instead of decorator `@CurrentUser()` 111 | 112 | ```typescript 113 | @Auth() 114 | index(@CurrentUserId() id: string): string { 115 | return 'Hello word' 116 | } 117 | ``` 118 | 119 | #### Rate limit 120 | >`ApiRateLimit(limit: number, ttl: number, status?: number)` 121 | 122 | > **Parameter:** 123 | > - limit: number of requests 124 | > - ttl: limited time request 125 | > - status: *[optional]* limit requests by status, for example you want to limit the number of failed login attempts in 1 minute to 3 times: `@ApiRateLimit(3, 60, 401)` 126 | 127 | ```typescript 128 | @ApiRateLimit(5, 60, 200) 129 | @ApiRateLimit(30, 60 * 60, 200) 130 | @ApiRateLimit(3, 60, 401) 131 | index(): string { 132 | return 'Hello word' 133 | } 134 | ``` 135 | 136 | ## Support: 137 | 138 | If you encounter any issues, have questions, or need assistance with the API Gateway, please contact the development team 139 | 140 | Thank you for using the API Gateway! Happy API management and development! 141 | -------------------------------------------------------------------------------- /pages/typeorm-helper/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/typeorm-helper" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

nestjs-validation enhances validation in your NestJS projects by providing a customized `ValidationPipe` that returns custom error messages. This library simplifies error handling by offering localized and user-friendly responses

10 | 11 | ## Installation 🤖 12 | 13 | Install the `typeorm-helper` package with: 14 | 15 | ```bash 16 | npm install @hodfords/typeorm-helper --save 17 | ``` 18 | 19 | ## Usage 🚀 20 | 21 | ### Defining custom repositories and entities 22 | 23 | When managing different entities, you can define custom repositories and entities. Below is an example for the Category entity and its corresponding repository. 24 | 25 | #### Entity 26 | 27 | The `Category` table in the database is modeled by the `CategoryEntity`, `typeorm` decorators should be used to define this entity. 28 | 29 | ```typescript 30 | import { BaseEntity } from '@hodfords/typeorm-helper'; 31 | import { Column, Entity, ManyToMany, JoinTable, PrimaryGeneratedColumn } from 'typeorm'; 32 | 33 | @Entity('Category') 34 | export class CategoryEntity extends BaseEntity { 35 | @PrimaryGeneratedColumn() 36 | id: number; 37 | 38 | @Column() 39 | name: string; 40 | 41 | @ManyToMany(() => PostEntity, (post) => post.categories) 42 | @JoinTable({ name: 'PostCategory' }) 43 | posts: PostEntity[]; 44 | } 45 | ``` 46 | 47 | #### Repository 48 | 49 | The `CategoryRepository` is a custom repository that handles all database operations related to the `CategoryEntity`. By using the `@CustomRepository` decorator and extending `BaseRepository`, you ensure that your repository has both common CRUD functionality and can be easily customized with entity-specific methods. 50 | 51 | ```typescript 52 | import { CustomRepository, BaseRepository } from '@hodfords/typeorm-helper'; 53 | 54 | @CustomRepository(CategoryEntity) 55 | export class CategoryRepository extends BaseRepository {} 56 | ``` 57 | 58 | ### Lazy Relations 59 | 60 | Lazy relations allow you to load related entities only when they are needed. This can significantly improve performance by preventing the fetching of unnecessary data upfront. 61 | 62 | This functionality supports handling single entity, collection of entities, and paginated collection. Below is an example of how to load a list of posts associated with a specific category. 63 | 64 | ##### Single entity 65 | 66 | ```typescript 67 | const categoryRepo = getDataSource().getCustomRepository(CategoryRepository); 68 | const category = await categoryRepo.findOne({}); 69 | await category.loadRelation(['posts']); 70 | ``` 71 | 72 | ##### Collection of entities 73 | 74 | ```typescript 75 | const categoryRepo = getDataSource().getCustomRepository(CategoryRepository); 76 | const categories = await categoryRepo.findOne({ name: ILIKE('%football' }); 77 | await this.categories.loadRelations(['posts']); 78 | ``` 79 | 80 | ##### Paginate collection 81 | 82 | ```typescript 83 | const categoryRepo = getDataSource().getCustomRepository(CategoryRepository); 84 | const pagedCategories = await categoryRepo.pagination({}, { page: 1, perPage: 10 }); 85 | await pagedCategories.loadRelation('posts'); 86 | ``` 87 | 88 | You can also make use of the loadRelations function to efficiently load and retrieve related data 89 | 90 | ```typescript 91 | await loadRelations(categories, ['posts']); 92 | ``` 93 | 94 | ### Relation Condition 95 | 96 | Sometimes, you need to add custom conditions when loading related entities. `typeorm-helper` provides the 97 | `@RelationCondition` decorator for this purpose. 98 | 99 | ##### Simple condition 100 | 101 | This ensures that the posts relation is only loaded when the condition `posts.id = :postId` is satisfied. 102 | 103 | ```typescript 104 | @Entity('User') 105 | export class UserEntity extends BaseEntity { 106 | @PrimaryGeneratedColumn() 107 | id: number; 108 | 109 | @Column() 110 | name: string; 111 | 112 | @RelationCondition((query: SelectQueryBuilder) => { 113 | query.where(' posts.id = :postId', { postId: 1 }); 114 | }) 115 | @OneToMany(() => PostEntity, (post) => post.user, { cascade: true }) 116 | posts: PostEntity[]; 117 | 118 | @RelationCondition((query: SelectQueryBuilder, entities) => { 119 | query.orderBy('id', 'DESC'); 120 | if (entities.length === 1) { 121 | query.limit(1); 122 | } else { 123 | query.andWhere( 124 | ' "latestPost".id in (select max(id) from "post" "maxPost" where "maxPost"."userId" = "latestPost"."userId")' 125 | ); 126 | } 127 | }) 128 | @OneToOne(() => PostEntity, (post) => post.user, { cascade: true }) 129 | latestPost: PostEntity; 130 | } 131 | ``` 132 | 133 | #### Complex condition 134 | 135 | Here, the condition applies a limit if only one entity is found, and fetches the latest post for each user if there are multiple entities. 136 | 137 | ```typescript 138 | @Entity('User') 139 | export class UserEntity extends BaseEntity { 140 | @PrimaryGeneratedColumn() 141 | id: number; 142 | 143 | @Column() 144 | name: string; 145 | 146 | @RelationCondition( 147 | (query: SelectQueryBuilder) => { 148 | query.where(' posts.id = :postId', { postId: 1 }); 149 | }, 150 | (entity, result, column) => { 151 | return entity.id !== 2; 152 | } 153 | ) 154 | @OneToMany(() => PostEntity, (post) => post.user, { cascade: true }) 155 | posts: PostEntity[]; 156 | } 157 | ``` 158 | 159 | ### Where Expression 160 | 161 | For complex queries that need to be reused or involve a lot of logic, it's best to put them in a class 162 | 163 | ```typescript 164 | export class BelongToUserWhereExpression extends BaseWhereExpression { 165 | constructor(private userId: number) { 166 | super(); 167 | } 168 | 169 | where(query: WhereExpression) { 170 | query.where({ userId: this.userId }); 171 | } 172 | } 173 | ``` 174 | 175 | ```typescript 176 | const posts = await this.postRepo.find({ where: new BelongToUserWhereExpression(1) }); 177 | ``` 178 | 179 | ### Query Builder 180 | 181 | For complex and reusable queries, it's helpful to put the logic inside a class. This makes it easier to manage and reuse the query, resulting in cleaner and more maintainable code. 182 | 183 | ```typescript 184 | export class PostOfUserQuery extends BaseQuery { 185 | constructor(private userId: number) { 186 | super(); 187 | } 188 | 189 | query(query: SelectQueryBuilder) { 190 | query.where({ userId: this.userId }).limit(10); 191 | } 192 | 193 | order(query: SelectQueryBuilder) { 194 | query.orderBy('id', 'DESC'); 195 | } 196 | } 197 | ``` 198 | 199 | ```typescript 200 | const posts = await this.postRepo.find(new PostOfUserQuery(1)); 201 | ``` 202 | 203 | ## License 📝 204 | 205 | This project is licensed under the MIT License 206 | -------------------------------------------------------------------------------- /pages/nestjs-grpc-helper/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-grpc-helper" 4 | --- 5 |

6 | Hodfords Logo 7 |

8 | 9 |

10 | nestjs-grpc-helper simplifies gRPC integration in NestJS, allowing seamless communication between services. It enables easy setup of gRPC clients and servers, and supports building SDK packages that any service can import and use, ensuring consistent API interaction across your microservices architecture. 11 |

12 | 13 | ## Installation 🤖 14 | 15 | Install the `nestjs-grpc-helper` package with: 16 | 17 | ```bash 18 | npm install @hodfords/nestjs-grpc-helper --save 19 | ``` 20 | 21 | Next, automatically generate the proto file and include it in main.ts before starting the application: 22 | 23 | ```typescript 24 | import { generateProtoService } from '@hodfords/nestjs-grpc-helper'; 25 | 26 | generateProtoService(camelCase(env.APP_NAME), env.ROOT_PATH + '/../'); 27 | ``` 28 | 29 | ## Usage 🚀 30 | 31 | ### Creating microservices 32 | 33 | Create microservices using the `@GrpcMicroservice` decorator, similar to how you would use a Controller. Ensure that the response adheres to the [nestjs-response](https://www.npmjs.com/package/@hodfords/nestjs-response) rules: 34 | 35 | ```typescript 36 | @GrpcMicroservice() 37 | export class UserMicroservice { 38 | constructor(private userService: UserService) {} 39 | 40 | @GrpcAction('Get user by id') 41 | @ResponseModel(UserResponse) 42 | findUserById(@GrpcValue() dto: GetUserByIdDto): Promise { 43 | return this.userService.findUserById(dto.userId); 44 | } 45 | } 46 | ``` 47 | 48 | ### Any Type 49 | 50 | You can use any type if fixed types are not an option. However, since it’s passed as JSON, the performance may not be as optimal as with binary. Consider using binary if performance is a concern. 51 | 52 | ```typescript 53 | @Property({ type: String, format: 'any', required: false }) 54 | @AnyType() 55 | data: any; 56 | ``` 57 | 58 | ### Create SDK 59 | 60 | To generate a TypeScript SDK for your gRPC services, you can use the `make-sdk` command. This command will automatically generate the necessary proto files and package them into a JavaScript SDK. 61 | You also need the following configuration in your sdk-config.json file: 62 | 63 | ```json 64 | { 65 | "name": "sdkName", 66 | "packageName": "@hodfords/package-name", 67 | "format": true, 68 | "build": true, 69 | "output": "sdk", 70 | "outputBuild": "sdkBuild", 71 | "removeOutput": true, 72 | "addAllowDecorator": true, 73 | "tsconfig": { 74 | "extends": "./tsconfig.json", 75 | "compilerOptions": { 76 | "outDir": "sdkBuild" 77 | }, 78 | "include": ["sdk"] 79 | } 80 | } 81 | ``` 82 | Details of the configuration: 83 | 84 | | Field | Description | 85 | |-------------------|-------------------------------------------------------| 86 | | name | Name of the SDK | 87 | | packageName | Name of the package | 88 | | format | Format the generated code | 89 | | build | Build the generated code | 90 | | output | Output directory for the generated code | 91 | | outputBuild | Output directory for the built code | 92 | | removeOutput | Remove the output directory | 93 | | addAllowDecorator | Add the allow decorator, need class-validator package | 94 | | tsconfig | TypeScript configuration | 95 | 96 | 97 | To generate the SDK, run the following command: 98 | ```shell 99 | npm run wz-command make-sdk 100 | ``` 101 | 102 | #### What this command does 103 | 104 | This command will: 105 | 106 | 1. **Collect all request and response types**: It gathers all `@GrpcValue` request and response types from your project. 107 | 2. **Generate proto file**: Automatically generates the necessary proto files based on the collected types. 108 | 3. **Create JavaScript Package**: Packages the generated code into a JavaScript SDK. The SDK will be published using the name and version specified in your package.json, making it available for other services to import and use. The arguments, response structure, and method names remain consistent with the definitions in your gRPC service, ensuring seamless integration and functionality across services. 109 | 110 | ### SDK usage 111 | 112 | After publishing the SDK, other services can easily integrate it. Here’s an example of how to use the generated SDK 113 | 114 | 1. **Import the sdk package** 115 | 116 | 2. **Register the microservice module**: Configure the microservice in `AppModule` with the appropriate gRPC URL and timeout settings. 117 | 118 | ```typescript 119 | UserModule.register({ 120 | url: env.GRPC_URL, 121 | timeout: 5000 122 | }); 123 | ``` 124 | 125 | 3. **Use the SDK in another service**: Import the SDK and use it to interact with your gRPC services. 126 | 127 | ```typescript 128 | export class OtherService { 129 | constructor(private userMicroservice: UserMicroservice) {} 130 | 131 | async doTask(userId: string): Promise { 132 | const user = await this.userMicroservice.findUserById({ id: userId }); 133 | // Process user information as needed 134 | } 135 | } 136 | ``` 137 | 138 | In this example, `OtherService` uses the `UserMicroservice` class from the SDK to call the `findUserById` method. 139 | 140 | ### Mock response 141 | 142 | To effectively generate and handle mock data in your application, you can use the `@MockMethod`, `@MockSample`, and `@MockNested` decorators. 143 | 144 | ##### Generate dynamic data with `@MockMethod` 145 | 146 | Use `@MockMethod` to apply Faker methods for generating random values. 147 | 148 | For example, to create a random string of 10 characters 149 | 150 | ```typescript 151 | @Property({ type: String, required: false }) 152 | @MockMethod('faker.datatype.string', [10]) 153 | @IsString() 154 | name: string; 155 | ``` 156 | 157 | ##### Set fixed values with `@MockSample` 158 | 159 | If you need to set a fixed value for a property, use the `@MockSample` decorator. This is useful for enumerations or other predefined values. 160 | 161 | For example, to set a fixed user type 162 | 163 | ```typescript 164 | @Property({ 165 | type: String, 166 | enum: UserTypeEnum, 167 | enumName: 'UserTypeEnum' 168 | }) 169 | @MockSample(UserTypeEnum.STANDARD) 170 | @IsEnum(UserTypeEnum) 171 | type: UserTypeEnum; 172 | ``` 173 | 174 | ##### Generate nested data 175 | 176 | Use `@MockNested` to generate mock data for nested objects or arrays of nested objects. 177 | 178 | For example, to create an array of 5 nested objects 179 | 180 | ```typescript 181 | @Property({ type: UserResponse, isArray: true }) 182 | @IsArray() 183 | @ValidateNested() 184 | @Type(() => UserResponse) 185 | @MockNested(5) 186 | users: UserResponse[]; 187 | ``` 188 | 189 | ### Document for GRPC 190 | 191 | You can go to `http://xyz/microservice-documents` to check and try to call the gRPC method 192 | 193 | ```typescript 194 | MicroserviceDocumentModule.register({ 195 | isEnable: true, 196 | prefix: , 197 | packageName: camelCase(), 198 | clientOptions: { ...microserviceGrpcConfig, customClass: CustomGrpcClient, transport: undefined } 199 | }) 200 | ``` 201 | 202 | ## License 📝 203 | 204 | This project is licensed under the MIT License 205 | -------------------------------------------------------------------------------- /static/img/image1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /static/img/image2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /static/img/image3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /pages/nestjs-base-decorator/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | displayed_sidebar: docs 3 | title: "@hodfords/nestjs-base-decorator" 4 | --- 5 |

6 | Nest Logo 7 |

8 | 9 |

10 | Nestjs-Base-Decorator provides a collection of useful, ready-to-use custom decorators for NestJS, designed to enhance and simplify common functionality in your applications. These decorators will help you write cleaner and more maintainable code, reducing the need for repetitive boilerplate. 11 |

12 | 13 | ## Installation 🤖 14 | 15 | To begin using it, we first install the required dependencies. 16 | 17 | ``` 18 | npm install @hodfords/nestjs-base-decorator 19 | ``` 20 | 21 | ## Table of Contents 22 | 23 | - [Installation 🤖](#installation-) 24 | - [Table of Contents](#table-of-contents) 25 | - [Usage 🚀](#usage-) 26 | - [Param Decorators](#param-decorators) 27 | - [@EnumQuery()](#enumquery) 28 | - [@EnumsQuery()](#enumsquery) 29 | - [@Id()](#id) 30 | - [@Ids()](#ids) 31 | - [@Int()](#int) 32 | - [@Ints()](#ints) 33 | - [@Pagination()](#pagination) 34 | - [@QueryBoolean()](#queryboolean) 35 | - [@QueryStrings()](#querystrings) 36 | - [@RealIp()](#realip) 37 | - [@RequireToUploadFile()](#requiretouploadfile) 38 | - [@RequireToUploadFiles()](#requiretouploadfiles) 39 | - [@Sort()](#sort) 40 | - [@Sorts()](#sorts) 41 | - [@Timestamp()](#timestamp) 42 | - [@Timezone()](#timezone) 43 | - [@UserAgent()](#useragent) 44 | - [@Value()](#value) 45 | - [Validators](#validators) 46 | 47 | ## Usage 🚀 48 | 49 | ### Param Decorators 50 | 51 | #### @EnumQuery() 52 | 53 | Use the `EnumQuery` decorator when you expect a single enum value as a query parameter. This will validate the query parameter against a predefined enum and generate Swagger documentation for it. 54 | 55 | Parameters: 56 | 57 | - `options`: 58 | - `key`: The name of the query parameter (required). 59 | - `enum`: The enum to validate against (required). 60 | - `separator`: The delimiter for multiple values (optional, default: `,`). 61 | - `description`: A description for Swagger documentation (optional). 62 | - `nullable`: If true, the query parameter is allowed to be `null` (optional, default: `false`). 63 | - `singleValue`: Only used internally for `EnumQuery` to indicate a single value (you don't need to set this manually). 64 | 65 | Example for usage: 66 | 67 | ```typescript 68 | 69 | import { Controller, Get } from '@nestjs/common'; 70 | import { EnumQuery } from '@hodfords/nestjs-base-decorator'; 71 | 72 | enum StatusEnum { 73 | ACTIVE = 'active', 74 | INACTIVE = 'inactive', 75 | } 76 | 77 | @Controller() 78 | export class ItemController { 79 | @Get('items') 80 | getItemByStatus( 81 | @EnumQuery({ 82 | key: 'status', 83 | enum: StatusEnum, 84 | description: 'Status of the item', 85 | nullable: false, 86 | }) 87 | status: StatusEnum, 88 | ) { 89 | return status; 90 | } 91 | } 92 | 93 | /* 94 | Request: GET /items?status=active 95 | Response: active 96 | */ 97 | ``` 98 | 99 | #### @EnumsQuery() 100 | 101 | Use the `EnumsQuery` decorator when you expect multiple enum values as query parameters, separated by a custom delimiter (default is `,`). This will validate the query parameters against a predefined enum and generate Swagger documentation for them. 102 | 103 | Parameters: 104 | 105 | - `options`: 106 | 107 | - `key`: The name of the query parameter (required). 108 | - `enum`: The enum to validate against (required). 109 | - `separator`: The delimiter for multiple values (optional, default: `,`). 110 | - `description`: A description for Swagger documentation (optional). 111 | - `nullable`: If true, the query parameter is allowed to be `null` (optional, default: `false`). 112 | - `singleValue`: Only used internally for `EnumQuery` to indicate a single value (you don't need to set this manually). 113 | 114 | Example for usage: 115 | 116 | ```typescript 117 | import { Controller, Get } from '@nestjs/common'; 118 | import { EnumsQuery } from '@hodfords/nestjs-base-decorator'; 119 | 120 | enum CategoryEnum { 121 | ELECTRONICS = 'electronics', 122 | FASHION = 'fashion', 123 | BOOKS = 'books', 124 | } 125 | 126 | @Controller() 127 | export class ProductController { 128 | @Get('products') 129 | getProductsByCategories( 130 | @EnumsQuery({ 131 | key: 'categories', 132 | enum: CategoryEnum, 133 | description: 'Categories of the product', 134 | separator: ',', 135 | nullable: false, 136 | }) 137 | categories: CategoryEnum[], 138 | ) { 139 | return categories; 140 | } 141 | } 142 | 143 | /* 144 | Request: GET /products?categories=books,fashion 145 | Response: ["books", "fashion"] 146 | */ 147 | ``` 148 | 149 | #### @Id() 150 | 151 | Use the `Id` decorator to validate a single UUID, either from the route parameters or query parameters. If the provided value is not a valid UUID, it will throw a `UuidException` from the `@hodfords/nestjs-exception` package. 152 | 153 | Parameters: 154 | 155 | - `options` (`string` | `ParamOptions`): 156 | 157 | - `key`: The name of the parameter in the request (required). 158 | - `nullable`: If `true`, the parameter is allowed to be null (optional, default: `false`). 159 | 160 | Example for usage: 161 | 162 | ```typescript 163 | import { Controller, Get } from '@nestjs/common'; 164 | import { Id } from '@hodfords/nestjs-base-decorator'; 165 | 166 | @Controller('users') 167 | export class UserController { 168 | @Get(':id') 169 | getUserById(@Id('id') id: string) { 170 | return id; 171 | } 172 | } 173 | 174 | /* 175 | Request: GET users/8be26127-c0ed-4cad-bd45-7f40dcf53e89 176 | Response: 8be26127-c0ed-4cad-bd45-7f40dcf53e89 177 | */ 178 | ``` 179 | 180 | #### @Ids() 181 | 182 | Use the `Ids` decorator when you need to validate multiple UUIDs passed as a comma-separated list in the `query` parameters. If any value in the list is not a valid UUID, it throws a `UuidException` from the `@hodfords/nestjs-exception` package. 183 | 184 | Parameters: 185 | 186 | - `options` (`string` | `ParamOptions`): 187 | 188 | - `key`: The name of the parameter in the request (required). 189 | - `nullable`: If `true`, the parameter is allowed to be null (optional, default: `false`). 190 | 191 | Example for usage: 192 | 193 | ```typescript 194 | import { Controller, Get } from '@nestjs/common'; 195 | import { Ids } from '@hodfords/nestjs-base-decorator'; 196 | 197 | @Controller('users') 198 | export class UserController { 199 | @Get() 200 | getUsersByIds(@Ids('ids') ids: string[]) { 201 | return ids; 202 | } 203 | } 204 | 205 | /* 206 | Request: GET users?ids=8be26127-c0ed-4cad-bd45-7f40dcf53e89,1b3a0d50-2695-49e7-9498-c4cb1cada6e9 207 | Response: ["8be26127-c0ed-4cad-bd45-7f40dcf53e89","1b3a0d50-2695-49e7-9498-c4cb1cada6e9"] 208 | */ 209 | ``` 210 | 211 | #### @Int() 212 | 213 | The `Int` decorator is used to validate a single integer, either from route parameters or query parameters. 214 | 215 | Parameters: `key` (default: `'id'`) 216 | 217 | Example for usage: 218 | 219 | ```typescript 220 | import { Controller, Get } from '@nestjs/common'; 221 | import { Int } from '@hodfords/nestjs-base-decorator'; 222 | 223 | @Controller('users') 224 | export class UserController { 225 | @Get(':id') 226 | getUserById(@Int('id') id: number) { 227 | return id; 228 | } 229 | } 230 | 231 | /* 232 | Request: GET /users/123 233 | Response: 123 234 | */ 235 | ``` 236 | 237 | #### @Ints() 238 | 239 | The `Ints` decorator is used to validate multiple integers passed as a comma-separated list in query parameters. 240 | 241 | Parameters: `key` (default: `'ids'`) 242 | 243 | Example for usage: 244 | 245 | ```typescript 246 | import { Controller, Get } from '@nestjs/common'; 247 | import { Ints } from '@hodfords/nestjs-base-decorator'; 248 | 249 | @Controller('users') 250 | export class UserController { 251 | @Get() 252 | getUsersByIds(@Ints('ids') ids: number[]) { 253 | return ids; 254 | } 255 | } 256 | 257 | /* 258 | Request: GET /users?ids=123,456 259 | Response: [123,456] 260 | */ 261 | ``` 262 | 263 | #### @Pagination() 264 | 265 | The `Pagination` decorator is used to handle pagination logic by extracting the `page` and `perPage` parameters from the query string of an incoming request. The decorator also includes automatic Swagger documentation using `nestjs/swagger`. 266 | 267 | Parameters: 268 | 269 | - `paginationParams`: 270 | - `page`: The current page number (optional, default: 1). 271 | - `perPage`: The number of items per page (optional, default: 10, max: 1000). 272 | 273 | Example for usage: 274 | 275 | ```typescript 276 | import { Controller, Get } from '@nestjs/common'; 277 | import { Pagination, PaginationParams } from '@hodfords/nestjs-base-decorator'; 278 | 279 | @Controller('users') 280 | export class UserController { 281 | @Get() 282 | getUsers( 283 | @Pagination() pagination: PaginationParams 284 | ) { 285 | return `Page: ${pagination.page}, Per Page: ${pagination.perPage}`; 286 | } 287 | } 288 | 289 | /* 290 | Request: GET /users?page=1&perPage=10 291 | Response: Page: 1, Per Page: 10 292 | */ 293 | ``` 294 | 295 | #### @QueryBoolean() 296 | 297 | The `QueryBoolean` decorator allows you to extract and validate boolean values from the query parameters in a NestJS route. It checks if the query parameter is `'true'` and returns `true`, otherwise it returns `false`. 298 | 299 | Parameters: `key`, `options` (`ApiQueryOptions`) 300 | 301 | Example for usage: 302 | 303 | ```typescript 304 | import { Controller, Get } from '@nestjs/common'; 305 | import { QueryBoolean } from '@hodfords/nestjs-base-decorator'; 306 | 307 | @Controller('users') 308 | export class UsersController { 309 | @Get() 310 | getUsers(@QueryBoolean('isActive') isActive: boolean) { 311 | return isActive; 312 | } 313 | } 314 | 315 | /* 316 | Request: GET /users?isActive=true 317 | Response: true 318 | 319 | Request: GET /users?isActive=123 320 | Response: false 321 | */ 322 | ``` 323 | 324 | #### @QueryStrings() 325 | 326 | The `QueryStrings` decorator extracts query parameters from HTTP requests, parses them as comma-separated strings, and ensures uniqueness by eliminating duplicates. Additionally, it integrates with `@nestjs/swagger` to automatically document the query parameters in the Swagger UI. 327 | 328 | Parameters: `key`, `options` (`ApiQueryOptions`) 329 | 330 | Example for usage: 331 | 332 | ```typescript 333 | import { Controller, Get } from '@nestjs/common'; 334 | import { QueryStrings } from '@hodfords/nestjs-base-decorator'; 335 | 336 | @Controller() 337 | export class AppController { 338 | constructor() {} 339 | 340 | @Get() 341 | findTags(@QueryStrings('tags') tags: string[]): string[] { 342 | return tags; 343 | } 344 | } 345 | 346 | /* 347 | Request: GET ?tags=name,age 348 | Response: ["name, "age"] 349 | */ 350 | ``` 351 | 352 | #### @RealIp() 353 | 354 | The `RealIp` decorator is a custom NestJS decorator that retrieves the client's real IP address from an incoming HTTP request. It leverages the `@supercharge/request-ip` library to accurately identify the IP address, even if the client is behind a proxy or using a load balancer. 355 | 356 | Example for usage: 357 | 358 | ```typescript 359 | import { Controller, Get } from '@nestjs/common'; 360 | import { RealIp } from '@hodfords/nestjs-base-decorator'; 361 | 362 | @Controller('users') 363 | export class UserController { 364 | 365 | @Get('ip') 366 | getClientIp(@RealIp() ip: string): string { 367 | return `Client IP: ${ip}`; 368 | } 369 | } 370 | 371 | /* 372 | Request: GET /ip 373 | Response: "Client IP": "203.0.113.195" 374 | */ 375 | ``` 376 | 377 | #### @RequireToUploadFile() 378 | 379 | The `RequireToUploadFile` decorator is a custom NestJS decorator that simplifies file upload handling for single and multiple file uploads. It leverages NestJS `Interceptors` and custom interceptors to validate file uploads. The decorator allows developers to specify file upload configurations like file size limits, allowed MIME types, and custom storage engines. 380 | 381 | Parameters: 382 | 383 | - `fieldName`: Specifies the form field name used to upload the file(s). 384 | - `options`: An object that configures the file upload behavior. It includes: 385 | - `fileSize`: Maximum file size (in bytes) for the upload. Default is set to `10 * 1024 * 1024`. 386 | - `allowedMimeTypes`: Array of allowed MIME types for the uploaded files. Default is `['image/png', 387 | 'image/jpeg', 388 | 'image/jpg', 389 | 'image/svg+xml', 390 | 'image/bmp', 391 | 'image/heic', 392 | 'image/heif', 393 | 'application/pdf', 394 | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 395 | 'application/vnd.ms-excel']`. 396 | - `maxCount`: Maximum number of files that can be uploaded in a single request. 397 | - `storageEngine`: Custom storage engine for handling uploaded files (e.g., disk storage or cloud storage). 398 | 399 | Example for usage: 400 | 401 | ```typescript 402 | import { Controller, Post, UploadedFiles } from '@nestjs/common'; 403 | import { RequireToUploadFile } from '@hodfords/nestjs-base-decorator'; 404 | 405 | @Controller('files') 406 | export class FileController { 407 | @Post('upload') 408 | @RequireToUploadFile({ fieldName: 'file' }) 409 | uploadFile(@UploadedFiles() file: Express.Multer.File[]) { 410 | return file; 411 | } 412 | } 413 | ``` 414 | 415 | If you want only upload with one file and present it as a object, you can follow below: 416 | 417 | ```typescript 418 | import { Controller, Post, UploadedFile } from '@nestjs/common'; 419 | import { RequireToUploadFile } from '@hodfords/nestjs-base-decorator'; 420 | 421 | @Controller('files') 422 | export class FileController { 423 | @Post('upload') 424 | @RequireToUploadFile({ fieldName: 'file', options: { maxCount: 1 } }) 425 | uploadFile(@UploadedFile() file: Express.Multer.File) { 426 | return file; 427 | } 428 | } 429 | ``` 430 | 431 | #### @RequireToUploadFiles() 432 | 433 | The `RequireToUploadFiles` decorator simplifies the process of uploading multiple files with different field names in NestJS. It uses the `FileFieldsInterceptor` from `@nestjs/platform-express` to manage multiple file uploads from distinct form fields and allows customization through file upload options. The decorator integrates with custom interceptors for additional validation and handling of the uploaded files. 434 | 435 | Parameters: 436 | 437 | - `fieldNames`: 438 | - `name`: The name of the form field. 439 | - `maxCount`: (Optional) Maximum number of files allowed for this field. If not provided, defaults to the global `maxCount` value. 440 | - `options`: 441 | - `fileSize`: Maximum file size (in bytes). 442 | - `allowedMimeTypes`: Array of allowed MIME types for uploaded files. 443 | - `maxCount`: Default maximum number of files allowed per field. 444 | - `storageEngine`: Custom storage engine to handle file uploads. 445 | 446 | Example for usage: 447 | 448 | ```typescript 449 | import { Controller, Post, UploadedFile } from '@nestjs/common'; 450 | import { RequireToUploadFile } from '@hodfords/nestjs-base-decorator'; 451 | 452 | @Controller('files') 453 | export class FileController { 454 | @Post('upload') 455 | @RequireToUploadFiles({ 456 | fieldNames: [ 457 | { name: 'profile', maxCount: 1 }, 458 | { name: 'documents', maxCount: 5 } 459 | ] 460 | }) 461 | uploadUserFiles(@UploadedFiles() files: { profile: Express.Multer.File[]; documents: Express.Multer.File[] }) { 462 | return files; 463 | } 464 | } 465 | ``` 466 | 467 | #### @Sort() 468 | 469 | The `Sort` decorator simplifies handling sorting parameters in NestJS controllers. It defines two query parameters, `sortField` and `sortDirection`, allowing API clients to specify how to sort data in their requests. The decorator integrates with Swagger for automatic API documentation and validates the sorting fields and directions against predefined sets of allowed values. 470 | 471 | Parameters: 472 | 473 | - `sortParams`: 474 | - `allowedFields`: An array of allowed fields for sorting (e.g., `['name', 'createdAt']`). 475 | - `default`: An object with default sorting parameters: 476 | - `sortField`: The default field to sort by. 477 | - `sortDirection`: The default sorting direction (`ASC` or `DESC`). 478 | 479 | Example for usage: 480 | 481 | ```typescript 482 | import { Controller, Get, Query } from '@nestjs/common'; 483 | import { Sort, SortParams } from '@hodfords/nestjs-base-decorator'; 484 | 485 | @Controller('users') 486 | export class UserController { 487 | 488 | @Get() 489 | getUsers(@Sort({ allowedFields: ['name', 'createdAt'], default: { sortField: 'createdAt', sortDirection: 'DESC' } }) query: SortParams) { 490 | const { sortField, sortDirection } = query; 491 | return `Sorted by ${sortField} in ${sortDirection} order`; 492 | } 493 | } 494 | 495 | /* 496 | Request: GET ?sortField=createdAt&sortDirection=DESC 497 | Response: Sorted by createdAt in DESC order 498 | */ 499 | ``` 500 | 501 | #### @Sorts() 502 | 503 | The `Sorts` decorator provides an elegant solution for handling multiple sorting fields in NestJS controllers. It allows clients to specify multiple fields and their respective sort directions. The decorator automatically generates Swagger documentation for these parameters and ensures proper validation of both fields and directions. 504 | 505 | Parameters: 506 | 507 | - `sortParams`: 508 | - `allowedFields`: An array of allowed fields for sorting (e.g., `['name', 'createdAt']`). 509 | - `default`: An object with default sorting parameters: 510 | - `sortField`: The default field to sort by. 511 | - `sortDirection`: The default sorting direction (`ASC` or `DESC`). 512 | 513 | Example for usage: 514 | 515 | ```typescript 516 | import { Controller, Get, Query } from '@nestjs/common'; 517 | import { Sorts, SortMultipleParams } from '@hodfords/nestjs-base-decorator'; 518 | 519 | @Controller('users') 520 | export class UserController { 521 | 522 | @Get() 523 | getUsers( 524 | @Sorts({ allowedFields: ['name', 'createdAt'], default: { sortField: 'createdAt', sortDirection: 'DESC' } }) 525 | query: SortMultipleParams[] 526 | ) { 527 | return query; 528 | } 529 | } 530 | 531 | /* 532 | Request: GET ?sortFields=createdAt,name:DESC 533 | Response: [ 534 | { 535 | "field": "createdAt", 536 | "direction": "ASC" 537 | }, 538 | { 539 | "field": "name", 540 | "direction": "DESC" 541 | } 542 | ] 543 | */ 544 | ``` 545 | 546 | #### @Timestamp() 547 | 548 | The `Timestamp` decorator is used to extract and validate a timestamp from the request parameters or query in a NestJS controller. It ensures the timestamp is valid and handles optional or nullable parameters. 549 | 550 | Parameters: 551 | 552 | - `options`: 553 | - `key`: The name of the parameter to extract (required). 554 | - `nullable`: Indicates if the timestamp can be nullable (optional, default: `false`). 555 | 556 | Example for usage: 557 | 558 | ```typescript 559 | import { Controller, Get, Query } from '@nestjs/common'; 560 | import { Timestamp } from '@hodfords/nestjs-base-decorator'; 561 | 562 | @Controller('events') 563 | export class EventController { 564 | 565 | @Get() 566 | getEvents(@Timestamp('timestamp') timestamp: number) { 567 | return timestamp; 568 | } 569 | } 570 | 571 | /* 572 | Request: GET /events?timestamp=1581739337 573 | Response: 1581739337 574 | */ 575 | ``` 576 | 577 | #### @Timezone() 578 | 579 | The `Timezone` decorator is designed to validate and extract a timezone string from request parameters or query in a NestJS controller. It leverages the `dayjs` library along with the `timezone` and `utc` plugins to ensure that the provided timezone is valid. 580 | 581 | Parameters: 582 | 583 | - `options`: 584 | - `key`: The name of the parameter to extract (required). 585 | - `nullable`: Indicates if the timestamp can be nullable (optional, default: `false`). 586 | 587 | Example for usage: 588 | 589 | ```typescript 590 | import { Controller, Get, Query } from '@nestjs/common'; 591 | import { Timezone } from '@hodfords/nestjs-base-decorator'; 592 | 593 | @Controller('events') 594 | export class EventController { 595 | 596 | @Get() 597 | getEvents(@Timezone('timezone') timezone: string) { 598 | return timezone; 599 | } 600 | } 601 | 602 | /* 603 | Request: GET ?timezone=America/New_York 604 | Response: America/New_York 605 | */ 606 | ``` 607 | 608 | #### @UserAgent() 609 | 610 | The `UserAgent` decorator is a simple utility that extracts the `User-Agent` header from incoming requests in a NestJS application. This header typically contains information about the client's browser, operating system, and device. 611 | 612 | Example for usage: 613 | 614 | ```typescript 615 | import { Controller, Get, Query } from '@nestjs/common'; 616 | import { UserAgent } from '@hodfords/nestjs-base-decorator'; 617 | 618 | @Controller('users') 619 | export class UserController { 620 | 621 | @Get('user-agent') 622 | getUserAgent(@UserAgent() userAgent: string) { 623 | return userAgent; 624 | } 625 | } 626 | ``` 627 | 628 | #### @Value() 629 | 630 | The `Value` decorator is used to extract the `value` property from the payload of a microservice request in NestJS. It is a wrapper around the `Payload` decorator provided by `@nestjs/microservices`, allowing you to directly access the `value` property in microservice message handlers. 631 | 632 | Example for usage: 633 | 634 | ```typescript 635 | import { Controller } from '@nestjs/common'; 636 | import { EventPattern } from '@nestjs/microservices'; 637 | import { Value } from '@hodfords/nestjs-base-decorator'; 638 | 639 | @Controller() 640 | export class MathController { 641 | 642 | @EventPattern('math.add') 643 | handleAddition(@Value() value: number) { 644 | return `Value received: ${value}`; 645 | } 646 | } 647 | 648 | ``` 649 | 650 | ### Validators 651 | 652 | #### @ExistIds() 653 | 654 | The `@ExistIds()` decorator is used to validate whether all provided IDs exist in a specified database table column. It uses `TypeORM` to query the table and check if the given values match the records in the database. 655 | 656 | Parameters: 657 | 658 | - `table`: The `Entity` class (which extends `BaseEntity`) represents the database table where the validation will be performed. 659 | - `allowEmpty`: A boolean to allow empty arrays as valid input (optional, default: `false`). 660 | - `validationOptions`: `ValidationOptions` from `class-validator` to configure custom messages and options. 661 | 662 | Example for usage: 663 | 664 | ```typescript 665 | import { ExistIds } from '@hodfords/nestjs-base-decorator'; 666 | import { Entity } from 'typeorm'; 667 | import { BaseEntity } from '@hodfords/typeorm-helper'; 668 | 669 | @Entity() 670 | export class UserEntity extends BaseEntity { 671 | @PrimaryGeneratedColumn('uuid') 672 | id: string; 673 | } 674 | 675 | class UserRequestDto { 676 | @ExistIds(UserEntity) 677 | userIds: string[]; 678 | } 679 | ``` 680 | 681 | #### @Exists() 682 | 683 | The `@Exists()` decorator is used to validate if a specific value exists in a column of a database table. It supports case-insensitive searches and custom query conditions to allow more flexible validations. 684 | 685 | Parameters: 686 | 687 | - `table`: The `Entity` class (which extends `BaseEntity`) represents the database table where the validation will be performed. 688 | - `column`: The column name in the database to check the existence of the value. 689 | - `caseInsensitive`: A boolean to enable case-insensitive comparison (option, default: `false`). 690 | - `customs`: An array of custom conditions that apply additional filters to the query (optional). 691 | - `validationOptions`: `ValidationOptions` from `class-validator` to configure custom messages and options (optional). 692 | 693 | ```typescript 694 | import { Exists } from '@hodfords/nestjs-base-decorator'; 695 | import { Entity } from 'typeorm'; 696 | import { IsEmail } from 'class-validator'; 697 | import { BaseEntity } from '@hodfords/typeorm-helper'; 698 | 699 | @Entity() 700 | export class UserEntity extends BaseEntity { 701 | @Column() 702 | email: string; 703 | } 704 | 705 | class UserRequestDto { 706 | @Exists(UserEntity, 'email') 707 | @IsEmail() 708 | email: string; 709 | } 710 | ``` 711 | 712 | ## License 713 | 714 | This project is licensed under the MIT License 715 | --------------------------------------------------------------------------------