{"id":18697424,"url":"https://github.com/cassiofb-dev/happy-backend","last_synced_at":"2026-05-05T04:05:53.070Z","repository":{"id":104454730,"uuid":"303855456","full_name":"cassiofb-dev/happy-backend","owner":"cassiofb-dev","description":"NLW3, Happy backend","archived":false,"fork":false,"pushed_at":"2020-10-16T00:33:13.000Z","size":5625,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-18T09:08:44.723Z","etag":null,"topics":["backend","happy","nlw3","nodejs"],"latest_commit_sha":null,"homepage":"","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/cassiofb-dev.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":"2020-10-13T23:51:37.000Z","updated_at":"2020-10-16T00:33:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"1209fd70-fd8c-43ab-9ddf-e58636410eae","html_url":"https://github.com/cassiofb-dev/happy-backend","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/cassiofb-dev%2Fhappy-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cassiofb-dev%2Fhappy-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cassiofb-dev%2Fhappy-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cassiofb-dev%2Fhappy-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cassiofb-dev","download_url":"https://codeload.github.com/cassiofb-dev/happy-backend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239558929,"owners_count":19658929,"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":["backend","happy","nlw3","nodejs"],"created_at":"2024-11-07T11:24:17.422Z","updated_at":"2025-11-08T16:30:36.763Z","avatar_url":"https://github.com/cassiofb-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dia 2\n\n 1. [Rotas, parâmetros e métodos HTTP](#rotas-par%C3%A2metros-e-m%C3%A9todos-http)\n 2. [Banco de dados](#banco-de-dados)\n 3. [Criando tabelas no banco de dados](#criando-tabelas-no-banco-de-dados)\n 4. [Orfanato Controller](#orfanato-controller)\n 5. [Trabalhando com views](#trabalhando-com-views)\n 6. [Validação de dados](#valida%C3%A7%C3%A3o-dos-dados)\n 7. [Lidando com exceções](#lidando-com-exce%C3%A7%C3%B5es)\n 8. [Árvore de arquivos](#árvore-de-arquivos-backend)\n 9. [Resultado](#resultado)\n\n## Rotas, parâmetros e métodos HTTP \n\nPara construirmos o nosso backend precisamos entender alguns conceitos de rede. Esse é apenas um resumo básico, para entender mais e se aprofundar recomendo o [RFC 7231](https://tools.ietf.org/html/rfc7231).\n\n### Rotas\n\nUm conjunto de funcionalidade/instruções do backend. Abaixo coloquei um exemplo de um app bem simples com apenas uma rota:\n\n```ts\nimport express from  'express';\n\nconst app = express();\n\napp.use(express.json());\n\napp.get('/users/:id', (request, response) =\u003e {\n\n  console.log({\n    \"query params\":  request.query,\n    \"route params\":  request.params,\n    \"body\":  request.body,\n  });\n\n  return  response.json({\n    message:  'hello world',\n  });\n});\n\napp.listen(3333);\n```\n\nSe executada uma request com a URL:\n```http://localhost:3333/users/teste?testequery=testando```\n\nE o body:\n```js\n{\n  \"name\": \"teste\",\n  \"value\": \"testando\"\n}\n```\nA saída no console será:\n```shell\n{\n  'query params': { testequery: 'testando' },\n  'route params': { id: 'teste' },\n  body: { name: 'teste', value: 'testando' }\n}\n```\n\n### Recurso\n\nDado que será criado, buscado, deletado ou modificado. No caso do exemplo, ```users``` é o nosso recurso.\n\n### Parâmetros\n\nURL exemplo: ```http://localhost:3333/users/teste?testequery=testando```\n\nInformações enviadas através de uma HTTP request através das seguintes formas:\n\n 1. Query Params\n Parâmetros enviados através da URL. No exemplo, o query param é ```testequery``` e o seu valor é ```testando```.\n 2. Route Params\n Também enviado através da URL, porém utilizado para identificar o recurso. No exemplo o route param é ```users```\n 3. Body\n Enviado através do corpo da requisição. Utilizado para transferência de dados extensos ou complexos.\n\n### Métodos HTTP\n\nConjunto de métodos para identificar como será resolvida a requisição de um recurso. No exemplo, ```GET``` é o nosso método.\n\nOs principais métodos HTTP são:\n\n 1. GET - Requisita dados em um servidor. Geralmente associado à listagem de um recurso. Segundo ao  [RFC 7231](https://tools.ietf.org/html/rfc7231):\n\t\u003e Transfer a current representation of the target resource.\n 2. POST - Envia dados para um servidor. Geralmente relacionado à criação de um recurso. Segundo ao  [RFC 7231](https://tools.ietf.org/html/rfc7231):\n\t\u003e Perform resource-specific processing on the request payload.\n 3. PUT - Envia dados para um servidor. Geralmente relacionado à atualização de um recurso existente. Segundo ao  [RFC 7231](https://tools.ietf.org/html/rfc7231):\n\t\u003e Replace all current representations of the target resource with the request payload.\n 4. DELETE - Requisita o apagamento de um determinado recurso. Segundo ao  [RFC 7231](https://tools.ietf.org/html/rfc7231):\n\t\u003e Remove all current representations of the target resource.\n\n## Banco de dados\n\nPodemos configurar o banco de dados de diversas formas no Node:\n\n 1. Driver Nativo - Queries podem ser executadas diretamento no Node, porém sem nenhuma abstração. Ex: [SQLite](https://www.sqlite.org/index.html).\n 2. Query Builder - Queries são escritas através do próprio JavaScript e depois são construidas pelo query builder. Ex: [Knext.js](http://knexjs.org/).\n 3. ORM - Maior nível de abstração. Tabelas do banco de dados são representadas através de classes. Registros de cada tabela são representados como instâncias das classes correspondentes. Ex: [TypeORM](https://typeorm.io/#/).\n\n## Criando tabelas no banco de dados\n\nVocê pode procurar o lugar dor arquivo no código ou simplesmente visualizar a [árvore de arquivos](#%C3%A1rvore-de-arquivos).\n\nConfigure o ```package.json```:\n\n```js\n{\n  \"name\": \"happy-backend\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Happy app backend server source code\",\n  \"main\": \"index.js\",\n  \"repository\": \"git\",\n  \"author\": \"cassiofb-dev\",\n  \"license\": \"MIT\",\n  \"private\": false,\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^4.17.1\",\n    \"express-async-errors\": \"^3.1.1\",\n    \"multer\": \"^1.4.2\",\n    \"sqlite3\": \"^5.0.0\",\n    \"typeorm\": \"^0.2.28\",\n    \"yup\": \"^0.29.3\"\n  },\n  \"scripts\": {\n    \"dev\": \"ts-node-dev --transpile-only --ignore-watch node_modules src/server.ts\",\n    \"typeorm\": \"ts-node-dev ./node_modules/typeorm/cli.js\"\n  },\n  \"devDependencies\": {\n    \"@types/cors\": \"^2.8.8\",\n    \"@types/express\": \"^4.17.8\",\n    \"@types/multer\": \"^1.4.4\",\n    \"@types/yup\": \"^0.29.8\",\n    \"ts-node-dev\": \"^1.0.0-pre.63\",\n    \"typescript\": \"^4.0.3\"\n  }\n}\n\n```\n\nConfigure o arquivo ```ormconfig.json```:\n\n```js\n{\n  \"type\": \"sqlite\",\n  \"database\": \"./src/database/database.sqlite\",\n  \"migrations\": [\n    \"./src/database/migrations/*.ts\"\n  ],\n  \"entities\": [\n    \"./src/models/*.ts\"\n  ],\n  \"cli\": {\n    \"migrationsDir\": \"./src/database/migrations\"\n  }\n}\n\n```\n\nExecute o comando:\n\n```shell\nyarn typeorm migration:create -n \u003cnome da sua tabela\u003e\n```\n## Orfanato Controller\n\nO controller é uma abstração do método [MVC](https://pt.wikipedia.org/wiki/MVC), o link para leitura está disponível irei focar apenas no exemplo resumido e prático.\n\nExemplo de controller:\n\n```ts\n// OrphanagesController.ts\n\nimport { Request, Response } from 'express';\nimport { getRepository } from 'typeorm';\n\nimport Orphanages from '../models/Orphanage';\nimport OrphanageView from '../views/orphanage_view';\n\nimport * as Yup from 'yup';\n\nexport default {\n  async index(request: Request, response: Response) {\n    const orphanagesRepository = getRepository(Orphanages);\n\n    const orphanages = await orphanagesRepository.find({\n      relations: ['images'],\n    });\n\n    response.json(OrphanageView.renderMany(orphanages));\n  },\n\n  async show(request: Request, response: Response) {\n    const { id } = request.params;\n\n    const orphanagesRepository = getRepository(Orphanages);\n\n    const orphanage = await orphanagesRepository.findOneOrFail(id, {\n      relations: ['images'],\n    });\n\n    return response.json(OrphanageView.render(orphanage));\n  },\n\n  async create(request: Request, response: Response) {\n    const {\n      name,\n      latitude,\n      longitude,\n      about,\n      instructions,\n      opening_hours,\n      open_on_weekends,\n    } = request.body;\n  \n    const orphanagesRepository = getRepository(Orphanages);\n\n    const requestImages = request.files as Express.Multer.File[];\n\n    const images = requestImages.map(image =\u003e ({ path: image.filename}) );\n\n    const data = {\n      name,\n      latitude,\n      longitude,\n      about,\n      instructions,\n      opening_hours,\n      open_on_weekends,\n      images,\n    };\n\n    const schema = Yup.object().shape({\n      name: Yup.string().required(),\n      latitude: Yup.number().required(),\n      longitude: Yup.number().required(),\n      about: Yup.string().required(),\n      instructions: Yup.string().required(),\n      opening_hours: Yup.string().required(),\n      open_on_weekends: Yup.string().required(),\n      images: Yup.array(\n        Yup.object().shape({\n          path: Yup.string().required(),\n        })\n      ),\n    });\n\n    await schema.validate(data, {\n      abortEarly: false,\n    })\n  \n    const orphanage = orphanagesRepository.create(data);\n  \n    await orphanagesRepository.save(orphanage);\n  \n    return response.status(201).json(orphanage);\n  },\n};\n\n```\n\n## Trabalhando com Views\n\nUma analogia simples de View:\n\n\u003e Views são como uma camada entre o backend e o cliente que filtra e controla o fluxo de dados que serão consumidos pelo cliente.\n\nA baixo segue os views utilizados\n\n```ts\n// orphanage_view.ts\n\nimport Orphanage from '../models/Orphanage'\nimport ImageView from './image_view';\n\nexport default {\n  render(orphanage: Orphanage) {\n    return {\n      id: orphanage.id,\n      name: orphanage.name,\n      latitude: orphanage.latitude,\n      longitude: orphanage.longitude,\n      about: orphanage.about,\n      instructions: orphanage.instructions,\n      opening_hours: orphanage.opening_hours,\n      open_on_weekends: orphanage.open_on_weekends,\n      images: ImageView.renderMany(orphanage.images),\n    }\n  },\n\n  renderMany(orphanages: Orphanage[]) {\n    return orphanages.map(orphanage =\u003e this.render(orphanage));\n  }\n};\n\n```\n\n\n```ts\n// image_view.ts\n\nimport Image from '../models/Image'\n\nexport default {\n  render(image: Image) {\n    return {\n      id: image.id,\n      url: `http://localhost:3333/uploads/${image.path}`,\n    }\n  },\n\n  renderMany(image: Image[]) {\n    return image.map(image =\u003e this.render(image));\n  }\n};\n\n```\n\n## Validação dos dados\n\nNa validação dos dados será utilizado o validador de esquemas [Yup](https://github.com/jquense/yup).\n\nNo controller dado acima, a validação se encontra nessa parte:\n\n```ts\nconst schema = Yup.object().shape({\n  name: Yup.string().required(),\n  latitude: Yup.number().required(),\n  longitude: Yup.number().required(),\n  about: Yup.string().required(),\n  instructions: Yup.string().required(),\n  opening_hours: Yup.string().required(),\n  open_on_weekends: Yup.string().required(),\n  images: Yup.array(\n    Yup.object().shape({\n      path: Yup.string().required(),\n    })\n  ),\n});\n\nawait schema.validate(data, {\n  abortEarly: false,\n})\n```\n\n## Lidando com exceções\n\nPor padrão o express não trata os erros ```async```. Para isso precisamos utilizar o pacote ```express-async-errors``` e construir nosso ```errorHandler```:\n\n```ts\nimport { ErrorRequestHandler } from 'express';\nimport { ValidationError } from 'yup';\n\ninterface ValidationErrors {\n  [key: string]: string[];\n}\n\nconst errorHandler: ErrorRequestHandler = (error, request, response, next) =\u003e {\n  if(error instanceof ValidationError) {\n    let errors: ValidationErrors = {};\n\n    error.inner.forEach(err =\u003e {\n      errors[err.path] = err.errors;\n    });\n\n    return response.status(400).json({ message: 'Validation Fails', errors })\n  }\n\n  console.error(error);\n\n  return response.status(500).json({ message: 'internal server error'} );\n};\n\nexport default errorHandler;\n\n```\n\n## Árvore de arquivos Backend\n\nNo final desse dia sua árvore de arquivo ficará similar a essa:\n\n```\n📦src  \n ┣ 📂config  \n ┃ ┗ 📜upload.ts  \n ┣ 📂controllers  \n ┃ ┗ 📜OrphanagesController.ts  \n ┣ 📂database  \n ┃ ┣ 📂migrations  \n ┃ ┃ ┣ 📜1602604062694-create_orphanages.ts  \n ┃ ┃ ┗ 📜1602619431968-create_images.ts  \n ┃ ┣ 📜connection.ts  \n ┃ ┗ 📜database.sqlite  \n ┣ 📂errors  \n ┃ ┗ 📜handler.ts  \n ┣ 📂models  \n ┃ ┣ 📜Image.ts  \n ┃ ┗ 📜Orphanage.ts  \n ┣ 📂views  \n ┃ ┣ 📜image_view.ts  \n ┃ ┗ 📜orphanage_view.ts  \n ┣ 📜routes.ts  \n ┗ 📜server.ts\n```\n\n# Dia 03\n\nEsse dia foi muito mais prático do que teórico. Então só irei colocar as tasks realizadas, a árvore de arquivos para referência e um exemplo rodando em nuvem.\n\n## Tasks\n\n 1. Finalizar página do mapa\n 2. Copiando páginas faltantes\n 3. Criando navegação entre elas\n 4. Abstraindo componentes (Sidebar)\n 5. Conectando Frontend com Backend\n 6. Detalhe do orfanato\n 7. Criação de um orfanato\n\n## Exemplo\n\nDepois de finalizar o dia 3, coloquei o frontend e o backend online (irei tirar depois de algum tempo) para referências.\nSegue os links:\n\n - [Frontend](https://happy-frontend.netlify.app/)\n - [Backend](https://happy-back-end.herokuapp.com/)\n\n### Árvores de arquivos backend - Dia 3\n```\n📦src\n ┣ 📂config\n ┃ ┗ 📜upload.ts\n ┣ 📂controllers\n ┃ ┗ 📜OrphanagesController.ts\n ┣ 📂database\n ┃ ┣ 📂migrations\n ┃ ┃ ┣ 📜1602604062694-create_orphanages.ts\n ┃ ┃ ┗ 📜1602619431968-create_images.ts\n ┃ ┣ 📜connection.ts\n ┃ ┗ 📜database.sqlite\n ┣ 📂errors\n ┃ ┗ 📜handler.ts\n ┣ 📂models\n ┃ ┣ 📜Image.ts\n ┃ ┗ 📜Orphanage.ts\n ┣ 📂views\n ┃ ┣ 📜image_view.ts\n ┃ ┗ 📜orphanage_view.ts\n ┣ 📜routes.ts\n ┗ 📜server.ts\n```\n\n### Árvores de arquivos frontend - Dia 3\n\n```\n📦src\n ┣ 📂components\n ┃ ┗ 📜Sidebar.tsx\n ┣ 📂images\n ┃ ┣ 📜landing.svg\n ┃ ┣ 📜logo.svg\n ┃ ┗ 📜map-marker.svg\n ┣ 📂pages\n ┃ ┣ 📜CreateOrphanage.tsx\n ┃ ┣ 📜Landing.tsx\n ┃ ┣ 📜Orphanage.tsx\n ┃ ┗ 📜OrphanagesMap.tsx\n ┣ 📂services\n ┃ ┗ 📜api.ts\n ┣ 📂styles\n ┃ ┣ 📂components\n ┃ ┃ ┗ 📜sidebar.css\n ┃ ┣ 📂pages\n ┃ ┃ ┣ 📜create-orphanage.css\n ┃ ┃ ┣ 📜landing.css\n ┃ ┃ ┣ 📜orphanage-map.css\n ┃ ┃ ┗ 📜orphanage.css\n ┃ ┗ 📜global.css\n ┣ 📂utils\n ┃ ┗ 📜mapIcon.ts\n ┣ 📜App.tsx\n ┣ 📜index.tsx\n ┣ 📜react-app-env.d.ts\n ┗ 📜routes.tsx\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcassiofb-dev%2Fhappy-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcassiofb-dev%2Fhappy-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcassiofb-dev%2Fhappy-backend/lists"}