{"id":17651746,"url":"https://github.com/diegovictor/proffy-api","last_synced_at":"2025-05-07T02:43:17.729Z","repository":{"id":98964698,"uuid":"285134541","full_name":"DiegoVictor/proffy-api","owner":"DiegoVictor","description":"Project built during Rocketseat's Next Level Week #2","archived":false,"fork":false,"pushed_at":"2025-05-02T00:33:06.000Z","size":1749,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-02T01:26:45.336Z","etag":null,"topics":["api","editorconfig","eslint","insomnia","knex","nextlevelweek","nextlevelweek-booster","nlw","nlw-booster","node","nodejs","prettier","proffy","redis","rocketseat","sqlite","sqlite3","ts","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DiegoVictor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2020-08-05T00:29:00.000Z","updated_at":"2025-05-02T00:33:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"1ea37007-8508-486b-a0ad-11a53c8436bd","html_url":"https://github.com/DiegoVictor/proffy-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiegoVictor%2Fproffy-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiegoVictor%2Fproffy-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiegoVictor%2Fproffy-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiegoVictor%2Fproffy-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DiegoVictor","download_url":"https://codeload.github.com/DiegoVictor/proffy-api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252802607,"owners_count":21806537,"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":["api","editorconfig","eslint","insomnia","knex","nextlevelweek","nextlevelweek-booster","nlw","nlw-booster","node","nodejs","prettier","proffy","redis","rocketseat","sqlite","sqlite3","ts","typescript"],"created_at":"2024-10-23T11:43:29.093Z","updated_at":"2025-05-07T02:43:17.706Z","avatar_url":"https://github.com/DiegoVictor.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [API] Proffy\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/DiegoVictor/proffy-api/config.yml?logo=github\u0026style=flat-square)](https://github.com/DiegoVictor/proffy-api/actions)\n[![sqlite3](https://img.shields.io/badge/sqlite-5.1.7-003b57?style=flat-square\u0026logo=sqlite\u0026logoColor=white)](https://www.sqlite.org)\n[![typescript](https://img.shields.io/badge/typescript-5.5.4-3178c6?style=flat-square\u0026logo=typescript)](https://www.typescriptlang.org/)\n[![eslint](https://img.shields.io/badge/eslint-8.57.0-4b32c3?style=flat-square\u0026logo=eslint)](https://eslint.org/)\n[![airbnb-style](https://flat.badgen.net/badge/style-guide/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)\n[![jest](https://img.shields.io/badge/jest-29.7.0-brightgreen?style=flat-square\u0026logo=jest)](https://jestjs.io/)\n[![coverage](https://img.shields.io/codecov/c/gh/DiegoVictor/proffy-api?logo=codecov\u0026style=flat-square)](https://codecov.io/gh/DiegoVictor/proffy-api)\n[![MIT License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](https://raw.githubusercontent.com/DiegoVictor/proffy-api/main/LICENSE)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\u003cbr\u003e\n[![Run in Insomnia}](https://insomnia.rest/images/run.svg)](https://insomnia.rest/run/?label=Proffy\u0026uri=https%3A%2F%2Fraw.githubusercontent.com%2FDiegoVictor%2Fproffy-api%2Fmaster%2FInsomnia_2024-08-08.json)\n\n\nResponsible for provide data to the [`web`](https://github.com/DiegoVictor/proffy-web) and [`mobile`](https://github.com/DiegoVictor/proffy-app) front-ends. Permit to register your class availability and subject, also count the number of teacher connected to users (get contacted by whatsapp). The app has validation and a simple versioning was made.\n\n## Table of Contents\n* [Installing](#installing)\n  * [Configuring](#configuring)\n    * [SQLite](#sqlite)\n      * [Migrations](#migrations)\n    * [.env](#env)\n* [Usage](#usage)\n  * [Error Handling](#error-handling)\n    * [Errors Reference](#errors-reference)\n  * [Pagination](#pagination)\n    * [Link Header](#link-header)\n    * [X-Total-Count](#x-total-count)\n  * [Bearer Token](#bearer-token)\n  * [Versioning](#versioning)\n  * [Routes](#routes)\n    * [Requests](#requests)\n* [Running the tests](#running-the-tests)\n  * [Coverage report](#coverage-report)\n\n# Installing\nEasy peasy lemon squeezy:\n```\n$ yarn\n```\nOr:\n```\n$ npm install\n```\n\u003e Was installed and configured the [`eslint`](https://eslint.org/) and [`prettier`](https://prettier.io/) to keep the code clean and patterned.\n\n## Configuring\nThe application uses just one database: [SQLite](https://www.sqlite.org/index.html).  For the fastest setup is recommended to use [docker-compose](https://docs.docker.com/compose/), you just need to up all services:\n```\n$ docker-compose up -d\n```\n\n### SQLite\nStore all the users, classes and connections. For more information to how to setup your database see:\n* [knexfile.ts](http://knexjs.org/#knexfile)\n\u003e You can find the application's `knexfile.ts` file in the root folder.\n\n#### Migrations\nRemember to run the SQLite database migrations:\n```\n$ yarn knex:migrate\n```\nOr:\n```\n$ npx knex migrate:latest\n```\n\u003e See more information on [Knex Migrations](http://knexjs.org/#Migrations).\n\n### .env\nIn this file you may configure the environment, your app's port and a url to documentation (this will be returned with error responses, see [error section](#error-handling)). Rename the `.env.example` in the root directory to `.env` then just update with your settings.\n\n|key|description|default\n|---|---|---\n|APP_PORT|Port number where the app will run.|`3333`\n|NODE_ENV|App environment. The knex's connection configuration used rely on the this key value, so if the environment is `development` the knex connection used will be`development`.|`development`\n|APP_WEB_URL|Used to create the reset password link (front-end) sent in the recover password email.|`http://127.0.0.1:3000`\n|MAIL_HOST|Hostname or IP address of the email provider service| -\n|MAIL_PORT|Service's port| `587`\n|MAIL_USER|Username to authenticate in the email service| -\n|MAIL_PASSWORD|Password to authenticate in the email service| -\n|JWT_SECRET|A alphanumeric random string. Used to create signed tokens.| -\n|JWT_EXPIRATION_TIME|How long time will be the token valid. See [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#usage) repo for more information.|`7d`\n|DOCS_URL|An url to docs where users can find more information about the app's internal code errors.|`https://github.com/DiegoVictor/proffy-api#errors-reference`\n\u003e For more information about the `MAIL_*` keys configurations see [nodemailer](https://nodemailer.com) website, since this project uses it to mail.\n\n# Usage\nTo start up the app run:\n```\n$ yarn dev:server\n```\nOr:\n```\nnpm run dev:server\n```\n\n## Error Handling\nInstead of only throw a simple message and HTTP Status Code this API return friendly errors:\n```json\n{\n  \"statusCode\": 429,\n  \"error\": \"Too Many Requests\",\n  \"message\": \"Too Many Requests\",\n  \"code\": 529,\n  \"docs\": \"https://github.com/DiegoVictor/proffy-api#errors-reference\"\n}\n```\n\u003e Errors are implemented with [@hapi/boom](https://github.com/hapijs/boom).\n\u003e As you can see a url to error docs are returned too. To configure this url update the `DOCS_URL` key from `.env` file.\n\u003e In the next sub section ([Errors Reference](#errors-reference)) you can see the errors `code` description.\n\n### Errors Reference\n|code|message|description\n|---|---|---\n|141|User not found|Could not found the user of the class.\n|144|Class not found|The `id` sent does not references an existing class in the database.\n|150|Unexpected error while update new classes|An error ocurred during the updating/creation of the user, classes and schedules.\n|240|Email already in use|The provided email is already used by another user.\n|244|User not found|The `id` sent does not references an existing user in the database.\n|340|User and/or password not match|User and/or password is incorrect.\n|344|User not exists|The email sent not references an existing user in the database.\n|440|You can not favorite yourself|You provide your own `id` as `favorited_user_id`.\n|444|Users not match|Couldn't found one or both users, the favorited (proffy) and you (you not exists xD!).\n|540|Token invalid or expired|The reset password JWT token is invalid or expired.\n|541|Token invalid or expired|The login JWT token is invalid or expired.\n|542|Invalid token|The login JWT token not contain a valid user id.\n|543|Token not provided|The login JWT token was not sent.\n|544|User does not exists|The provided email not references a user in the database.\n|529|Too Many Requests|You reached at the requests limit.\n|550|An unexpected error while updating the user occured|Was not possible to reset user password.\n\n## Pagination\nAll the routes with pagination returns 10 records per page, to navigate to other pages just send the `page` query parameter with the number of the page.\n\n* To get the third page of incidents:\n```\nGET http://localhost:3333/v1/classes?page=3\n```\n\n### Link Header\nAlso in the headers of every route with pagination the `Link` header is returned with links to `first`, `last`, `next` and `prev` (previous) page.\n```\n\u003chttp://localhost:3333/v1/classes?page=7\u003e; rel=\"last\",\n\u003chttp://localhost:3333/v1/classes?page=4\u003e; rel=\"next\",\n\u003chttp://localhost:3333/v1/classes?page=1\u003e; rel=\"first\",\n\u003chttp://localhost:3333/v1/classes?page=2\u003e; rel=\"prev\"\n```\n\u003e See more about this header in this MDN doc: [Link - HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link).\n\n### X-Total-Count\nAnother header returned in routes with pagination, this bring the total records amount.\n\n## Bearer Token\nA few routes expect a Bearer Token in an `Authorization` header.\n\u003e You can see these routes in the [routes](#routes) section.\n```\nGET http://localhost:3333/v1/classes Authorization: Bearer \u003ctoken\u003e\n```\n\u003e To achieve this token you just need authenticate through the `/sessions` route and it will return the `token` key with a valid Bearer Token.\n\n## Versioning\nA simple versioning was made. Just remember to set after the `host` the `/v1/` string to your requests.\n```\nGET http://localhost:3333/v1/classes\n```\n\n## Routes\n|route|HTTP Method|pagination|params|description|auth method\n|:---|:---:|:---:|:---:|:---:|:---:\n|`/sessions`|POST|:x:|Body with user `email` and `password`.|Authenticates user and return an access token.|:x:\n|`/connections`|GET|:x:| - |Lists connections total.|Bearer\n|`/connections`|POST|:x:|Body with `user_id`.|Increase the number of connections.|Bearer\n|`/classes`|GET|:heavy_check_mark:|`week_day`, `subject`, `time`, `page` query parameters.|Lists classes available.|Bearer\n|`/classes/my-class`|GET|:x:| - |Return the logged in user's class.|Bearer\n|`/classes`|POST|:x:|Body with class `subject`, `cost`, user `user_id`, `whatsapp`, `bio` and class schedule `schedules.week_day`, `schedules.from`, `schedules.to`.|Create new class availability.|Bearer\n|`/users`|POST|:x:|Body with user `name`, `surname`, `email`, `password`, `avatar` (url), `whatsapp` and `bio`.|Creates a new user.|:x:\n|`/users/:id`|GET|:x:|`id` of the user.|Return one user.|Bearer\n|`/users/forgot_password`|POST|:x:|Body with `email`.|Send forgot password email.|:x:\n|`/users/reset_password`|POST|:x:|Body with `password`, `password_confirmation` and `token`.|Change user's current password.|:x:\n|`/favorites`|GET|:heavy_check_mark:|`page` query parameters.|Lists favorited proffys.|Bearer\n|`/favorites`|POST|:x:|Body with `user_id` from user that intending to be favorite.|Set a proffy as favorite.|Bearer\n\n### Requests\n* `POST /sessions`\n\nRequest body:\n```json\n{\n  \"email\": \"johndoe@example.com\",\n  \"password\": \"123456\"\n}\n```\n\n* `POST /connections`\n\nRequest body:\n```json\n{\n  \"user_id\": 76988\n}\n```\n\n* `POST /classes`\n\nRequest body:\n```json\n{\n  \"user_id\": 76988,\n  \"whatsapp\": \"39379976591\",\n  \"bio\": \"I have been worked with PHP/Laravel and JavaScript/Node.js for +4 years. Recently I started studying ReactJs and React Native :)\",\n  \"cost\": 30,\n  \"subject\": \"Node.js\",\n  \"schedule\": [\n    {\n      \"week_day\": 0,\n      \"from\": \"10:00\",\n      \"to\": \"15:00\"\n    },\n    {\n      \"week_day\": 4,\n      \"from\": \"7:00\",\n      \"to\": \"11:00\"\n    }\n  ]\n}\n```\n\n* `POST /users`\n\nRequest body:\n```json\n{\n  \"name\": \"John\",\n  \"surname\": \"Doe\",\n  \"email\": \"johndoe@example.com\",\n  \"password\": \"123456\",\n  \"avatar\": \"https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/1103.jpg\",\n  \"whatsapp\": \"1125585262\",\n  \"bio\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \",\n}\n```\n\n* `POST /users/forgot_password`\n\nRequest body:\n```json\n{\n  \"email\": \"johndoe@example.com\"\n}\n```\n\n* `POST /users/reset_password`\n\nRequest body:\n```json\n{\n  \"password\": \"123456\",\n  \"password_confirmation\": \"123456\",\n  \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpZCI6IjE4ZjZkNzYzLTQ1NWMtNDEwNC1iMmFlLWUwNzE2N2JhOTMwYyJ9.uqiIJHRc6wsNCBy3MVtO-hDWwvs_afIhv7adswrUas4\"\n}\n```\n\n* `POST /favorites`\n\nRequest body:\n```json\n{\n  \"user_id\": 76988\n}\n```\n\n# Running the tests\n[Jest](https://jestjs.io/) was the choice to test the app, to run:\n```\n$ yarn test\n```\nOr:\n```\n$ npm run test\n```\n\n## Coverage report\nYou can see the coverage report inside `tests/coverage`. They are automatically created after the tests run.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiegovictor%2Fproffy-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiegovictor%2Fproffy-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiegovictor%2Fproffy-api/lists"}