{"id":13300746,"url":"https://github.com/nicolastrote/cat-fight","last_synced_at":"2025-03-10T11:33:02.582Z","repository":{"id":48877856,"uuid":"229335399","full_name":"nicolastrote/cat-fight","owner":"nicolastrote","description":"Project cat-fight","archived":false,"fork":false,"pushed_at":"2023-08-01T16:20:01.000Z","size":2748,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-08-01T19:03:42.325Z","etag":null,"topics":["fastify","graphql","mongo","mongodb","nodejs","react","react-bootstrap","react-hooks","redux","redux-saga","rest-api","scss"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nicolastrote.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2019-12-20T20:58:49.000Z","updated_at":"2023-08-01T19:03:42.326Z","dependencies_parsed_at":"2023-01-28T13:26:35.610Z","dependency_job_id":null,"html_url":"https://github.com/nicolastrote/cat-fight","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolastrote%2Fcat-fight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolastrote%2Fcat-fight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolastrote%2Fcat-fight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolastrote%2Fcat-fight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicolastrote","download_url":"https://codeload.github.com/nicolastrote/cat-fight/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242843516,"owners_count":20194379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastify","graphql","mongo","mongodb","nodejs","react","react-bootstrap","react-hooks","redux","redux-saga","rest-api","scss"],"created_at":"2024-07-29T17:43:04.141Z","updated_at":"2025-03-10T11:33:02.576Z","avatar_url":"https://github.com/nicolastrote.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cat-fight\n\nProject cat-fight\n\nBest practises around a simple but attracting app where cats are fighting to rule the world.\n\n![alt text](https://github.com/nicolastrote/cat-fight/blob/master/assets/cat-fight.jpg)\n\n## Table of contents\n\n- [Sources](#sources)\n- [Prior](#prior)\n- [Github](#github)\n\n- [Backend Implementation](#backend-implementation)\n\n  - [Gitignore](#gitignore)\n  - [Licences](#licences)\n  - [Server Packages](#server-packages)\n  - [Set Up Nodemon](#set-up-nodemon)\n  - [Setup Up The Server](#setup-up-the-server)\n  - [MongoDB Setup](#mongodb-setup)\n    - [MongoDB Install](#mongodb-install)\n    - [Backup Of Mongodb](#backup-of-mongodb)\n    - [Robo-3T Install](#robo-3t-install)\n  - [MongoDB Models](#mongodb-models)\n  - [Controller For Breed Cat Model](#controller-for-breed-cat-model)\n  - [Routes For Breed Cat CRUD](#routes-for-breed-cat-CRUD)\n  - [Testing The REST API](#testing-the-rest-api)\n  - [Swagger](#swagger)\n  - [Refactoring Connexion](#refactoring-connexion)\n  - [MongoDB New Model](#mongodb-new-model)\n\n- [Frontend Implementation](#frontend-implementation)\n  - [React Basic Architecture](#react-basic-architecture)\n  - [SCSS](#scss)\n  - [ESLint \u0026 Prettier](#eslint-\u0026-prettier)\n    - [ESLint](#eslint)\n    - [Prettier](#prettier)\n    - [Activate ESLint In Webstorm](#activate-eslint-in-webstorm)\n    - [Pre-commit](#pre-commit)\n  - [Favicon](#favicon)\n\n## Sources\n\n- REST API/NodeJS+Mongodb:\n  - Part1: https://www.freecodecamp.org/news/how-to-build-blazing-fast-rest-apis-with-node-js-mongodb-fastify-and-swagger-114e062db0c9/\n  - Part2: https://medium.com/better-programming/how-to-build-a-blazing-fast-graphql-api-with-node-js-mongodb-and-fastify-77fd5acd2998\n- GraphQL: https://medium.com/codingthesmartway-com-blog/creating-a-graphql-server-with-node-js-and-express-f6dddc5320e1\n- React:\n  - Bootstrap: https://medium.com/better-programming/how-to-make-a-responsive-app-with-react-and-bootstrap-938a22dac9d4\n  - Bootstrap: https://medium.com/javascript-in-plain-english/how-to-use-bootstrap-with-react-3bab2b35564e\n  - GraphQL + Apollo: https://medium.com/future-vision/react-apollo-c952fdc6d2a7\n  - Call Axios: https://medium.com/better-programming/the-modern-way-to-use-promise-based-http-requests-axios-hooks-f00791345a37\n\n## Prior\n\n- here is tools I will use during this tutorial:\n  - iTerm2: https://www.iterm2.com/downloads.html\n  - XCode from MacOs\n  - brew: https://brew.sh/\n  - `brew update \u0026\u0026 brew upgrade` (update for already installed packages)\n  - NodeJS and Yarn: `brew install nodejs yarn`\n  - postman:\n    - `brew cask install postman`\n    - import in postman collections from: cat-fight/fastify-api/postman/Backup.postman_dump.json\n- create a repo named \"cat-fight\" on git without the licence you need\n\n## Github\n\n- open iTerm2 and `cd ~ \u0026\u0026 mkdir Workspacecd \u0026\u0026 cd ~/Workspace`\n- `yarn init`\n- `git clone https://github.com/nicolastrote/cat-fight.git \u0026\u0026 cd cat-fight`\n\n## Backend Implementation\n\nsource: https://www.freecodecamp.org/news/how-to-build-blazing-fast-rest-apis-with-node-js-mongodb-fastify-and-swagger-114e062db0c9/\n\nWe will implement NodeJS, MongoDB, Mongoose, Fastify, Swagge and Postman.\n\n- `cd ~/Workspace/cat-fight/ \u0026\u0026 mkdir fastify-api`\n- `cd fastify-api \u0026\u0026 mkdir src \u0026\u0026 cd src`\n- `touch index.js \u0026\u0026 yarn init`\n\n### Gitignore\n\n```shell script\n$ touch .gitignore \u0026\u0026 $ git config --global core.excludesFile ~/.gitignore\n$ nano .gitignore\n```\n\nand ignore those files/folders:\n\n```gitignore\n# ide\n.idea/\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n```\n\n### Licences\n\n- `yarn global add yo \u0026\u0026 yarn global add generator-license-cc`\n- `yo license-cc`\n- I chose CC-BY-NC-SA-4.0 to protect mostly my sharing code\n\n### Server Packages\n\n- `yarn add nodemon mongoose fastify fastify-swagger boom`\n\n### Set Up Nodemon\n\n- **nodemon** : tool which reload nodejs server when you save new code\n- **mongoose** : coding library for mongodb\n- **fastify** : web framework to deliver small and smart nodejs services\n- **fastify-swagger** : documentation generator using swagger tool.\n- **boom** : utility tool for HTTP errors\n\n* `nano package.json`\n* add :\n\n```text\n\"scripts\": {\n    \"start\": \"./node_modules/nodemon/bin/nodemon.js ./src/index.js\",\n    \"test\": \"echo \\\"Error: no test specified\\\" \u0026\u0026 exit 1\"\n  },\n```\n\n### Setup Up The Server\n\n- add in index.js :\n\n```javascript\n// Require the framework and instantiate it\nconst fastify = require(\"fastify\")({\n  logger: true\n});\n\n// Declare a route\nfastify.get(\"/\", async (request, reply) =\u003e {\n  return { hello: \"world\" };\n});\n\n// Run the server!\nconst start = async () =\u003e {\n  try {\n    await fastify.listen(3000);\n    fastify.log.info(`server listening on ${fastify.server.address().port}`);\n  } catch (err) {\n    fastify.log.error(err);\n    process.exit(1);\n  }\n};\n\nstart();\n```\n\n- you can start the server:\n  `yarn start`\n- and see the first result at http://localhost:3000/\n  you should read : {\"hello\":\"world\"}\n\n### MongoDB Setup\n\n#### MongoDB Install\n\nsource : https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/\n\n- `brew tap mongodb/brew \u0026\u0026 brew install mongodb-community@4.2`\n\ninstall creates:\n\n- the configuration file (/usr/local/etc/mongod.conf)\n- the log directory path (/usr/local/var/log/mongodb)\n- the data directory path (/usr/local/var/mongodb)\n\nFor running MongoDB:\n\n- Start : `brew services start mongodb-community@4.2`\n- For running MongoDB as a background process : `mongod --config /usr/local/etc/mongod.conf --fork`\n- To test if is running : `ps aux | grep -v grep | grep mongod`\n- connexion: `mongo`\n\n\\_**\\_With MongoDB, we do not need to create a database.\nWe can just specify a name in the setup and as soon as we store data, MongoDB will create this database for us.\\_\\_**\n\n- add in index.js the connexion:\n\n```javascript\n// Require external modules\nconst mongoose = require(\"mongoose\");\n\n// Connect to MongoDB with Mongoose\nmongoose\n  .connect(\"mongodb://localhost/cat-fight\")\n  .then(() =\u003e console.log(\"MongoDB connected...\"))\n  .catch(err =\u003e console.log(err));\n```\n\nand run `yarn start` if you stop it before. You will see the new message: MongoDB connected...\n\n##### Backup Of Mongodb\n\nsource : https://www.tutorialspoint.com/mongodb/mongodb_create_backup.htm\n\n```shell script\ncd ~/Workspace/cat-fight/fastify-api/mongodb/\nsudo mongodump\n```\n\n##### Restore Of Mongodb\n\n```shell script\nsudo mongorestore\n```\n\n#### Robo-3T Install\n\nRobo-3T is an utility app that allows you to manage MongoDB graphically.\n\n- `brew cask install robo-3t`\n  You can find it and run it in your mac launcher of apps, and create a new connexion with default values.\n\n### MongoDb Models\n\n- create a new folder under src/ named \"models\"\n- and a file named : breed.js\n- look at breed.js file for the cat breed model details.\n\n### Controller For Breed Cat Model\n\n- create a new folder under src/ named \"controllers\"\n- and a file named : breedController.js\n- look at breedController.js file for the cat breed CRUD controller details.\n\n### Routes For Breed Cat CRUD\n\n- create a new folder under src/ named \"routes\"\n- and a file named : index.js\n- look at index.js file for routes details.\n\n- and connect routes with the code by adding/replace the following line of code to the src/index.js file:\n\n```\nconst routes = require(‘./routes’);\n[...]\n// Loop over each route\nroutes.forEach((route, index) =\u003e {\n    fastify.route(route)\n});\n[...]\n```\n\n### Testing The REST API\n\n- in Chrome we can test the URL: http://localhost:3000/api/breeds, because we have no data, it should reply en empty array: []\n- Open postman, click on \"new request\", add in GET input \"http://localhost:3000/api/breeds\" and push \"SEND\" button.\n  You should receive the same response, an empty array.\n  ![alt text](https://github.com/nicolastrote/cat-fight/blob/master/assets/postman.jpg)\n  At this state, you can save this request and put it in a collection named \"cat-fights\".\n\nnb: put breed information declaration as \"RAW\" + \"JSON\" in Postman.\n\n- GET ALL : GET + http://localhost:3000/api/breeds\n- GET ONE : GET + http://localhost:3000/api/breeds/[id]\n- POST : POST + http://localhost:3000/api/breeds + raw json info\n- DELETE : DELETE + http://localhost:3000/api/breeds/[id]\n- UPDATE: PUT + http://localhost:3000/api/breeds/[id] + new raw json info\n\n### Swagger\n\n- create a new folder under src/ named \"config\"\n- and a file named : swagger.js with :\n\n```javascript\nexports.options = {\n  routePrefix: \"/documentation\",\n  exposeRoute: true,\n  swagger: {\n    info: {\n      title: \"Fastify API\",\n      description:\n        \"Building a blazing fast REST API with Node.js, MongoDB, Fastify and Swagger\",\n      version: \"1.0.0\"\n    },\n    externalDocs: {\n      url: \"https://swagger.io\",\n      description: \"Find more info here\"\n    },\n    host: \"localhost:3000\",\n    schemes: [\"http\"],\n    consumes: [\"application/json\"],\n    produces: [\"application/json\"]\n  }\n};\n```\n\nThis code is an object with all options for fastify-swagger plugin.\nFor giving this object, add the following in ~/src/index.js file:\n\n```\nawait fastify.listen(3000)\nfastify.swagger()\nfastify.log.info(`listening on ${fastify.server.address().port}`)\n```\n\n### Refactoring Connexion\n\nsource: https://medium.com/better-programming/how-to-build-a-blazing-fast-graphql-api-with-node-js-mongodb-and-fastify-77fd5acd2998\n\n- create a new file under src/ named \"server.js\" :\n\n```javascript\n// Require the fastify framework and instantiate it\nconst fastify = require(\"fastify\")({\n  logger: true\n});\n\n// Require external modules\nconst mongoose = require(\"mongoose\");\n\n// Connect to MongoDB with Mongoose\nmongoose\n  .connect(\"mongodb://localhost/cat-fight\")\n  .then(() =\u003e\n    console.log(\n      \"  /\\\\_/\\\\  \\n\" +\n        \" ( o o ) MongoDB connected...\\n\" +\n        \"              _         __ _       _     _   \\n\" +\n        \"     ___ __ _| |_      / _(_) __ _| |__ | |_ \\n\" +\n        \"    / __/ _` | __|____| |_| |/ _` | `_ \\\\| __|\\n\" +\n        \"   | (_| (_| | |______|  _| | (_| | | | | |_ \\n\" +\n        \"    \\\\___\\\\__,_|\\\\__|    |_| |_|\\\\__, |_| |_|\\\\__|\\n\" +\n        \"                             |___/           \\n\"\n    )\n  )\n  .catch(err =\u003e console.log(err));\n```\n\n- Now we need to delete this code in index.js, and import it :\n\n```\n// Import Server\nconst fastify = require('./server.js');\n```\n\n### MongoDB New Model\n\n- At this step I need to put more models in the database. I will start with a cats, user and services tables :\n\n  - breeds: content all breed cat information\n  - cats: content information from a specific car which have owner(s)\n  - services: content information services for on cat (ie: birthday, vaccine date, ...)\n  - user: information from the connected user, which have a cat or not!\n\n  Here is the simple model:\n  ![alt text](https://github.com/nicolastrote/cat-fight/blob/master/assets/schema.jpg)\n\n- add in the model src/models/breed : `cats_id: [{type: ObjectId}],`\n- create a src/models/cat.js file with:\n\n```javascript\nconst mongoose = require(\"mongoose\");\nconst Schema = mongoose.Schema;\nconst ObjectId = mongoose.Schema.Types.ObjectId;\n\nconst catSchema = new Schema({\n  alt_names: { type: String, required: false, max: 100 },\n  breed_id: { type: ObjectId },\n  description: { type: String, required: true, max: 100 },\n  id: { type: String, required: true, max: 100 },\n  name: { type: String, required: true, max: 100 },\n  users_id: [{ type: ObjectId }],\n  wikipedia_url: { type: String, max: 100 }\n});\n\nmodule.exports = mongoose.model(\"Cat\", catSchema);\n```\n\n- create a src/models/catServices.js file with:\n\n```javascript\nconst mongoose = require(\"mongoose\");\nconst Schema = mongoose.Schema;\nconst ObjectId = mongoose.Schema.Types.ObjectId;\n\nconst catServicesSchema = new Schema({\n  cat_id: { type: ObjectId },\n  date: { type: String, required: true, max: 100 },\n  description: { type: String, required: true, max: 100 },\n  name: { type: String, required: true, max: 100 }\n});\n\nmodule.exports = mongoose.model(\"CatServices\", catServicesSchema);\n```\n\n## Frontend Implementation\n\n### React Basic Architecture\n\n- creation of the frontend folder named react-app:\n\n```shell script\n cd ~/Workspace/cat-fight/\n yarn create react-app react-app --template typescript\n```\n\n- we will change the default port 3000 for 4200 in the package.json:\n\n```\n    \"start\": \"PORT=4200 react-scripts start\",\n```\n\n### SCSS\n\nhttps://facebook.github.io/create-react-app/docs/adding-a-sass-stylesheet\n• yarn add node-sass\n• rename src/App.css to src/App.scss\n• update src/App.tsx to import src/App.scss\n• This file and any other file will be automatically compiled if imported with the extension .scss.\n\n### ESLint \u0026 Prettier\n\n#### ESLint\n\nWe will controle the write rules in our application with :\n\n- eslint: The core ESLint linting library\n- @typescript-eslint/parser: The parser that will allow ESLint to lint TypeScript code\n- @typescript-eslint/eslint-plugin: A plugin that contains a bunch of ESLint rules that are TypeScript specific\n\nLet's install this:\n\n```shell script\nyarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react @types/react\n```\n\nCreate react-app/.eslintrc.js and add inside :\n\n```shell script\nnano .eslintrc.js\n```\n\nadd :\n\n```javascript\nmodule.exports = {\n  parser: \"@typescript-eslint/parser\", // Specifies the ESLint parser\n\n  // Extend without prettier\n  // extends:  [\n  //  'plugin:react/recommended',  // Uses the recommended rules from @eslint-plugin-react\n  //  'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from @typescript-eslint/eslint-plugin\n  //],\n\n  // Extend WITH prettier\n  extends: [\n    \"plugin:react/recommended\", // Usefull to avoid error of Modules import\n    \"plugin:@typescript-eslint/recommended\", // Uses the recommended rules from the @typescript-eslint/eslint-plugin\n    \"prettier/@typescript-eslint\", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier\n    \"plugin:prettier/recommended\" // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.\n  ],\n  parserOptions: {\n    ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features\n    sourceType: \"module\", // Allows for the use of imports\n    ecmaFeatures: {\n      jsx: true // Allows for the parsing of JSX\n    }\n  },\n  rules: {\n    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs\n    // e.g. \"@typescript-eslint/explicit-function-return-type\": \"off\",\n  },\n  settings: {\n    react: {\n      version: \"detect\" // Tells eslint-plugin-react to automatically detect the version of React to use\n    }\n  }\n};\n```\n\n#### Prettier\n\nPrettier handling code formatting in IDE:\n\n- prettier: The core prettier library\n- eslint-config-prettier: Disables ESLint rules that might conflict with prettier\n- eslint-plugin-prettier: Runs prettier as an ESLint rule\n\n```shell script\nyarn add -D prettier eslint-config-prettier eslint-plugin-prettier pretty-quick\nnano .prettierrc.js\n```\n\n#### Activate ESLint In Webstorm\n\nGo on : https://www.jetbrains.com/help/webstorm/eslint.html\n\n#### Pre-commit\n\n```shell script\nyarn add -D lint-staged husky\n```\n\nadd in package.json\n\n```json\n\"husky\": {\n  \"hooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n},\n\"lint-staged\": {\n  \"src/**/*.{js,jsx,ts,tsx}\": [\n    \"eslint\",\n    \"pretty-quick — staged\"\n  ]\n},\n```\n\n### Favicon\n\nYou will have to create in public logos of 512px and 192px, and a favicon.ico (64/32/24/16px).\nAnd change informations about the website in index.html and manifest.json.\n\n## BootStrap Axios Translation\n\n```shell script\nyarn add -D axios bootstrap i18next i18next-browser-languagedetector i18next-xhr-backend querystring react-bootstrap react-i18next react-router-dom @types/react-router-dom\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicolastrote%2Fcat-fight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicolastrote%2Fcat-fight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicolastrote%2Fcat-fight/lists"}