├── .demo └── fastify-ts-gen-demo.gif ├── .eslintrc.yml ├── .github └── workflows │ ├── codeql-analysis.yml │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── .snyk ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── fastify-typescript-generator.js ├── lib ├── .gitignore ├── cli.js ├── fastify-generator.js └── templates │ ├── express-structure-mongoose │ ├── .env.sample │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── config │ │ │ └── db.ts │ │ ├── dao │ │ │ ├── index.ts │ │ │ └── product.ts │ │ ├── index.ts │ │ ├── models │ │ │ └── product.ts │ │ ├── routes │ │ │ ├── health.ts │ │ │ └── product.ts │ │ └── server.ts │ ├── test │ │ └── routes.test.ts │ └── tsconfig.json │ ├── express-structure-typeorm │ ├── .env.sample │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── ormconfig.json │ ├── package.json │ ├── src │ │ ├── config │ │ │ └── db.ts │ │ ├── index.ts │ │ ├── models │ │ │ ├── index.ts │ │ │ └── product.ts │ │ ├── routes │ │ │ ├── health.ts │ │ │ └── product.ts │ │ └── server.ts │ ├── test │ │ └── server.test.ts │ └── tsconfig.json │ ├── plugin-structure-mongoose │ ├── .env.sample │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── dao │ │ │ ├── index.ts │ │ │ └── product.ts │ │ ├── index.ts │ │ ├── modules │ │ │ ├── health │ │ │ │ ├── routes.ts │ │ │ │ └── schema.ts │ │ │ └── products │ │ │ │ ├── entity.ts │ │ │ │ ├── routes.ts │ │ │ │ └── schema.ts │ │ ├── plugins │ │ │ └── db.ts │ │ └── server.ts │ ├── test │ │ └── routes.test.ts │ └── tsconfig.json │ └── plugin-structure-typeorm │ ├── .env.sample │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── ormconfig.json │ ├── package.json │ ├── src │ ├── index.ts │ ├── modules │ │ ├── health │ │ │ ├── routes.ts │ │ │ └── schema.ts │ │ └── products │ │ │ ├── entity.ts │ │ │ ├── routes.ts │ │ │ └── schema.ts │ ├── plugins │ │ └── db.ts │ └── server.ts │ ├── test │ └── server.test.ts │ └── tsconfig.json ├── package-lock.json └── package.json /.demo/fastify-ts-gen-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opendevs-org/fastify-typescript-generator/2ac845681e4476cb477157742645e23df03ba561/.demo/fastify-ts-gen-demo.gif -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true 3 | commonjs: true 4 | es2020: true 5 | extends: 'eslint:recommended' 6 | parserOptions: 7 | ecmaVersion: 11 8 | rules: 9 | no-useless-catch: 'off' 10 | no-undef: 'off' 11 | indent: 12 | - error 13 | - tab 14 | linebreak-style: 'off' 15 | quotes: 16 | - error 17 | - single 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 2 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | # Override automatic language detection by changing the below list 21 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 22 | language: ['javascript'] 23 | # Learn more... 24 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v2 29 | with: 30 | # We must fetch at least the immediate parents so that if this is 31 | # a pull request then we can checkout the head. 32 | fetch-depth: 2 33 | 34 | # If this run was triggered by a pull request event, then checkout 35 | # the head of the pull request instead of the merge commit. 36 | - run: git checkout HEAD^2 37 | if: ${{ github.event_name == 'pull_request' }} 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v1 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v1 63 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: 12 19 | - run: npm ci 20 | # - run: npm test 21 | 22 | publish-npm: 23 | needs: build 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v1 28 | with: 29 | node-version: 12 30 | registry-url: https://registry.npmjs.org/ 31 | - run: npm ci 32 | - run: npm publish 33 | env: 34 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ./**/node_modules 3 | 4 | demo 5 | 6 | .vscode 7 | lib/templates/**/package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ./**/node_modules 3 | coverage 4 | ./**/coverage -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.14.1 3 | ignore: {} 4 | patch: {} 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface. 13 | 3. Increase the version numbers in any examples files and the README.md, Package.json to the new version that this 14 | Pull Request would represent. 15 | 4. Use `npm run commit-a` to add and commit changes. 16 | 17 | ## Code of Conduct 18 | 19 | ### Our Pledge 20 | 21 | In the interest of fostering an open and welcoming environment, we as 22 | contributors and maintainers pledge to making participation in our project and 23 | our community a harassment-free experience for everyone, regardless of age, body 24 | size, disability, ethnicity, gender identity and expression, level of experience, 25 | nationality, personal appearance, race, religion, or sexual identity and 26 | orientation. 27 | 28 | ### Our Standards 29 | 30 | Examples of behavior that contributes to creating a positive environment 31 | include: 32 | 33 | * Using welcoming and inclusive language 34 | * Being respectful of differing viewpoints and experiences 35 | * Gracefully accepting constructive criticism 36 | * Focusing on what is best for the community 37 | * Showing empathy towards other community members 38 | 39 | Examples of unacceptable behavior by participants include: 40 | 41 | * The use of sexualized language or imagery and unwelcome sexual attention or 42 | advances 43 | * Trolling, insulting/derogatory comments, and personal or political attacks 44 | * Public or private harassment 45 | * Publishing others' private information, such as a physical or electronic 46 | address, without explicit permission 47 | * Other conduct which could reasonably be considered inappropriate in a 48 | professional setting 49 | 50 | ### Our Responsibilities 51 | 52 | Project maintainers are responsible for clarifying the standards of acceptable 53 | behavior and are expected to take appropriate and fair corrective action in 54 | response to any instances of unacceptable behavior. 55 | 56 | Project maintainers have the right and responsibility to remove, edit, or 57 | reject comments, commits, code, wiki edits, issues, and other contributions 58 | that are not aligned to this Code of Conduct, or to ban temporarily or 59 | permanently any contributor for other behaviors that they deem inappropriate, 60 | threatening, offensive, or harmful. 61 | 62 | ### Scope 63 | 64 | This Code of Conduct applies both within project spaces and in public spaces 65 | when an individual is representing the project or its community. Examples of 66 | representing a project or community include using an official project e-mail 67 | address, posting via an official social media account, or acting as an appointed 68 | representative at an online or offline event. Representation of a project may be 69 | further defined and clarified by project maintainers. 70 | 71 | ### Enforcement 72 | 73 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 74 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 75 | complaints will be reviewed and investigated and will result in a response that 76 | is deemed necessary and appropriate to the circumstances. The project team is 77 | obligated to maintain confidentiality with regard to the reporter of an incident. 78 | Further details of specific enforcement policies may be posted separately. 79 | 80 | Project maintainers who do not follow or enforce the Code of Conduct in good 81 | faith may face temporary or permanent repercussions as determined by other 82 | members of the project's leadership. 83 | 84 | ### Attribution 85 | 86 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 87 | available at [http://contributor-covenant.org/version/1/4][version] 88 | 89 | [homepage]: http://contributor-covenant.org 90 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sean Maxwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

welcome to fastify-typescript-generator 👋

2 |

3 | Version 4 | 5 | npm 6 | 7 | npm bundle size 8 | npm 9 | 10 | Documentation 11 | 12 | 13 | Maintenance 14 | 15 | 16 | License: MIT 17 | Snyk Vulnerabilities for npm package 18 | 19 |

20 | 21 | > generates new [fastify](https://www.fastify.io/) applications in everyone's favourite language [typescript](https://github.com/microsoft/TypeScript) with various options to choose from based on your project needs 22 | 23 | ### 🏠 [homepage](https://github.com/open-devs/fastify-typescript-generator#readme) 24 | 25 | ### 📰 [npm](https://www.npmjs.com/package/fastify-typescript-generator) 26 | 27 | ## 📸 demo 28 | 29 | ![fastify-typescript-generator-demo](.demo/fastify-ts-gen-demo.gif) 30 | 31 | ## install 32 | 33 | ```sh 34 | $ npm i -g fastify-typescript-generator 35 | ``` 36 | 37 | ## usage 38 | 39 | Run anyone of following commands: 40 | 41 | ```sh 42 | $ fastify-gen 43 | # or 44 | $ fastify-typescript-generator 45 | # or 46 | $ fastify-ts-gen 47 | ``` 48 | 49 | Or, to avoid installation and usage simply use the following command: 50 | 51 | ```sh 52 | $ npx fastify-typescript-generator 53 | ``` 54 | 55 | ## ❓ what is it 56 | 57 | creates a new fastify application as a starter boilerplate similar to the fastify-cli module. except this new application is configured to use typeScript instead of plain javascript and provides various options such as, kind of project structure to use with focus on plugin structure for fastify developers and express structure for developers with experience in developing with express, it also provides options for typeorm or mongoose. 58 | 59 | ## 🤔 why fastify-typescript-generator 60 | 61 | nodejs is great for the rapid development of web-projects, but is often neglected because of the lack of type safety. typescript solves this issue and (along with its linter file) can even make your code more robust than some other static languages like java. 62 | 63 | there are some other tools out there to generate fastify apps with javascript such as fastify-cli, but these either haven't been updated in a while or don't support typescript or don't support various development patterns. 64 | 65 | in this application you have two options to setup a project in fastify plugin architecture or express architecture, we have setup mongoose/typeorm integration, routes, swagger (in plugin structure only) for you. 66 | 67 | ## 📜 different options available explained 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
description of various options available
namedescription
plugin-structure-mongoosethis type of structure includes fastify plugin structure which relies on modules described as reusable plugins that contain their own entities, routes & schemas, additionally paired with swagger and mongoose for connection with mongodb
plugin-structure-typeormthis type of structure includes fastify plugin structure which relies on modules described as reusable plugins that contain their own entities, routes & schemas, additionally paired with swagger and typeorm for connection with various sql (like postgresql, mysql) and no-sql databases (like mongodb)
express-structure-mongoosethis type of structure includes express generator structure which relies on models, routes, dao, services, additionally paired with swagger and mongoose for connection with mongodb
express-structure-typeormthis type of structure includes express generator structure which relies on models, routes, services, additionally paired with swagger and typeorm for connection with various sql (like postgresql, mysql) and no-sql databases (like mongodb)
96 |
97 | 98 | happy app-deving 😊 99 | 100 | ## 👤 author 101 | 102 | **open devs (open.devs.github@gmail.com)** 103 | 104 | * website: https://opendevs.in/ 105 | * github: [@open-devs](https://github.com/open-devs) 106 | * core members: [@alok722](https://github.com/alok722), [@mikr13](https://github.com/mikr13) 107 | 108 | ## 🚀 future scope 109 | 110 | * adding docker & container configurations 111 | * adding template support 112 | * more template structures 113 | 114 | ## 🤝 contributing 115 | 116 | contributions, issues and feature requests are welcome!
feel free to check [issues page](https://github.com/open-devs/fastify-typescript-generator/issues). you can also take a look at the [contributing guide](https://github.com/open-devs/fastify-typescript-generator/blob/master/CONTRIBUTING.md). 117 | 118 | ## 🙌 show your support 119 | 120 | give a ⭐️ if this project helped you! 121 | 122 | Buy Me A Coffee 123 | 124 | ## 📝 license 125 | 126 | copyright © 2020 [open devs (open.devs.github@gmail.com)](https://github.com/open-devs).
127 | This project is [MIT](https://github.com/open-devs/fastify-typescript-generator/blob/master/LICENSE) licensed. 128 | 129 | *** 130 | _this README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ -------------------------------------------------------------------------------- /bin/fastify-typescript-generator.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/cli'); -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path'); 4 | const fastifyGenTs = require('./fastify-generator'); 5 | const { prompt } = require('prompts'); 6 | const { say } = require('cfonts'); 7 | const { red, green, yellow, blue } = require('chalk'); 8 | 9 | const options = [ 10 | { 11 | description: 'Fastify Plugin Structure with Mongoose and Swagger', 12 | defaultName: 'fastify-plugin-mongoose-swagger', 13 | templateName: 'plugin-structure-mongoose' 14 | }, { 15 | description: 'Fastify Plugin Structure with TypeORM and Swagger', 16 | defaultName: 'fastify-plugin-typeorm-swagger', 17 | templateName: 'plugin-structure-typeorm' 18 | }, { 19 | description: 'Fastify with Mongoose and Express Directory Structure', 20 | defaultName: 'fastify-express-mongoose', 21 | templateName: 'express-structure-mongoose' 22 | }, { 23 | description: 'Fastify with TypeORM and Express Directory Structure', 24 | defaultName: 'fastify-express-typeorm', 25 | templateName: 'express-structure-typeorm' 26 | }, 27 | ]; 28 | 29 | 30 | const getDest = (option, destFolder, projectName) => { 31 | return `${path.join(process.cwd(), destFolder)}/${projectName || option.defaultName}`; 32 | }; 33 | 34 | 35 | const initiate = async (option, p, projectName, git) => { 36 | try { 37 | const destination = getDest(option, p, projectName); 38 | await fastifyGenTs(option, destination, git); 39 | console.log(green(`Project setup complete! at ${path.resolve(destination)}`)) 40 | } catch (error) { 41 | console.error(error); 42 | } 43 | }; 44 | 45 | 46 | say('fastify typescript generator', { 47 | font: 'chrome', 48 | align: 'center', 49 | colors: ['system'], 50 | background: 'transparent', 51 | letterSpacing: 1, 52 | lineHeight: 1, 53 | space: true, 54 | }); 55 | 56 | 57 | say('opendevs', { 58 | font: 'chrome', 59 | align: 'right', 60 | colors: ['system'], 61 | background: 'transparent', 62 | letterSpacing: 1, 63 | lineHeight: 1, 64 | space: true, 65 | gradient: ['#fff', '#89d8d3'], 66 | }); 67 | 68 | 69 | console.log(blue('Setting up new Fastify TypeScript project...\n')); 70 | 71 | 72 | const onCancel = () => { 73 | console.log(red('Project creation failed!')); 74 | }; 75 | 76 | 77 | (async () => { 78 | const questions = [ 79 | { 80 | type: 'select', 81 | name: 'option', 82 | message: 'Select a option for the type of project', 83 | validate: value => value >= 1 && value <=4 ? true : 'Specify number in the range of 1 - 4', 84 | suggest: (input, choices) => choices.filter(i => i.value), 85 | choices: options.map((el, index) => ({ value: index, title: el.templateName, description: el.description })), 86 | fallback: { 87 | title: 'Using default', 88 | value: 1 89 | } 90 | }, 91 | { 92 | type: 'text', 93 | name: 'projectName', 94 | message: 'Specify project name', 95 | validate: projectName => /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName) ? true : 'Invalid project name, please follow npm guidelines' 96 | }, 97 | { 98 | type: 'text', 99 | name: 'path', 100 | message: '[OPTIONAL] Specify directory where you want to create the project, if no path is provided current directory will be used.', 101 | fallback: { 102 | title: 'Using default', 103 | value: './' 104 | } 105 | }, 106 | { 107 | type: 'confirm', 108 | name: 'git', 109 | message: '[OPTIONAL] Initialize git', 110 | initial: true 111 | } 112 | ]; 113 | 114 | const answers = await prompt(questions, { onCancel }); 115 | 116 | if (answers.option, answers.path, answers.projectName) { 117 | console.log(yellow('Starting Project creation...')); 118 | initiate(options[answers.option], answers.path, answers.projectName, answers.git); 119 | } 120 | 121 | 122 | })(); 123 | -------------------------------------------------------------------------------- /lib/fastify-generator.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { execSync } = require('child_process'); 3 | const ncp = require('ncp').ncp; 4 | const { writeFile, mkdir } = require('fs').promises; 5 | const { existsSync } = require('fs'); 6 | const { green, yellow } = require('chalk'); 7 | 8 | const copyProjectFiles = (config, destination) => { 9 | const prjFolder = `./templates/${config.templateName}`; 10 | const source = path.join(__dirname, prjFolder); 11 | return new Promise((resolve, reject) => { 12 | ncp.limit = 16; 13 | ncp(source, destination, { stopOnErr: true }, (err) => { 14 | if (err) { 15 | reject(err); 16 | } 17 | resolve(); 18 | }); 19 | }); 20 | }; 21 | 22 | 23 | const updatePackageJson = async (destination) => { 24 | try { 25 | const pathName = `${destination}/package.json`; 26 | let data = require(pathName); 27 | data.name = path.basename(destination); 28 | data = JSON.stringify(data, null, 2); 29 | await writeFile(pathName, data); 30 | } catch (err) { 31 | throw err; 32 | } 33 | }; 34 | 35 | 36 | const downloadNodeModules = (config, destination) => { 37 | const options = { cwd: destination, stdio: 'inherit' } 38 | console.log(yellow('Installing dependencies...\n')) 39 | execSync('npm install', options) 40 | console.log(green('Dependencies installation done...\n')) 41 | }; 42 | 43 | const setupGit = (destination) => { 44 | const options = { cwd: destination, stdio: 'inherit' } 45 | console.log(yellow('Initializing git...\n')) 46 | execSync('git init', options) 47 | } 48 | 49 | const fastifyGenTs = async (config, destination, git) => { 50 | try { 51 | if (!existsSync(destination)) { 52 | await mkdir(destination, { recursive: true }); 53 | } 54 | await copyProjectFiles(config, destination); 55 | await updatePackageJson(destination); 56 | git && setupGit(destination); 57 | downloadNodeModules(config, destination); 58 | } catch (err) { 59 | throw err; 60 | } 61 | }; 62 | 63 | 64 | module.exports = fastifyGenTs; 65 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/.env.sample: -------------------------------------------------------------------------------- 1 | MONGO_CONNECTION= 2 | MONGO_HOST=localhost 3 | MONGO_USERNAME= 4 | MONGO_PASSWORD= 5 | MONGO_PORT=27017 6 | MONGO_DATABASE=products -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es2020": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 11 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | "tab" 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "windows" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ], 35 | "no-useless-catch": "off", 36 | "@typescript-eslint/no-var-requires": "off", 37 | "@typescript-eslint/explicit-module-boundary-types": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .env -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/README.md: -------------------------------------------------------------------------------- 1 | # Generated using FASTIFY-Generator 2 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfig: 'tsconfig.json' 5 | } 6 | }, 7 | moduleFileExtensions: ['ts', 'tsx', 'js'], 8 | transform: { 9 | '^.+\\.(ts|tsx)$': 'ts-jest' 10 | }, 11 | testMatch: ['**/test/**/*.test.ts'], 12 | testEnvironment: 'node' 13 | }; 14 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --watch './src/*.ts' --exec ts-node ./src/index.ts", 8 | "build": "tsc", 9 | "test": "jest --coverage", 10 | "test:watch": "npm run test -- --watchAll", 11 | "commit": "git cz" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "dotenv": "8.2.0", 18 | "fastify": "^3.14.2", 19 | "fastify-cors": "5.2.0", 20 | "mongoose": "^5.12.3" 21 | }, 22 | "devDependencies": { 23 | "@types/cors": "^2.8.10", 24 | "@types/jest": "^26.0.22", 25 | "@types/node": "^14.14.37", 26 | "@types/mongoose": "^5.10.4", 27 | "jest": "^26.6.3", 28 | "nodemon": "^2.0.7", 29 | "ts-jest": "^26.5.4", 30 | "ts-node": "^9.1.1", 31 | "@typescript-eslint/eslint-plugin": "^4.22.0", 32 | "@typescript-eslint/parser": "^4.22.0", 33 | "eslint": "^7.24.0", 34 | "typescript": "^4.2.4", 35 | "git-cz": "^4.7.6" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/config/db.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const host = process.env.MONGO_HOST || 'localhost'; 4 | const port = process.env.MONGO_PORT || 27017; 5 | const database = process.env.MONGO_DATABASE || 'fastify'; 6 | 7 | mongoose.connect(`mongodb://${host}:${port}/${database}`, {useNewUrlParser: true, useUnifiedTopology: true}, (err) => { 8 | if (!err) 9 | console.log('MongoDB connection successful.'); 10 | else 11 | console.log('Error in DB connection : ' + JSON.stringify(err, undefined, 2)); 12 | }); 13 | 14 | module.exports = mongoose; -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/dao/index.ts: -------------------------------------------------------------------------------- 1 | export * from './product'; 2 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/dao/product.ts: -------------------------------------------------------------------------------- 1 | import Product from '../models/product'; 2 | 3 | export const getAllProducts = async () => { 4 | try { 5 | return await Product.find({}); 6 | } catch (err) { 7 | throw err; 8 | } 9 | }; 10 | 11 | export const getOneProduct = async (_id) => { 12 | try { 13 | return await Product.findOne({ _id }); 14 | } catch (err) { 15 | throw err; 16 | } 17 | }; 18 | 19 | export const createProduct = async (data) => { 20 | try { 21 | return await Product.create(data); 22 | } catch (err) { 23 | throw err; 24 | } 25 | }; 26 | 27 | export const updateProduct = async (_id, data) => { 28 | try { 29 | return await Product.updateOne({ _id }, data); 30 | } catch (err) { 31 | throw err; 32 | } 33 | }; 34 | 35 | export const deleteProduct = async (_id) => { 36 | try { 37 | return await Product.deleteOne({ _id }); 38 | } catch (err) { 39 | throw err; 40 | } 41 | }; -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/index.ts: -------------------------------------------------------------------------------- 1 | const server = require('./server'); 2 | 3 | const init = async () => { 4 | const fastify = await server(); 5 | 6 | fastify.listen(3000, (err, address) => { 7 | if (err) throw err; 8 | console.log(`fastify 🚀 server listening on ${address}`); 9 | }); 10 | 11 | return fastify; 12 | }; 13 | 14 | module.exports = init(); 15 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/models/product.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const productSchema = new Schema({ 6 | name: { type: String, required: true }, 7 | category: { type: String, required: true }, 8 | unit: { type: Number, required: true } 9 | }, { 10 | timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' }, 11 | }); 12 | 13 | export default mongoose.model('Product', productSchema); -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/routes/health.ts: -------------------------------------------------------------------------------- 1 | async function routes (fastify) { 2 | 3 | fastify.get('/', async (req, res) => { 4 | res.status(200).send('OK'); 5 | }); 6 | 7 | } 8 | 9 | module.exports = routes; -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/routes/product.ts: -------------------------------------------------------------------------------- 1 | import { getAllProducts, getOneProduct, createProduct, updateProduct, deleteProduct } from '../dao/index'; 2 | 3 | async function routes(fastify) { 4 | 5 | fastify.get('/', async (req, res) => { 6 | req.log.info('list products from db'); 7 | const products = await getAllProducts(); 8 | res.status(200).send(products); 9 | }); 10 | 11 | fastify.get('/:_id', async (req, res) => { 12 | req.log.info('Get one product from db'); 13 | const products = await getOneProduct(req.params._id); 14 | res.status(200).send(products); 15 | }); 16 | 17 | fastify.post('/', async (req, res) => { 18 | req.log.info('Add products to db'); 19 | const products = await createProduct(req.body); 20 | res.status(201).send(products); 21 | }); 22 | 23 | fastify.put('/:_id', async (req, res) => { 24 | req.log.info('Update product to db'); 25 | const products = await updateProduct(req.params._id, req.body); 26 | res.status(200).send(products); 27 | }); 28 | 29 | fastify.delete('/:_id', async (req, res) => { 30 | req.log.info(`delete product ${req.params._id} from db`); 31 | await deleteProduct(req.params._id); 32 | res.code(200).send('OK'); 33 | }); 34 | 35 | } 36 | 37 | module.exports = routes; 38 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/src/server.ts: -------------------------------------------------------------------------------- 1 | const fastify = require('fastify')(); 2 | require('../src/config/db'); 3 | 4 | const createServer = async () => { 5 | 6 | await fastify.register(require('fastify-cors')); 7 | 8 | await fastify.register(require('../src/routes/health'), { prefix: '/health' }); 9 | await fastify.register(require('../src/routes/product'), { prefix: '/product' }); 10 | 11 | fastify.setErrorHandler((error, req, res) => { 12 | req.log.error(error.toString()); 13 | res.send({ error }); 14 | }); 15 | 16 | return fastify; 17 | }; 18 | 19 | module.exports = createServer; 20 | -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/test/routes.test.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | describe('Product CRUD', () => { 4 | let server; 5 | 6 | beforeAll(async (done) => { 7 | done(); 8 | server = await require('../src/index'); 9 | }); 10 | 11 | afterAll(async (done) => { 12 | server.close(); 13 | await mongoose.connection.close(); 14 | done(); 15 | }); 16 | 17 | test('Add Product POST /product', async (done) => { 18 | server = await require('../src/index'); 19 | const response = await server.inject({ 20 | method: 'POST', 21 | url: '/product', 22 | payload: { 23 | _id: '5f2678dff22e1f4a3c0782ee', 24 | name: 'JBL Headphone', 25 | category: 'Electronic appliances', 26 | unit: 1 27 | } 28 | }); 29 | expect(response.statusCode).toBe(201); 30 | done(); 31 | }); 32 | 33 | test('Get All Product /product', async (done) => { 34 | const response = await server.inject({ 35 | method: 'GET', 36 | url: '/product' 37 | }); 38 | expect(response.statusCode).toBe(200); 39 | done(); 40 | }); 41 | 42 | test('Update Product PUT /product/:id', async (done) => { 43 | const response = await server.inject({ 44 | method: 'PUT', 45 | url: '/product/5f2678dff22e1f4a3c0782ee', 46 | payload: { 47 | unit: 2 48 | } 49 | }); 50 | expect(response.statusCode).toBe(200); 51 | done(); 52 | }); 53 | 54 | test('Get one Product GET /product/:id', async (done) => { 55 | const response = await server.inject({ 56 | method: 'GET', 57 | url: '/product/5f2678dff22e1f4a3c0782ee' 58 | }); 59 | expect(response.statusCode).toBe(200); 60 | done(); 61 | }); 62 | 63 | test('Delete one Product DELETE /product/:id', async (done) => { 64 | const response = await server.inject({ 65 | method: 'DELETE', 66 | url: '/product/5f2678dff22e1f4a3c0782ee' 67 | }); 68 | expect(response.statusCode).toBe(200); 69 | done(); 70 | }); 71 | 72 | test('Health Route', async (done) => { 73 | const response = await server.inject({ 74 | method: 'GET', 75 | url: '/health' 76 | }); 77 | expect(response.statusCode).toBe(200); 78 | done(); 79 | }); 80 | 81 | }); -------------------------------------------------------------------------------- /lib/templates/express-structure-mongoose/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "target": "es6", 7 | "allowJs": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "rootDir": ".", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "outDir": "dist", 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": ["node_modules/*"] 18 | }, 19 | "noImplicitAny": false, 20 | "lib": ["es6"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/.env.sample: -------------------------------------------------------------------------------- 1 | TYPEORM_CONNECTION= 2 | TYPEORM_HOST= 3 | TYPEORM_USERNAME= 4 | TYPEORM_PASSWORD= 5 | TYPEORM_DATABASE= 6 | TYPEORM_SYNCHRONIZE=true 7 | TYPEORM_LOGGING=true -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es2020": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 11 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | "tab" 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "windows" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ], 35 | "no-useless-catch": "off", 36 | "@typescript-eslint/no-var-requires": "off", 37 | "@typescript-eslint/explicit-module-boundary-types": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .env -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/README.md: -------------------------------------------------------------------------------- 1 | # Generated using FASTIFY-Generator 2 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfig: 'tsconfig.json' 5 | } 6 | }, 7 | moduleFileExtensions: ['ts', 'tsx', 'js'], 8 | transform: { 9 | '^.+\\.(ts|tsx)$': 'ts-jest' 10 | }, 11 | testMatch: ['**/test/**/*.test.ts'], 12 | testEnvironment: 'node' 13 | }; 14 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "mongodb", 3 | "url": "mongodb://localhost:27017/fastify", 4 | "useNewUrlParser": true, 5 | "synchronize": true, 6 | "logging": true, 7 | "entities": ["src/entity/*.*"], 8 | "useUnifiedTopology": true 9 | } -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --watch './src/**/*.ts' --exec ts-node ./src/index.ts", 8 | "build": "tsc", 9 | "test": "jest --coverage", 10 | "test:watch": "npm run test -- --watchAll", 11 | "commit": "git cz" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "dotenv": "^8.2.0", 18 | "fastify": "^3.14.2", 19 | "fastify-cors": "^5.2.0", 20 | "mongodb": "^3.6.6", 21 | "reflect-metadata": "^0.1.13", 22 | "typeorm": "^0.2.32" 23 | }, 24 | "devDependencies": { 25 | "@types/cors": "^2.8.10", 26 | "@types/jest": "^26.0.22", 27 | "@types/node": "^14.14.37", 28 | "@typescript-eslint/eslint-plugin": "^4.22.0", 29 | "@typescript-eslint/parser": "^4.22.0", 30 | "eslint": "^7.24.0", 31 | "jest": "^26.6.3", 32 | "nodemon": "^2.0.7", 33 | "ts-jest": "^26.5.4", 34 | "ts-node": "^9.1.1", 35 | "typescript": "^4.2.4", 36 | "git-cz": "^4.7.6" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/config/db.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import { createConnection, getConnectionOptions } from 'typeorm'; 3 | import { Product } from '../models/index'; 4 | 5 | async function db (server) { 6 | try { 7 | const connectionOptions = await getConnectionOptions(); 8 | Object.assign(connectionOptions, { 9 | options: { encrypt: true }, 10 | entities: [Product] 11 | }); 12 | 13 | const connection = await createConnection(connectionOptions); 14 | console.log('database connected'); 15 | 16 | server.decorate('db', { 17 | products: connection.getRepository(Product) 18 | }); 19 | 20 | } catch (error) { 21 | console.log(error); 22 | console.log('make sure you have set .env variables - see .env.sample'); 23 | } 24 | } 25 | 26 | module.exports = db; 27 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/index.ts: -------------------------------------------------------------------------------- 1 | const server = require('./server'); 2 | 3 | const init = async () => { 4 | const fastify = await server(); 5 | 6 | fastify.listen(3000, (err, address) => { 7 | if (err) throw err; 8 | console.log(`fastify 🚀 server listening on ${address}`); 9 | }); 10 | 11 | return fastify; 12 | }; 13 | 14 | module.exports = init(); 15 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './product'; 2 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/models/product.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | CreateDateColumn, 6 | UpdateDateColumn 7 | } from 'typeorm'; 8 | 9 | @Entity() 10 | export class Product { 11 | @PrimaryGeneratedColumn('uuid') 12 | _id: string 13 | 14 | @Column() 15 | name: string 16 | 17 | @Column() 18 | unit: number 19 | 20 | @Column() 21 | category: string 22 | 23 | @CreateDateColumn() 24 | created_at: string 25 | 26 | @UpdateDateColumn() 27 | updated_at: string 28 | } 29 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/routes/health.ts: -------------------------------------------------------------------------------- 1 | async function healthRoutes (fastify) { 2 | fastify.get('/', (req, res) => { 3 | res.send({ status: 'ok' }); 4 | }); 5 | } 6 | 7 | module.exports = healthRoutes; 8 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/routes/product.ts: -------------------------------------------------------------------------------- 1 | async function productRoutes (fastify) { 2 | fastify.get('/:_id', async (req, res) => { 3 | req.log.info('get one products from db'); 4 | const product = await fastify.db.products.findOne(req.params._id); 5 | res.send(product); 6 | }); 7 | 8 | fastify.get('/', async (req, res) => { 9 | req.log.info('list products from db'); 10 | const products = await fastify.db.products.find(); 11 | res.send(products); 12 | }); 13 | 14 | fastify.post('/', async (req, res) => { 15 | req.log.info('Add products to db'); 16 | const products = await fastify.db.products.save(req.body); 17 | res.status(201).send(products); 18 | }); 19 | 20 | fastify.put('/:_id', async (req, res) => { 21 | req.log.info('Update product to db'); 22 | const _id = req.params._id; 23 | const products = await fastify.db.products.save({ _id, ...req.body }); 24 | res.status(200).send(products); 25 | }); 26 | 27 | fastify.delete('/:_id', async (req, res) => { 28 | req.log.info(`delete product ${req.params._id} from db`); 29 | const product = await fastify.db.products.findOne(req.params._id); 30 | await fastify.db.products.remove(product); 31 | res.code(200).send({}); 32 | }); 33 | } 34 | 35 | module.exports = productRoutes; 36 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/src/server.ts: -------------------------------------------------------------------------------- 1 | const fastify = require('fastify')(); 2 | const db = require('../src/config/db'); 3 | 4 | const createServer = async () => { 5 | 6 | await db(fastify); 7 | 8 | await fastify.register(require('fastify-cors')); 9 | 10 | await fastify.register(require('../src/routes/health'), { prefix: '/health' }); 11 | await fastify.register(require('../src/routes/product'), { prefix: '/product' }); 12 | 13 | fastify.setErrorHandler((error, req, res) => { 14 | req.log.error(error.toString()); 15 | res.send({ error }); 16 | }); 17 | 18 | return fastify; 19 | }; 20 | 21 | module.exports = createServer; 22 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/test/server.test.ts: -------------------------------------------------------------------------------- 1 | import typeorm = require('typeorm'); 2 | 3 | const products = [ 4 | { 5 | _id: '5f2678dff22e1f4a3c0782ee', 6 | name: 'JBL Headphone', 7 | category: 'Electronic appliances', 8 | unit: 1, 9 | } 10 | ]; 11 | 12 | const dbMock = { 13 | Product: { 14 | find: jest.fn().mockReturnValue(products), 15 | findOne: jest.fn().mockReturnValue(products[0]), 16 | save: jest.fn().mockReturnValue(products[0]), 17 | remove: jest.fn(), 18 | }, 19 | }; 20 | 21 | typeorm.createConnection = jest.fn().mockReturnValue({ 22 | getRepository: (model) => dbMock[model.name], 23 | }); 24 | 25 | typeorm.getConnectionOptions = jest.fn().mockReturnValue({}); 26 | 27 | describe('Server', () => { 28 | let server; 29 | 30 | beforeEach(async () => { 31 | server = await require('../src/index'); 32 | await server.ready(); 33 | }); 34 | 35 | afterAll(() => server.close()); 36 | 37 | test('/health returns ok', (done) => { 38 | server.inject( 39 | { 40 | method: 'GET', 41 | url: '/health', 42 | }, 43 | (err, res) => { 44 | expect(res.statusCode).toBe(200); 45 | expect(JSON.parse(res.payload)).toEqual({ status: 'ok' }); 46 | done(err); 47 | } 48 | ); 49 | }); 50 | 51 | test('GET /product/:_id returns one of product by _id', (done) => { 52 | server.inject( 53 | { 54 | method: 'GET', 55 | url: `/product/${products[0]._id}`, 56 | }, 57 | (err, res) => { 58 | expect(res.statusCode).toBe(200); 59 | expect(dbMock.Product.findOne).toHaveBeenCalledWith(products[0]._id); 60 | expect(JSON.parse(res.payload)).toEqual(products[0]); 61 | done(err); 62 | } 63 | ); 64 | }); 65 | 66 | test('GET /product returns list of products', (done) => { 67 | server.inject( 68 | { 69 | method: 'GET', 70 | url: '/product', 71 | }, 72 | (err, res) => { 73 | expect(res.statusCode).toBe(200); 74 | expect(dbMock.Product.find).toHaveBeenCalledWith(); 75 | expect(JSON.parse(res.payload)[0]).toEqual(products[0]); 76 | done(err); 77 | } 78 | ); 79 | }); 80 | 81 | test('Add Product POST /product', async (done) => { 82 | const res = await server.inject({ 83 | method: 'POST', 84 | url: '/product', 85 | payload: { 86 | _id: '5f2678dff22e1f4a3c9992ee', 87 | name: 'Apple Headphone', 88 | category: 'Electronic appliances', 89 | unit: 2 90 | } 91 | }); 92 | expect(res.statusCode).toBe(201); 93 | done(); 94 | }); 95 | 96 | test('Update Product POST /product/:id', async (done) => { 97 | const res = await server.inject({ 98 | method: 'PUT', 99 | url: '/product/5f2678dff22e1f4a3c0782ee', 100 | payload: { 101 | unit: 2 102 | } 103 | }); 104 | expect(res.statusCode).toBe(200); 105 | done(); 106 | }); 107 | 108 | test('DELETE /product/:id deletes a product', (done) => { 109 | const { _id } = products[0]; 110 | server.inject( 111 | { 112 | method: 'DELETE', 113 | url: `/product/${_id}`, 114 | }, 115 | (err, res) => { 116 | expect(res.statusCode).toBe(200); 117 | expect(dbMock.Product.findOne).toHaveBeenCalledWith(_id); 118 | expect(dbMock.Product.remove).toHaveBeenCalledWith(products[0]); 119 | done(err); 120 | } 121 | ); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /lib/templates/express-structure-typeorm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "target": "es6", 7 | "allowJs": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "rootDir": ".", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "outDir": "dist", 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": ["node_modules/*"] 18 | }, 19 | "noImplicitAny": false, 20 | "lib": ["es6"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/.env.sample: -------------------------------------------------------------------------------- 1 | TYPEORM_CONNECTION= 2 | TYPEORM_HOST= 3 | TYPEORM_USERNAME= 4 | TYPEORM_PASSWORD= 5 | TYPEORM_DATABASE= -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es2020": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 11 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | "tab" 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "windows" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ], 35 | "no-useless-catch": "off", 36 | "@typescript-eslint/no-var-requires": "off", 37 | "@typescript-eslint/explicit-module-boundary-types": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .env -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/README.md: -------------------------------------------------------------------------------- 1 | # Generated using FASTIFY-Generator 2 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfig: 'tsconfig.json' 5 | } 6 | }, 7 | moduleFileExtensions: ['ts', 'tsx', 'js'], 8 | transform: { 9 | '^.+\\.(ts|tsx)$': 'ts-jest' 10 | }, 11 | testMatch: ['**/test/**/*.test.ts'], 12 | testEnvironment: 'node' 13 | }; 14 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --watch './src/**/*.ts' --exec ts-node ./src/index.ts", 8 | "build": "tsc", 9 | "test": "jest --coverage", 10 | "test:watch": "npm run test -- --watchAll", 11 | "commit": "git cz" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "fastify-cors": "^5.2.0", 18 | "dotenv": "^8.2.0", 19 | "fastify": "^3.14.2", 20 | "fastify-oas": "^3.0.8", 21 | "mongoose": "5.12.3" 22 | }, 23 | "devDependencies": { 24 | "@types/babel__core": "^7.1.14", 25 | "@types/cors": "^2.8.10", 26 | "@types/jest": "^26.0.22", 27 | "@types/mongoose": "^5.10.4", 28 | "@types/node": "^14.14.37", 29 | "@typescript-eslint/eslint-plugin": "^4.22.0", 30 | "@typescript-eslint/parser": "^4.22.0", 31 | "eslint": "^7.24.0", 32 | "git-cz": "^4.7.6", 33 | "jest": "^26.6.3", 34 | "nodemon": "^2.0.7", 35 | "ts-jest": "^26.5.4", 36 | "ts-node": "^9.1.1", 37 | "typescript": "^4.2.4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/dao/index.ts: -------------------------------------------------------------------------------- 1 | export * from './product'; 2 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/dao/product.ts: -------------------------------------------------------------------------------- 1 | import Product from '../modules/products/entity'; 2 | 3 | export const getAllProducts = async () => { 4 | try { 5 | return await Product.find({}); 6 | } catch (err) { 7 | throw err; 8 | } 9 | }; 10 | 11 | export const getOneProduct = async (_id) => { 12 | try { 13 | return await Product.findOne({ _id }); 14 | } catch (err) { 15 | throw err; 16 | } 17 | }; 18 | 19 | export const createProduct = async (data) => { 20 | try { 21 | // TODO: failing at below line: debug 22 | return await Product.create(data); 23 | } catch (err) { 24 | throw err; 25 | } 26 | }; 27 | 28 | export const updateProduct = async (_id, data) => { 29 | try { 30 | return await Product.updateOne({ _id }, data); 31 | } catch (err) { 32 | throw err; 33 | } 34 | }; 35 | 36 | export const deleteProduct = async (_id) => { 37 | try { 38 | return await Product.deleteOne({ _id }); 39 | } catch (err) { 40 | throw err; 41 | } 42 | }; -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/index.ts: -------------------------------------------------------------------------------- 1 | import createServer from './server'; 2 | 3 | const PORT = process.env.PORT || '3000'; 4 | const server = createServer(); 5 | 6 | server.listen(+PORT, '0.0.0.0', (err, address) => { 7 | if (err) throw err; 8 | console.log(`server listening on ${address}`); 9 | }); 10 | 11 | module.exports = server; 12 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/modules/health/routes.ts: -------------------------------------------------------------------------------- 1 | import { getHealthSchema } from './schema'; 2 | 3 | export default function healthHandler(server, options, next) { 4 | server.get('/', { schema: getHealthSchema }, (req, res) => { 5 | res.send({ status: 'ok' }); 6 | }); 7 | 8 | next(); 9 | } 10 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/modules/health/schema.ts: -------------------------------------------------------------------------------- 1 | export const getHealthSchema = { 2 | summary: 'health check', 3 | description: 'health check', 4 | response: { 5 | 200: { 6 | type: 'object', 7 | properties: { 8 | status: { 9 | type: 'string' 10 | } 11 | } 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/modules/products/entity.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const productSchema = new Schema({ 6 | name: { type: String, required: true }, 7 | category: { type: String, required: true }, 8 | unit: { type: Number, required: true } 9 | }, { 10 | timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' }, 11 | }); 12 | 13 | export default mongoose.model('Product', productSchema); -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/modules/products/routes.ts: -------------------------------------------------------------------------------- 1 | import { listProductsSchema, deleteProductSchema } from './schema'; 2 | import { getAllProducts, getOneProduct, createProduct, updateProduct, deleteProduct } from '../../dao/index'; 3 | 4 | export default function productHandler(server, options, next) { 5 | server.get( 6 | '/', 7 | { schema: listProductsSchema }, 8 | async (req, res) => { 9 | req.log.info('list products from db'); 10 | const products = await getAllProducts(); 11 | res.send(products); 12 | } 13 | ); 14 | 15 | server.get('/:_id', async (req, res) => { 16 | req.log.info('get one products from db'); 17 | const products = await getOneProduct(req.params._id); 18 | res.status(200).send(products); 19 | }); 20 | 21 | server.post('/', async (req, res) => { 22 | req.log.info('Add products to db'); 23 | const products = await createProduct(req.body); 24 | res.status(201).send(products); 25 | }); 26 | 27 | server.put('/:_id', async (req, res) => { 28 | req.log.info('Update product to db'); 29 | const products = await updateProduct(req.params._id, req.body); 30 | res.status(200).send(products); 31 | }); 32 | 33 | server.delete( 34 | '/:_id', 35 | { schema: deleteProductSchema }, 36 | async (req, res) => { 37 | req.log.info(`delete product ${req.params._id} from db`); 38 | await deleteProduct(req.params._id); 39 | res.code(200).send('OK'); 40 | } 41 | ); 42 | 43 | next(); 44 | } 45 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/modules/products/schema.ts: -------------------------------------------------------------------------------- 1 | export const productSchema = { 2 | _id: { type: 'string', format: 'uuid' }, 3 | name: { type: 'string' }, 4 | unit: { type: 'number' }, 5 | category: { type: 'string' }, 6 | created_at: { type: 'string', format: 'date-time' }, 7 | updated_at: { type: 'string', format: 'date-time' } 8 | }; 9 | 10 | export const listProductsSchema = { 11 | summary: 'products', 12 | description: 'products', 13 | response: { 14 | 200: { 15 | type: 'array', 16 | items: { 17 | properties: productSchema 18 | } 19 | } 20 | } 21 | }; 22 | 23 | export const deleteProductSchema = { 24 | summary: 'delete product', 25 | description: 'delete product', 26 | params: { 27 | type: 'object', 28 | required: ['_id'], 29 | properties: { 30 | _id: { type: 'string' } 31 | } 32 | }, 33 | response: { 34 | 200: { 35 | type: 'boolean' 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/plugins/db.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const host = process.env.MONGO_HOST || 'localhost'; 4 | const port = process.env.MONGO_PORT || 27017; 5 | const database = process.env.MONGO_DATABASE || 'fastify'; 6 | 7 | mongoose.connect(`mongodb://${host}:${port}/${database}`, {useNewUrlParser: true, useUnifiedTopology: true}, (err) => { 8 | if (!err) 9 | console.log('MongoDB connection successful.'); 10 | else 11 | console.log('Error in DB connection : ' + JSON.stringify(err, undefined, 2)); 12 | }); 13 | 14 | module.exports = mongoose; -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/src/server.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify'; 2 | 3 | import healthHandler from './modules/health/routes'; 4 | import productsHandler from './modules/products/routes'; 5 | 6 | require('./plugins/db'); 7 | 8 | function createServer() { 9 | const server = fastify(); 10 | server.register(require('fastify-cors')); 11 | 12 | server.register(require('fastify-oas'), { 13 | routePrefix: '/docs', 14 | exposeRoute: true, 15 | swagger: { 16 | info: { 17 | title: 'product api', 18 | description: 'api documentation', 19 | version: '0.1.0' 20 | }, 21 | servers: [ 22 | { url: 'http://localhost:3000', description: 'development' }, 23 | { 24 | url: 'https://', 25 | description: 'production' 26 | } 27 | ], 28 | schemes: ['http'], 29 | consumes: ['application/json'], 30 | produces: ['application/json'], 31 | } 32 | }); 33 | 34 | server.register(healthHandler, { prefix: '/health' }); 35 | server.register(productsHandler, { prefix: '/product' }); 36 | 37 | server.setErrorHandler((error, req, res) => { 38 | req.log.error(error.toString()); 39 | res.send({ error }); 40 | }); 41 | 42 | return server; 43 | } 44 | 45 | export default createServer; 46 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/test/routes.test.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | describe('Product CRUD', () => { 4 | let server; 5 | 6 | beforeAll(async (done) => { 7 | done(); 8 | server = await require('../src/index'); 9 | await server.ready(); 10 | }); 11 | 12 | afterAll(async (done) => { 13 | server.close(); 14 | await mongoose.connection.close(); 15 | done(); 16 | }); 17 | 18 | test('Add Product POST /product', async (done) => { 19 | server = await require('../src/index'); 20 | const response = await server.inject({ 21 | method: 'POST', 22 | url: '/product', 23 | payload: { 24 | _id: '5f2678dff22e1f4a3c0782ee', 25 | name: 'JBL Headphone', 26 | category: 'Electronic appliances', 27 | unit: 1 28 | } 29 | }); 30 | expect(response.statusCode).toBe(201); 31 | done(); 32 | }); 33 | 34 | test('Get All Product /product', async (done) => { 35 | const response = await server.inject({ 36 | method: 'GET', 37 | url: '/product' 38 | }); 39 | expect(response.statusCode).toBe(200); 40 | done(); 41 | }); 42 | 43 | test('Update Product PUT /product/:id', async (done) => { 44 | const response = await server.inject({ 45 | method: 'PUT', 46 | url: '/product/5f2678dff22e1f4a3c0782ee', 47 | payload: { 48 | unit: 2 49 | } 50 | }); 51 | expect(response.statusCode).toBe(200); 52 | done(); 53 | }); 54 | 55 | test('Get one Product GET /product/:id', async (done) => { 56 | const response = await server.inject({ 57 | method: 'GET', 58 | url: '/product/5f2678dff22e1f4a3c0782ee' 59 | }); 60 | expect(response.statusCode).toBe(200); 61 | done(); 62 | }); 63 | 64 | test('Delete one Product DELETE /product/:_id', async (done) => { 65 | const response = await server.inject({ 66 | method: 'DELETE', 67 | url: '/product/5f2678dff22e1f4a3c0782ee' 68 | }); 69 | expect(response.statusCode).toBe(200); 70 | done(); 71 | }); 72 | 73 | test('Health Route', async (done) => { 74 | const response = await server.inject({ 75 | method: 'GET', 76 | url: '/health' 77 | }); 78 | expect(response.statusCode).toBe(200); 79 | done(); 80 | }); 81 | 82 | }); -------------------------------------------------------------------------------- /lib/templates/plugin-structure-mongoose/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "target": "es6", 7 | "allowJs": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "rootDir": ".", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "outDir": "dist", 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": ["node_modules/*"] 18 | }, 19 | "noImplicitAny": false, 20 | "lib": ["es6"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/.env.sample: -------------------------------------------------------------------------------- 1 | TYPEORM_CONNECTION= 2 | TYPEORM_HOST= 3 | TYPEORM_USERNAME= 4 | TYPEORM_PASSWORD= 5 | TYPEORM_DATABASE= 6 | TYPEORM_SYNCHRONIZE=true 7 | TYPEORM_LOGGING=true -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es2020": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 11 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | "tab" 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "windows" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ], 35 | "no-useless-catch": "off", 36 | "@typescript-eslint/no-var-requires": "off", 37 | "@typescript-eslint/explicit-module-boundary-types": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .env -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/README.md: -------------------------------------------------------------------------------- 1 | # Generated using FASTIFY-Generator 2 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | 'ts-jest': { 4 | tsConfig: 'tsconfig.json' 5 | } 6 | }, 7 | moduleFileExtensions: ['ts', 'tsx', 'js'], 8 | transform: { 9 | '^.+\\.(ts|tsx)$': 'ts-jest' 10 | }, 11 | testMatch: ['**/test/**/*.test.ts'], 12 | testEnvironment: 'node' 13 | }; 14 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "mongodb", 3 | "url": "mongodb://localhost:27017/fastify", 4 | "useNewUrlParser": true, 5 | "synchronize": true, 6 | "logging": true, 7 | "entities": ["src/entity/*.*"], 8 | "useUnifiedTopology": true 9 | } -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --watch './src/**/*.ts' --exec ts-node ./src/index.ts", 8 | "build": "tsc", 9 | "test": "jest --coverage", 10 | "test:watch": "npm run test -- --watchAll", 11 | "commit": "git cz" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "dotenv": "^8.2.0", 18 | "fastify": "^3.14.2", 19 | "fastify-cors": "^5.2.0", 20 | "fastify-oas": "^3.0.8", 21 | "fastify-plugin": "3.0.0", 22 | "mongodb": "^3.6.6", 23 | "reflect-metadata": "^0.1.13", 24 | "typeorm": "^0.2.32" 25 | }, 26 | "devDependencies": { 27 | "@types/cors": "^2.8.10", 28 | "@types/jest": "^26.0.22", 29 | "@types/node": "^14.14.37", 30 | "jest": "^26.6.3", 31 | "nodemon": "^2.0.7", 32 | "ts-jest": "^26.5.4", 33 | "ts-node": "^9.1.1", 34 | "typescript": "^4.2.4", 35 | "@typescript-eslint/eslint-plugin": "^4.22.0", 36 | "@typescript-eslint/parser": "^4.22.0", 37 | "eslint": "^7.24.0", 38 | "git-cz": "^4.7.6" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/index.ts: -------------------------------------------------------------------------------- 1 | import createServer from './server'; 2 | 3 | const PORT = process.env.PORT || '3000'; 4 | const server = createServer(); 5 | 6 | server.listen(+PORT, '0.0.0.0', (err, address) => { 7 | if (err) throw err; 8 | console.log(`server listening on ${address}`); 9 | }); 10 | 11 | module.exports = server; 12 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/modules/health/routes.ts: -------------------------------------------------------------------------------- 1 | import { getHealthSchema } from './schema'; 2 | 3 | export default function healthHandler(server, options, next) { 4 | server.get('/', { schema: getHealthSchema }, (req, res) => { 5 | res.send({ status: 'ok' }); 6 | }); 7 | 8 | next(); 9 | } 10 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/modules/health/schema.ts: -------------------------------------------------------------------------------- 1 | export const getHealthSchema = { 2 | summary: 'health check', 3 | description: 'health check', 4 | response: { 5 | 200: { 6 | type: 'object', 7 | properties: { 8 | status: { 9 | type: 'string' 10 | } 11 | } 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/modules/products/entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | CreateDateColumn, 6 | UpdateDateColumn 7 | } from 'typeorm'; 8 | 9 | @Entity() 10 | export class Product { 11 | @PrimaryGeneratedColumn('uuid') 12 | _id: string 13 | 14 | @Column() 15 | name: string 16 | 17 | @Column() 18 | unit: number 19 | 20 | @Column() 21 | category: string 22 | 23 | @CreateDateColumn() 24 | created_at: string 25 | 26 | @UpdateDateColumn() 27 | updated_at: string 28 | } 29 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/modules/products/routes.ts: -------------------------------------------------------------------------------- 1 | import { listProductsSchema, deleteProductSchema } from './schema'; 2 | 3 | export default function productHandler(server, options, next) { 4 | server.get( 5 | '/', 6 | { schema: listProductsSchema }, 7 | async (req, res) => { 8 | req.log.info('list products from db'); 9 | const products = await server.db.products.find(); 10 | res.send(products); 11 | } 12 | ); 13 | 14 | server.get('/:_id', async (req, res) => { 15 | req.log.info('get one products from db'); 16 | const product = await server.db.products.findOne(req.params._id); 17 | res.send(product); 18 | }); 19 | 20 | server.post('/', async (req, res) => { 21 | req.log.info('Add products to db'); 22 | const products = await server.db.products.save(req.body); 23 | res.status(201).send(products); 24 | }); 25 | 26 | server.put('/:_id', async (req, res) => { 27 | req.log.info('Update product to db'); 28 | const _id = req.params._id; 29 | const products = await server.db.products.save({ _id, ...req.body }); 30 | res.status(200).send(products); 31 | }); 32 | 33 | server.delete( 34 | '/:_id', 35 | { schema: deleteProductSchema }, 36 | async (req, res) => { 37 | req.log.info(`delete product ${req.params._id} from db`); 38 | const product = await server.db.products.findOne(req.params._id); 39 | await server.db.products.remove(product); 40 | res.code(200).send({}); 41 | } 42 | ); 43 | 44 | next(); 45 | } 46 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/modules/products/schema.ts: -------------------------------------------------------------------------------- 1 | export const productSchema = { 2 | _id: { type: 'string', format: 'uuid' }, 3 | name: { type: 'string' }, 4 | unit: { type: 'number' }, 5 | category: { type: 'string' }, 6 | created_at: { type: 'string', format: 'date-time' }, 7 | updated_at: { type: 'string', format: 'date-time' } 8 | }; 9 | 10 | export const listProductsSchema = { 11 | summary: 'products', 12 | description: 'products', 13 | response: { 14 | 200: { 15 | type: 'array', 16 | items: { 17 | properties: productSchema 18 | } 19 | } 20 | } 21 | }; 22 | 23 | export const deleteProductSchema = { 24 | summary: 'delete product', 25 | description: 'delete product', 26 | params: { 27 | type: 'object', 28 | required: ['_id'], 29 | properties: { 30 | _id: { type: 'string' } 31 | } 32 | }, 33 | response: { 34 | 200: { 35 | type: 'boolean' 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/plugins/db.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import fp from 'fastify-plugin'; 3 | import { createConnection, getConnectionOptions } from 'typeorm'; 4 | import { Product } from '../modules/products/entity'; 5 | 6 | export default fp(async server => { 7 | try { 8 | const connectionOptions = await getConnectionOptions(); 9 | Object.assign(connectionOptions, { 10 | options: { encrypt: true }, 11 | entities: [Product] 12 | }); 13 | 14 | const connection = await createConnection(connectionOptions); 15 | console.log('database connected'); 16 | 17 | server.decorate('db', { 18 | products: connection.getRepository(Product) 19 | }); 20 | } catch (error) { 21 | console.log(error); 22 | console.log('make sure you have set .env variables - see .env.sample'); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/src/server.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify'; 2 | 3 | import db from './plugins/db'; 4 | import healthHandler from './modules/health/routes'; 5 | import productsHandler from './modules/products/routes'; 6 | 7 | function createServer() { 8 | const server = fastify(); 9 | server.register(require('fastify-cors')); 10 | 11 | server.register(require('fastify-oas'), { 12 | routePrefix: '/docs', 13 | exposeRoute: true, 14 | swagger: { 15 | info: { 16 | title: 'product api', 17 | description: 'api documentation', 18 | version: '0.1.0' 19 | }, 20 | servers: [ 21 | { url: 'http://localhost:3000', description: 'development' }, 22 | { 23 | url: 'https://', 24 | description: 'production' 25 | } 26 | ], 27 | schemes: ['http'], 28 | consumes: ['application/json'], 29 | produces: ['application/json'], 30 | } 31 | }); 32 | 33 | server.register(db); 34 | server.register(healthHandler, { prefix: '/health' }); 35 | server.register(productsHandler, { prefix: '/product' }); 36 | 37 | server.setErrorHandler((error, req, res) => { 38 | req.log.error(error.toString()); 39 | res.send({ error }); 40 | }); 41 | 42 | return server; 43 | } 44 | 45 | export default createServer; 46 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/test/server.test.ts: -------------------------------------------------------------------------------- 1 | import typeorm = require('typeorm'); 2 | 3 | const products = [ 4 | { 5 | _id: '5f2678dff22e1f4a3c0782ee', 6 | name: 'JBL Headphone', 7 | category: 'Electronic appliances', 8 | unit: 1, 9 | } 10 | ]; 11 | 12 | const dbMock = { 13 | Product: { 14 | find: jest.fn().mockReturnValue(products), 15 | findOne: jest.fn().mockReturnValue(products[0]), 16 | save: jest.fn().mockReturnValue(products[0]), 17 | remove: jest.fn(), 18 | }, 19 | }; 20 | 21 | typeorm.createConnection = jest.fn().mockReturnValue({ 22 | getRepository: (model) => dbMock[model.name], 23 | }); 24 | 25 | typeorm.getConnectionOptions = jest.fn().mockReturnValue({}); 26 | 27 | describe('Server', () => { 28 | let server; 29 | 30 | beforeEach(async () => { 31 | server = await require('../src/index'); 32 | await server.ready(); 33 | }); 34 | 35 | afterAll(() => server.close()); 36 | 37 | test('/health returns ok', (done) => { 38 | server.inject( 39 | { 40 | method: 'GET', 41 | url: '/health', 42 | }, 43 | (err, res) => { 44 | expect(res.statusCode).toBe(200); 45 | expect(JSON.parse(res.payload)).toEqual({ status: 'ok' }); 46 | done(err); 47 | } 48 | ); 49 | }); 50 | 51 | test('GET /product/:_id returns one of product by _id', (done) => { 52 | server.inject( 53 | { 54 | method: 'GET', 55 | url: `/product/${products[0]._id}`, 56 | }, 57 | (err, res) => { 58 | expect(res.statusCode).toBe(200); 59 | expect(dbMock.Product.findOne).toHaveBeenCalledWith(products[0]._id); 60 | expect(JSON.parse(res.payload)).toEqual(products[0]); 61 | done(err); 62 | } 63 | ); 64 | }); 65 | 66 | test('GET /product returns list of products', (done) => { 67 | server.inject( 68 | { 69 | method: 'GET', 70 | url: '/product', 71 | }, 72 | (err, res) => { 73 | expect(res.statusCode).toBe(200); 74 | expect(dbMock.Product.find).toHaveBeenCalledWith(); 75 | expect(JSON.parse(res.payload)[0]).toEqual(products[0]); 76 | done(err); 77 | } 78 | ); 79 | }); 80 | 81 | test('Add Product POST /product', async (done) => { 82 | const res = await server.inject({ 83 | method: 'POST', 84 | url: '/product', 85 | payload: { 86 | _id: '5f2678dff22e1f4a3c9992ee', 87 | name: 'Apple Headphone', 88 | category: 'Electronic appliances', 89 | unit: 2 90 | } 91 | }); 92 | expect(res.statusCode).toBe(201); 93 | done(); 94 | }); 95 | 96 | test('Update Product POST /product/:id', async (done) => { 97 | const res = await server.inject({ 98 | method: 'PUT', 99 | url: '/product/5f2678dff22e1f4a3c0782ee', 100 | payload: { 101 | unit: 2 102 | } 103 | }); 104 | expect(res.statusCode).toBe(200); 105 | done(); 106 | }); 107 | 108 | test('DELETE /product/:id deletes a product', (done) => { 109 | const { _id } = products[0]; 110 | server.inject( 111 | { 112 | method: 'DELETE', 113 | url: `/product/${_id}`, 114 | }, 115 | (err, res) => { 116 | expect(res.statusCode).toBe(200); 117 | expect(dbMock.Product.findOne).toHaveBeenCalledWith(_id); 118 | expect(dbMock.Product.remove).toHaveBeenCalledWith(products[0]); 119 | done(err); 120 | } 121 | ); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /lib/templates/plugin-structure-typeorm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "target": "es6", 7 | "allowJs": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "rootDir": ".", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "outDir": "dist", 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": ["node_modules/*"] 18 | }, 19 | "noImplicitAny": false, 20 | "lib": ["es6"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-typescript-generator", 3 | "version": "0.3.0", 4 | "description": "Generate new Fastify applications similar to express-generate which but sets it up to use TypeScript instead", 5 | "preferGlobal": true, 6 | "scripts": { 7 | "fastify-generator-typescript": "node lib/cli.js", 8 | "start": "node lib/cli.js", 9 | "test": "snyk test", 10 | "commit-a": "git add . && git cz", 11 | "commit": "git cz", 12 | "snyk-protect": "snyk protect" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/open-devs/fastify-typescript-generator.git" 17 | }, 18 | "main": "./lib/cli.js", 19 | "module": "./lib/cli.js", 20 | "unpkg": "./lib/cli.js", 21 | "bin": { 22 | "fastify-gen": "./bin/fastify-typescript-generator.js", 23 | "fastify-ts-gen": "./bin/fastify-typescript-generator.js", 24 | "fastify-typescript-generator": "./bin/fastify-typescript-generator.js" 25 | }, 26 | "keywords": [ 27 | "generator", 28 | "generate", 29 | "fastify", 30 | "fast", 31 | "boilerplate", 32 | "starter", 33 | "start", 34 | "template", 35 | "express", 36 | "ex", 37 | "mongodb", 38 | "mongo", 39 | "typeorm", 40 | "orm", 41 | "swagger", 42 | "open", 43 | "open-api", 44 | "gen", 45 | "typescript", 46 | "ts", 47 | "types", 48 | "typing", 49 | "overnight", 50 | "overnightjs", 51 | "js", 52 | "new", 53 | "app", 54 | "decorators", 55 | "decorate", 56 | "test", 57 | "testing", 58 | "routes", 59 | "router", 60 | "routing", 61 | "fastify-typescript-generator", 62 | "fastify-typescript-gen", 63 | "fastify-ts-gen" 64 | ], 65 | "author": "open devs (open.devs.github@gmail.com)", 66 | "contributors": [ 67 | "Mihir Kumar (mikr13)", 68 | "Alok Raj (alok722)" 69 | ], 70 | "license": "MIT", 71 | "private": false, 72 | "bugs": { 73 | "url": "https://github.com/open-devs/fastify-typescript-generator/issues" 74 | }, 75 | "homepage": "https://github.com/open-devs/fastify-typescript-generator#readme", 76 | "dependencies": { 77 | "cfonts": "^2.9.1", 78 | "chalk": "^4.1.0", 79 | "ncp": "^2.0.0", 80 | "prompts": "^2.4.1", 81 | "snyk": "^1.534.0" 82 | }, 83 | "devDependencies": { 84 | "eslint": "^7.24.0", 85 | "git-cz": "^4.7.6" 86 | }, 87 | "config": { 88 | "commitizen": { 89 | "path": "./node_modules/cz-conventional-changelog" 90 | } 91 | }, 92 | "snyk": true 93 | } 94 | --------------------------------------------------------------------------------