{"id":26568904,"url":"https://github.com/normyee/geospot-api","last_synced_at":"2026-04-20T11:34:31.221Z","repository":{"id":281049263,"uuid":"943449546","full_name":"normyee/geospot-api","owner":"normyee","description":"A robust backend API built with Clean Architecture and DDD principles, featuring MongoDB, JWT authentication, OpenStreetMap integration, and Docker containerization.","archived":false,"fork":false,"pushed_at":"2025-03-20T14:34:12.000Z","size":202,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-12T05:36:34.542Z","etag":null,"topics":["clean-architecture","ddd","docker-compose","geospatial","jwt-authentication","logging","mongo-session","mongodb","openstreetmap-api","repository-pattern","routing-controllers","typescript"],"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/normyee.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":"2025-03-05T18:13:54.000Z","updated_at":"2025-03-20T15:52:51.000Z","dependencies_parsed_at":"2025-03-20T15:47:25.582Z","dependency_job_id":null,"html_url":"https://github.com/normyee/geospot-api","commit_stats":null,"previous_names":["normyee/ozmap-challenge","normyee/georegion-api","normyee/geospot-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/normyee/geospot-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/normyee%2Fgeospot-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/normyee%2Fgeospot-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/normyee%2Fgeospot-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/normyee%2Fgeospot-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/normyee","download_url":"https://codeload.github.com/normyee/geospot-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/normyee%2Fgeospot-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31705574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T05:11:36.334Z","status":"ssl_error","status_checked_at":"2026-04-12T05:11:27.332Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["clean-architecture","ddd","docker-compose","geospatial","jwt-authentication","logging","mongo-session","mongodb","openstreetmap-api","repository-pattern","routing-controllers","typescript"],"created_at":"2025-03-22T20:18:24.106Z","updated_at":"2026-04-12T05:37:21.382Z","avatar_url":"https://github.com/normyee.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 🚀 **Solução** \n\nDesenvolvi o Backend utilizando `Clean Architecture` e modelagem `DDD`, aplicando injeção de dependências pelo módulo `app-geo.module.ts` e implementando inversão de dependências. Usei o padrão `Repository` para abstrair a persistência e `MongoDB` com `sessions` para garantir a atomicidade das transações. A autenticação é implementada com `JWT`. Para conteinerização, utilizei `Docker Compose`.\n\nIntegrei a `API` `OpenStreetMap` para conversão entre coordenadas e endereços. As rotas foram criadas com decorators e implementei um logger eficiente usando Pino. A solução conta com várias abstrações para garantir flexibilidade e manutenção fácil.\n\n## 🔍 **Funcionalidades Implementadas**\n\n### 🧑‍💻 Usuários\n- **CRUD completo**.\n- Conversão entre endereço e coordenadas via API de geolocalização.\n- Validação para evitar envio de ambos ou nenhum dos dados.\n- Atualização de localização segue a mesma regra.\n- Integração com API OpenStreetMap para conversão entre endereço e coordenadas geográficas.\n\n### 🌍 Regiões\n- **CRUD completo**.\n- Regiões representadas como polígonos em GeoJSON, associadas a um usuário.\n- Listagem de regiões contendo um ponto específico ou dentro de um raio definido.\n- Listar regiões a uma certa distância de um ponto, com opção de filtrar regiões não pertencentes ao usuário que fez a requisição.\n- **Geospatial Queries**:\n  - Operações avançadas utilizando índices `2dsphere` para suportar consultas espaciais eficientes.\n  - Implementação de consultas para localização de regiões e otimização de busca com indexação geoespacial.\n\n### 🚀 Extras\n- Autenticação com JsonWebToken (usuário pode ser passado na requisição de criação ou obtido no `/users/login`). ✔️\n- Container do MongoDB configurado com ReplicaSet SingleNode ✔️.\n- Utilização de **Mongo Sessions** para transações. ✔️\n- Documentação completa da API por ✔️.\n- Cobertura de código (baixa cobertura de testes) ⚠️. \n- Injeção de dependências utilizando `typedi` para melhor modularidade e testabilidade ✔️\n- Implementação de logs estruturados com `Pino` para melhor monitoramento ✔️.\n- Uso do `Zod` para garantir a integridade das configurações das variáveis de ambiente. ✔️\n\n### Como executar em minha máquina?\n\n- Clone o projeto em sua máquina: `git clone https://github.com/normyee/ozmap-challenge.git`\n- Entre no projeto: `cd ozmap-challenge`\n`\n- Crie um arquivo no pasta raiz com nome de `.env`\n- Abra o `.env.example` e passe no `.env` as mesmas variáveis\n- Instale as dependências: `yarn install`\n- Inicialize o contêiner: `docker-compose up --wait`\n- Inicialize a aplicação: `yarn start:prod`\n  \n#### Pronto 🎉\n- Agora pode criar seu usuário em `localhost:3003/users`\n## Documentação\n- [Como o Backend está estruturado?](doc/API-STRUCTURE.md/)\n\n### Endpoints\n- Utilize `https://geojson.io/` para obter `coordenadas` de um local nas endpoints de criação de usuário (`POST /users`) caso passe `coordinates` em vez do `address`. Se apenas passar o nome de um endereço válido, e automaticamente as coordenadas do endereço serão obtidas. Também use `https://geojson.io/` para obter pontos geométricos para criação de `regions`.\n\n- **CRUD completo para rota /users**.\n`POST - /users`\n```\nhttp://localhost:3000/users -\u003e Cria um novo usuário no sistema e retorna um Bearer Token de autenticação em todas as outras rotas, exceto esta rota e a de 'localhost:3003/users/login'\n```\n#### Exemplo Request:\n```\n{\n    \"name\": \"fulano\",\n    \"email\": \"fulano@gmail.com\",\n    \"coordinates\": [-48.47821611448057,-1.4229724705990219]\n}\n```\n#### Response esperada:\n```\n{\n    \"status\": \"success\",\n    \"message\": \"User has been created successfully\",\n    \"data\": {\n        \"id\": \"67d503d379222fd1a1bba282\",\n        \"name\": \"fulano\",\n        \"email\": \"fulano@gmail.com\",\n        \"address\": \"Passagem Marinho, Sacramenta, Belém, Região Geográfica Imediata de Belém, Região Geográfica Intermediária de Belém, Pará, Região Norte, 66083-000, Brasil\",\n        \"coordinates\": [\n            -48.47821611448057,\n            -1.4229724705990219\n        ]\n    },\n    \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2N2Q1MDNkMzc5MjIyZmQxYTFiYmEyODIiLCJpYXQiOjE3NDIwMTMzOTUsImV4cCI6MTc0MjAzMTM5NX0.--pZDPTGmMO9u6ASWyWggna4YkbP3-dTGEcDtIevpM4\"\n}\n\n```\n\n----------------------------------------------------------------------------------\n`POST - /login`\n```\nhttp://localhost:3003/auth/login-\u003e Retorna um Bearer Token que usaremos para acessar todas as outras rotas. *OBS*: O Bearer Token tem expiração de 5h\n```\n\n#### Exemplo Request:\n```\n{\n    \"email\": \"fulano@gmail.com\",\n}\n```\n\n#### Response esperada:\n```\n{\n    \"status\": \"success\",\n    \"message\": \"User has signed in successfully\",\n    \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2N2Q1MDNkMzc5MjIyZmQxYTFiYmEyODIiLCJpYXQiOjE3NDIwMTM5NjAsImV4cCI6MTc0MjAzMTk2MH0.-pG-4UP2YEob-6sPWW8ikFBf28_4CBoxZ5WDbfEjSvc\"\n}\n```\n----------------------------------------------------------------------------------\n- **CRUD completo para rota /regions**.\n`POST - /regions`\n```\nhttp://localhost:3003/regions-\u003e Cria uma região nova associada a um user. *OBS*: É necessário passar o Bearer Token obtido em POST \"/login\" ou POST \"/users\"\n```\n#### Exemplo Request:\n```\n{\n    \"name\": \"Belém Ponto\",\n    \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n            [\n                [\n                    -48.53588241001793,\n                    -1.289183889687223\n                ],\n                [\n                    -48.53588241001793,\n                    -1.496723909393296\n                ],\n                [\n                    -48.22618185632396,\n                    -1.496723909393296\n                ],\n                [\n                    -48.22618185632396,\n                    -1.289183889687223\n                ],\n                [\n                    -48.53588241001793,\n                    -1.289183889687223\n                ]\n            ]\n        ]\n    }\n}\n```\n\n#### Response esperada:\n```\n{\n    \"status\": \"success\",\n    \"message\": \"Region has been created successfully\",\n    \"data\": {\n        \"id\": \"67d5075179222fd1a1bba285\",\n        \"name\": \"Belém Ponto\",\n        \"userId\": \"67d503d379222fd1a1bba282\",\n        \"geometry\": {\n            \"type\": \"Polygon\",\n            \"coordinates\": [\n                [\n                    [\n                        -48.53588241001793,\n                        -1.289183889687223\n                    ],\n                    [\n                        -48.53588241001793,\n                        -1.496723909393296\n                    ],\n                    [\n                        -48.22618185632396,\n                        -1.496723909393296\n                    ],\n                    [\n                        -48.22618185632396,\n                        -1.289183889687223\n                    ],\n                    [\n                        -48.53588241001793,\n                        -1.289183889687223\n                    ]\n                ]\n            ]\n        }\n    }\n}\n```\n----------------------------------------------------------------------------------\n`GET - /regions/containing?lng=-48.4782055\u0026lat=-1.4229802\u0026page=1\u0026limit=30`\n```\nhttp://localhost:3003/containing -\u003e Retorna listagem de regiões contendo um ponto específico ou dentro de um raio definido\n```\n- **Parâmetros da rota**\n  - `lng` -\u003e longitude\n  - `lat` -\u003e latitude\n  - `page` -\u003e página atual da busca\n  - `limit` -\u003e limitar a quantidade por página\n\n#### Response esperado:\n```\n{\n    \"status\": \"success\",\n    \"message\": \"Regions containing the point [-48.4782055, -1.4229802] have been found successfully.\",\n    \"data\": [\n        {\n            \"_id\": \"67d5075179222fd1a1bba285\",\n            \"_name\": \"Belém Ponto\",\n            \"_userId\": \"67d503d379222fd1a1bba282\",\n            \"_geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            -48.53588241001793,\n                            -1.289183889687223\n                        ],\n                        [\n                            -48.53588241001793,\n                            -1.496723909393296\n                        ],\n                        [\n                            -48.22618185632396,\n                            -1.496723909393296\n                        ],\n                        [\n                            -48.22618185632396,\n                            -1.289183889687223\n                        ],\n                        [\n                            -48.53588241001793,\n                            -1.289183889687223\n                        ]\n                    ]\n                ]\n            }\n        }\n    ]\n}\n```\n----------------------------------------------------------------------------------\n`GET - /regions/near?lng=-48.451393226238935\u0026lat=-1.402774657344807\u0026km_distance=10\u0026page=1\u0026limit=10`\n```\nhttp://localhost:3003/containing -\u003e Retorna listagem de regiões contendo um ponto específico dentro de um raio definido em quilômetros\n```\n- **Parâmetros da rota**\n  - `lng` -\u003e longitude\n  - `lat` -\u003e latitude\n  - `km_distance` -\u003e distância em quilômetros do ponto\n  - `filter_user` -\u003e `true` para filtragem regiões por usuário\n  - `page` -\u003e página atual da busca\n  - `limit` -\u003e limitar a quantidade por página\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnormyee%2Fgeospot-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnormyee%2Fgeospot-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnormyee%2Fgeospot-api/lists"}