{"id":15176819,"url":"https://github.com/brunomoleta/investment-app-server","last_synced_at":"2026-02-17T14:32:22.335Z","repository":{"id":220034576,"uuid":"750023951","full_name":"brunomoleta/Investment-app-server","owner":"brunomoleta","description":"Backend app for connecting investors and investment advisors. ","archived":false,"fork":false,"pushed_at":"2024-05-11T19:47:40.000Z","size":484,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-24T11:49:32.968Z","etag":null,"topics":["nest","node","postgresql","prisma"],"latest_commit_sha":null,"homepage":"https://investment-fullstack.onrender.com/","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/brunomoleta.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-29T20:58:05.000Z","updated_at":"2024-02-23T18:46:44.000Z","dependencies_parsed_at":"2024-03-19T23:24:42.206Z","dependency_job_id":"4455836a-a97a-4817-b0fe-11a00303c633","html_url":"https://github.com/brunomoleta/Investment-app-server","commit_stats":{"total_commits":105,"total_committers":2,"mean_commits":52.5,"dds":0.03809523809523807,"last_synced_commit":"ea62c54eee575f126652b8665ddd66c749473164"},"previous_names":["brunomoleta/investment-app-server"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/brunomoleta/Investment-app-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomoleta%2FInvestment-app-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomoleta%2FInvestment-app-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomoleta%2FInvestment-app-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomoleta%2FInvestment-app-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brunomoleta","download_url":"https://codeload.github.com/brunomoleta/Investment-app-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brunomoleta%2FInvestment-app-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278692948,"owners_count":26029408,"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","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["nest","node","postgresql","prisma"],"created_at":"2024-09-27T13:42:51.589Z","updated_at":"2025-10-06T23:09:01.859Z","avatar_url":"https://github.com/brunomoleta.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Backend for a Fullstack project\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Description\n\nBackend of a fullstack app that connects investors and investment advisors.\nThe goal of this project was to make my first fullstack app using Nest.js.\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n\n## Table of contents\n\n- [Overview](#overview)\n    - [Built with](#build-with)\n    - [Deploy links](#deploy-links)\n- [Project Structure](#project-structure)\n    - [Relationships](#relationships)\n    - [Architecture](#architecture)\n    - [Scripts](#scripts)\n    - [Dependencies](#dependencies)\n    - [Dev dependencies](#dev-dependencies)\n- [Local settings ](#local-settings)\n    - [Installation](#installation)\n    - [Environment variables](#environment-variables)\n    - [Database config](#database-config)\n    - [Start server](#start-server)\n- [Endpoints](#endpoints)\n- [The process](#the-process)\n    - [What I learned](#what-i-learned)\n        - [Session payload](#session-payload)\n        - [Update password](#update-password)\n    - [Continued development](#continued-development)\n    - [Useful resources](#useful-resources)\n- [Acknowledgments](#acknowledgments)\n    - [Author](#author)\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Overview\n\n### Build with\n\n- Node.js\n- Nest\n- Typescript\n- PostgreSQL\n- Prisma ORM\n\n### Deploy links\n\n- Explanação em Português: https://www.brunomoleta.com.br/easy-bank-investments\n- Documentation: https://investment-fullstack.onrender.com/doc\n- Live Backend: https://investment-fullstack.onrender.com\n- Front End Repo: https://github.com/brunomoleta/Investment-app-client\n- Live Application: https://easybank-investments.vercel.app/\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Project Structure\n\n### Relationships\n\n![](relationships.jpeg)\n\n### Architecture\n\n```\ninvesting-app-server/\n│\n├── node_modules/       Dependencies installed in your local environment.\n│\n├── prisma/             Contains Prisma migrations and Prisma models\n│\n├── src/                Source code\n|   |\n│   ├── database/       Contains the connection between Prisma and Nest;\n|   |\n│   ├── decorators/     Functions that may be used throughout the code;\n|   |          \n│   ├── interceptor/    Has a pagination function;\n|   |          \n│   ├── modules/        Classes that stablish the main ingredients of the API;\n|     |\n│     ├── admin/        The admin module has access to everything;\n|       | \n                        (The description below is equivalent for the\n                        following modules);\n|       | \n│       ├── dto         Defines the data the API will receive   \n                        and transfers it between controller and service;  \n|       | \n│       ├── entity      The class that maps to the database;   \n|       | \n│       ├── controller  Defines the type of route and what \n                        it will receive from the client.\n                        Also the place where the Swagger\n                        documentation is customized;                   \n|       | \n│       ├── service     Defines how the API will handle the client's request\n                        and interacts with the database;   \n|       | \n│       ├── module      Handles controllers, providers and exports;   \n|       |\n|     |\n│     ├── advisors/     Each is connected to N investor and, with\n                        one investment type;      \n|     | \n│     ├── investment_types/   \n|     |                 Each one is connect to an advisor,\n                        and to N products;\n|     | \n│     ├── investors/    The main user of the app. \n|     |                 Will connect to N products,\n                        and one advisor;\n                        \n|     | \n│     ├── products/     (Not implemented yet)\n|     |                 Connects with N investors, and\n                        N investment_types;\n|     | \n│     ├── session/      Starts the user's session\n|     |                 \n|     | \n│     ├── users/        Sets default values\n|     |                 that the other modules  \n                        will extend;\n|                    \n│   .env.example          example of how to write a .env\n|\n│   relationships.jpeg    visualization of the relationships between the modules \n│   └── ...\n```\n\n### Scripts\n\n- `build`: Compile Nest.js;\n- `format`: Format the source code with Prettier;\n- `start`: Start the Nest.js server;\n- `start:dev`: Start the server in dev mode monitoring updates;\n- `start:debug`: Start the server in debug mode monitoring updates;\n- `start:prod`: Start server to test the deployment of the source code;\n- `lint`: Runs ESLint for linting and automatic correction;\n\nRun the scripts starting with `npm run` or `yarn`,\n\n### Dependencies\n\n- ````@nestjs/common````: ^10.0.0,\n- ````@nestjs/core````: ^10.0.0,\n- ````@nestjs/jwt````: ^10.2.0,\n- ````@nestjs/passport````: ^10.0.3,\n- ````@nestjs/platform-express````: ^10.0.0,\n- ````@nestjs/swagger````: ^7.2.0,\n- ````@prisma/client````: ^5.9.0,\n- ````@types/multer````: ^1.4.11,\n- ````bcryptjs````: 2.4.3,\n- ````class-transformer````: 0.5.1,\n- ````class-validator````: 0.14.1,\n- ````cloudinary````: 2.0.1,\n- ````cors````: 2.8.5,\n- ````dotenv````: 16.4.1,\n- ````jsonwebtoken````: 9.0.2,\n- ````multer````: 1.4.5-lts.1,\n- ````passport````: 0.7.0,\n- ````passport-jwt````: 4.0.1,\n- ````pg````: 8.11.3,\n- ````prisma````: 5.9.0,\n- ````reflect-metadata````: 0.1.13,\n- ````rxjs````: 7.8.1,\n- ````swagger-ui-express````: 5.0.0\n\n### Dev dependencies\n\n- ````@nestjs/cli````: ^10.0.0,\n- ````@nestjs/schematics````: ^10.0.0,\n- ````@nestjs/testing````: ^10.0.0,\n- ````@swc/cli````: ^0.1.65,\n- ````@swc/core````: ^1.3.105,\n- ````@types/bcryptjs````: ^2.4.6,\n- ````@types/express````: ^4.17.17,\n- ````@types/jest````: ^29.5.2,\n- ````@types/node````: ^20.3.1,\n- ````@types/passport-jwt````: ^4.0.1,\n- ````@types/supertest````: ^6.0.0,\n- ````@typescript-eslint/eslint-plugin````: ^6.0.0,\n- ````@typescript-eslint/parser````: ^6.0.0,\n- ````eslint````: ^8.42.0,\n- ````eslint-config-prettier````: ^9.0.0,\n- ````eslint-plugin-prettier````: ^5.0.0,\n- ````jest````: ^29.5.0,\n- ````nodemon````: ^3.0.3,\n- ````prettier````: ^3.0.0,\n- ````source-map-support````: ^0.5.21,\n- ````supertest````: ^6.3.3,\n- ````ts-jest````: ^29.1.0,\n- ````ts-loader````: ^9.4.3,\n- ````ts-node````: ^10.9.1,\n- ````tsconfig-paths````: ^4.2.0,\n- ````typescript````: ^5.1.\"\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Local settings\n\n### Installation\n\n1. Clone the repositório:\n\n```bash\ngit clone git@github.com:brunomoleta/Investment-app-server.git\n```\n\n2. Install the project's dependencies:\n\n```bash\nyarn install\n```\n\n### Environment variables\n\nMake sure you set the environment variables in the `.env` file, using `.env.example`,\nlocated at the project's root.\n\n### Database config\n\n1. Install PostgreSQL\n\nMake sure you have PostgreSQL installed on your system. You can download it\nat [postgresql](https://www.postgresql.org/download/).\n\n2. Start the server\n\nAfter installing, start the PostgreSQL service.\nThe commands change depending on the operating system:\n\n- **Linux:**\n\n```bash\nsudo service postgresql start\n```\n\n- **Windows:**\n\nGo to  \"Control panel\" \u003e \"Admin Tools\" \u003e \"Services\".\nFind the PostgreSQL service and start it.\n\n- **MacOS:**\n\n```bash\npg_ctl -D /usr/local/var/postgres start\n```\n\n3. Enter the Database\n\nBy default, PostgreSQL creates a database named ````postgres````. You can access it using the word ````psql```` at the\nterminal:\n\n```bash\npsql -U postgres\n```\n\nThis will open an interactive session with the postgres database using your postgres user.\nYou will have to put your password without seeing it.\n\n4. Create a new Database\n\nInside the psql shell, run the following command to create a new database:\n\n```bash\nCREATE DATABASE db_name;\n```\n\nChange `db_name` with the desired name for the database.\n\n5. Run the prisma migrations\n\n```bash\nprisma migrate dev\n```\n\n### Start server\n\nRun the following command to start the server:\n\n```bash\n# development\n$ yarn start\n\n# watch mode\n$ yarn run start:dev\n\n# production mode\n$ yarn run start:prod\n```\n\nLocal server running at: [http://localhost:3001](http://localhost:3000).\n\nPS: The port may be diffent if you changed the config at `.env`.\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Endpoints\n\nThe full doc is at [deploy-swagger](https://investment-fullstack.onrender.com/doc)(deploy)\nor [local-swagger](http://localhost:3001/doc)(running locally).\n\n| `Method`   | `Endpoint`                            | `Responsability`                                 | `Autenticação`        |\n|------------|---------------------------------------|--------------------------------------------------|-----------------------|\n| POST       | /session/admin                        | Generate admin authentication token              | Universal Access      |\n| POST       | /session/advisor                      | Generate advisor authentication token            | Universal Access      |\n| POST       | /session/investor                     | Generate investor authentication token           | Universal Access      |\n| ---------- | -------------------------------       | --------------------------------------------     | --------------------- |\n| POST       | /investor                             | Creates an investor                              | Universal Access      |\n| GET        | /investor                             | Retrievess investors                             | Authenticated User    |\n| GET        | /investor/id                          | Retrievess investor by Token                     | Authenticated User    |\n| GET        | /investor/advisor/:advisor_id         | Filters investors by advisor                     | Authenticated User    |\n| GET        | /investor/amount/:amount              | Filters investors by amount($)                   | Authenticated User    |\n| PATCH      | /investor                             | Updates investor's data by the Token             | Authenticated User    |\n| PATCH      | /investor/password                    | Validates current password and update it         | Authenticated User    |\n| DELETE     | /investor                             | Deletes investor by the Token                    | Authenticated User    |\n| ---------- | -------------------------------       | --------------------------------------------     | --------------------- |\n| POST       | /advisor                              | Creates an advisor                               | Universal Access      |\n| GET        | /advisor                              | Retrieves advisors                               | Universal Access      |\n| GET        | /advisor/all                          | Retrieves advisors with all their data           | Authenticated User    |\n| GET        | /advisor/speciality_id/:speciality_id | Filter advisors through speciality_id            | Universal Access      |\n| GET        | /advisor/experience/:experience       | Filter advisors through experience               | Universal Access      |\n| GET        | /advisor/id                           | Retrieves advisor through the token              | Authenticated User    |\n| PATCH      | /advisor                              | Updates an advisor through the token             | Authenticated User    |\n| PATCH      | /advisor/password                     | Validates current password and update it         | Authenticated User    |\n| DELETE     | /advisor                              | Deletes an advisor through the token             | Authenticated User    |\n| ---------- | -------------------------------       | --------------------------------------------     | --------------------- |\n| POST       | /investment_type                      | Creates an investment type                       | Authenticated User    |\n| GET        | /investment_type/all                  | Retrieves investment types with all related data | Authenticated User    |\n| GET        | /investment_type                      | Retrieves investment types                       | Universal Access      |\n| GET        | /investment_type/risk/:risk           | Filter investment types by their risk            | Universal Access      |\n| GET        | /investment_type/id/:id               | Retrieves investment_type through their id       | Universal Access      |\n| PATCH      | /investment_type                      | Update investment_type through their id          | Authenticated User    |\n| ---------- | -------------------------------       | --------------------------------------------     | --------------------- |\n| POST       | /admin                                | Create admin                                     | Authenticated User    |\n| GET        | /admin                                | Retrieves admins                                 | Authenticated User    |\n| GET        | /admin/id                             | Retrieves admin through the Token                | Authenticated User    |\n| PATCH      | /admin/password                       | Validate current password and update it          | Authenticated User    |\n| DELETE     | /admin                                | Remove admin                                     | Authenticated User    |\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## The process\n\n### What I learned\n\n#### Session payload\n\nTo enhance the logged user security I decided to retrieve only the id at the payload.\nIt was the first time I tried it, so it was not straightforward.\nFinally, it worked with the following:\n\n```ts\n//session.service.ts\nconst { id } = user;\nconst token = { sub: id };\n\nreturn { token: await this.jwtService.signAsync(token) };\n```\n\nAt the controller the client passes the token to make\na request. Such as:\n\n```ts\n//advisor.controller.ts\n@Patch('password')\n//...\nchangePassword(\n  @Request()\nrequest: string,\n@Body()\nupdatePasswordDto: UpdatePasswordDto,\n)\n{\n  const token = request.headers.authorization.split(' ')[1];\n  const decoded: any = decode(token);\n\n  return await this.advisorsService.updatePassword(\n    decoded.sub,\n    updatePasswordDto,\n  );\n}\n```\n\n#### Update password\n\nI also want to highlight the update of the password service:\n\n```ts\n    async\nupdatePassword(\n  id: string, passwordDto: UpdatePasswordDto\n  )\n{\n  const advisor =\n    await this.prisma.advisor.findUnique({\n      where: { id },\n    });\n//...\n\n  const passwordMatch = await bcrypt.compare(\n    passwordDto.currentPassword,\n    advisor.password,\n  );\n//...\n\n  const hashedPassword = await\n    bcrypt.hash(passwordDto.newPassword, 10);\n\n  await this.prisma.advisor.update({\n    where: { id },\n    data: { password: hashedPassword },\n  });\n\n  return { message: 'Password successfully updated' };\n}\n```\n\n### Continued development\n\nMake use of UseGuards, something I studied and tried to implement\nbut could not finish it and automatize the pagination of GET requests\nthat involve retrieving multiple instances.\nAlso to experiment with MongoDB.\n\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n### Useful resources\n\n- [How to make a Nest documentation with Swagger](https://www.slingacademy.com/article/how-to-automatically-generate-swagger-docs-in-nestjs/)\n- [Nest Official doc](https://docs.nestjs.com/)\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n\n## Acknowledgments\n\n### Author\n\n- Github - [Bruno Moleta](https://github.com/brunomoleta)\n- Frontend Mentor - [@brunomoleta](https://www.frontendmentor.io/profile/brunomoleta)\n- LinkedIn - [@brunomoleta](https://www.linkedin.com/in/bruno-moleta-santos/)\n- Email - brunomoleta@pm.me\n\n\u003chr style=\"border-top: 3px solid #bbb;\"\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunomoleta%2Finvestment-app-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrunomoleta%2Finvestment-app-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrunomoleta%2Finvestment-app-server/lists"}