A localização, definida por latitude e longitude, pode ser usada em conjunto com outros dados para gerar insights para uma empresa, o que é conhecido como análise de localização.

As empresas que operam em todo o mundo usam análise de localização em toda a cadeia de valor, por exemplo, para localizar usuários, entregar serviços e veicular anúncios direcionados. O uso de análise de localização aumentou globalmente com o surgimento de mídias sociais e dispositivos móveis.

Neste tutorial, aprenderemos como construir uma API de serviço de relatório de análise de localização leve em Node.js. Ao final do tutorial, você poderá construir este tipo de API para seu próprio projeto. Você também terá um melhor entendimento de tratamento de erros e boa estrutura de arquivos em Node.js!

Vamos começar!

Pré-requisitos

Para acompanhar neste tutorial, você precisará do seguinte:

Familiaridade com Node.js, Express e Git Visual Studio Editor de código Conta Heroku Conta Postman

Configurando a estrutura do arquivo

Primeiro, nós precisamos configurar nossa estrutura de arquivos. Abra seu terminal e crie um novo diretório onde você armazenará todos os arquivos do projeto. Em seu terminal, digite o seguinte comando seguido do nome da pasta, lars:

mkdir lars

Abra o diretório de trabalho lars no editor de código do VS:

code.

Você verá a janela do código do VS aberta:

Janela de código do Visual Studio

Inicialize o diretório de trabalho abrindo seu terminal no Visual Studio e executando npm init-y.

Se desejar execute este comando fora do VS Code no terminal do seu sistema operacional, navegue até o diretório lars e execute o comando abaixo:

npm init-y

O código acima gera automaticamente o arquivo package.json:

Exibição do código do VS o arquivo package.json criado

Neste tutorial, usaremos Express como uma dependência. Instale o Express executando o comando abaixo:

npm install express–save Comando para instalar o Express como uma dependência

Depois de instalar o Express, você notará que uma pasta node_modules foi criada. Para confirmar se o Express está instalado, verifique seu arquivo package.json e você verá o Express instalado como uma dependência:

pasta node_modules criada e Express adicionado a package.json

Precisamos importar Express para nosso aplicativo porque ele é um módulo npm. Crie um novo arquivo chamado app.js no mesmo diretório que seu arquivo package.json:

Captura de tela da janela do código do VS mostrando app.js criado

Em seu arquivo app.js, requireExpress executando o código abaixo:

const express=require (‘express’); Importando expresso

Agora, chame Express para criar seu aplicativo, rotas e uma porta para execução de seu aplicativo:

const app=express ();

Node.js implementa modularidade, o que significa que separa seu aplicativo em módulos, ou vários arquivos, e exporta cada arquivo. Vamos exportar o aplicativo usando a palavra-chave export:

module.exports=app; arquivo app.js

Em seguida, crie outro arquivo chamado server.js no mesmo diretório do arquivo app.js. Requer o arquivo app.js no arquivo server.js:

const app=require (‘./app’);

Crie um arquivo chamado config.env no mesmo diretório que server.js. O arquivo config.env conterá todas as chaves process.env que iremos necessidade para nosso aplicativo. No arquivo config.env, crie uma variável PORT e defina a PORT para escutar na porta 8000:

PORT=8000

Depois de importar o aplicativo, crie uma constante chamada port no arquivo server.js. Defina-o para a variável PORT que você acabou de criar e uma porta padrão de 3000:

const port=process.env.PORT || 3000;

Finalmente, definiremos o aplicativo para escutar na porta com o método.listen ():

app.listen (port, ()=> {console.log (`Aplicativo escutando em $ {port } `)});

Construindo as rotas

Sempre que você visita uma página da web ou um aplicativo em execução na web, você está fazendo uma solicitação HTTP. O servidor responde com dados do back-end ou de um banco de dados, o que é conhecido como uma resposta HTTP.

Ao criar um recurso em um aplicativo da web, você está chamando a solicitação POST. Da mesma forma, se você tentar excluir ou atualizar um recurso em um aplicativo da web, estará chamando uma solicitação DELETE, PATCH ou UPDATE. Vamos construir rotas para lidar com essas solicitações.

Crie uma pasta chamada routes em seu diretório de trabalho e crie um arquivo chamado analyticsRoute.js dentro dela. Requer Express no arquivo analyticsRoute.js para definir a rota para a API:

const express=require (‘express’);

Também precisamos exigir nosso módulo de aplicativo do arquivo app.js:

const app=require (‘../app’);

Em seguida, criamos nossas rotas:

const router=express.Router ();

Por fim, exportaremos o roteador:

module.exports=router;

Construindo os controladores

Precisamos criar arquivos para controladores que importaremos para nosso arquivo analyticsRoutes. Primeiro, crie uma pasta chamada controladores em seu diretório de trabalho.

Nossa API usará o endereço IP e as coordenadas fornecidas pelo usuário para calcular a distância e a localização. Nossa solicitação precisa aceitar essas informações e a solicitação vinda do usuário.

Usaremos uma solicitação POST porque o usuário está incluindo dados no req.body. Para salvar as informações, precisamos exigir um módulo fs (sistema de arquivos) em nosso controlador.

Manipulando a solicitação POST

Crie um arquivo chamado storeController.js na pasta de controladores. No arquivo storeController.js, precisamos importar o módulo fs e o método fsPromises.readFile () para lidar com a promessa retornada, que é o endereço IP e as coordenadas do usuário.

Para instalar o módulo fs, abra seu terminal em seu diretório de trabalho e execute o seguinte comando:

npm i fs–save

Digite o seguinte código no topo do seu arquivo:

const fsp=require (‘fs’).promessas; const fs=require (‘fs’);

A seguir, criaremos o controlador que manipulará nossa rota para a solicitação POST. Usaremos a palavra-chave exportações e criaremos um Função de middleware que aceita três parâmetros:

req: representa o objeto de solicitação res: representa o próximo objeto de resposta: a função é chamada imediatamente após as exportações de middleware postAnalytics=async (req, res, next)=> {}

Agora, salvaremos as propriedades do objeto de dados no req.body no array reportAnalytics. Definiremos um objeto Date () para salvar a data em que todos os dados foram criados em uma chave createdAt:

reportAnalytics.push ({… req.body, createdAt: new Date ()});

Criaremos um arquivo chamado storeAnalytics.json para salvar o conteúdo de nosso array reportAnalytics como uma string usando JSON.stringify ():

await fsp.writeFile (`$ {__ dirname}/storeAnalytics.json` , JSON.stringify (reportAnalytics));

Precisamos verificar se o arquivo storeAnalytics.json existe quando um usuário faz uma solicitação POST. Se o arquivo existir, precisamos ler o arquivo e salvar a saída.

A saída contém uma constante chamada reportFile, que armazena o conteúdo do arquivo que foi lido. Use JSON.parse em reportFile para converter o conteúdo do arquivo em um objeto JavaScript:

//verifica se o arquivo existe if (fs.existsSync (`$ {__ dirname}/storeAnalytics.json`)) {//If o arquivo existe, lê o arquivo const reportFile=await fsp.readFile (`$ {__ dirname}/storeAnalytics.json`,’utf-8′)//converte o arquivo para JavaScript Object reportAnalytics=JSON.parse (reportFile)} else {//se o arquivo não existe return (‘Arquivo não existe’); }

O método fs.existsSync () verifica de forma síncrona se o arquivo existe. Ele aceita o caminho $ {__ dirname}/storeAnalytics.json como seu único parâmetro e aponta para a localização do arquivo que queremos verificar.

Usamos a palavra-chave await com reportFile para aguardar o resultado da leitura o arquivo com o método fsp.readFile (). Em seguida, especificamos o caminho do arquivo com o qual queremos ler ($ {__ dirname}/storeAnalytics.json. Definimos o formato de codificação para utf-8, que converterá o conteúdo lido do arquivo em uma string.

JSON.parse () converte reportFile em um objeto JavaScript e o armazena na matriz reportAnalytics. O código no bloco de instrução else será executado apenas quando o arquivo não existir. Por fim, usamos a instrução return porque queremos parar a execução da função após a execução do código.

Se o arquivo foi lido, criado e salvo com sucesso no arquivo storeAnalytics.json, precisamos enviar uma resposta. Vamos use o objeto de resposta (res), que é o segundo parâmetro em nossa função pós-analítica assíncrona:

res.status (201).json ({status:’sucesso’, dados: {mensagem:’IP e coordenadas tomadas com sucesso’}})

Nós responderemos com um status de sucesso e o IP da mensagem de dados e coordenadas tomadas com sucesso.

Seu arquivo storeController.js deve ser semelhante à imagem abaixo:

Lidando com a solicitação GET

Precisamos criar outro arquivo de controlador para lidar com nossa solicitação GET. Quando os usuários fazem uma solicitação GET para a API, calculamos sua localização com base em seus endereços IP e coordenadas.

Crie um arquivo chamado fetchController.js na pasta de controladores. No arquivo storeController.js, precisamos exigir o módulo fs e o método fsPromises.readFile () para lidar com a promessa retornada:

const fsp=require (‘fs’). Promises; const fs=require (‘fs’);

Vamos criar o controlador para lidar com nossa rota para a solicitação GET. Usaremos uma função de middleware e parâmetros semelhantes como fizemos para a solicitação POST acima:

exports.getAnalytics=async (req, res, next)=> {}

Dentro do middleware getAnalytics, digite o seguinte código para obter o endereço IP da consulta da solicitação:

const {ip}=req.query;

Agora, crie um array vazio que armazenará o conteúdo do req.body:

let reportAnalytics=[];

Como fizemos antes, precisamos verificar se o arquivo storeAnalytics.json existe. Se o arquivo existir, usaremos JSON.parse em reportFile para converter o conteúdo do arquivo em um objeto JavaScript:

if (fs.existsSync (`$ {__ dirname}/storeAnalytics.json`)) {const reportFile=esperar fsp.readFile (`$ {__ dirname}/storeAnalytics.json`,’utf-8′) reportAnalytics=JSON.parse (reportFile)} else {return (‘Arquivo não existe’); }

Agora, podemos salvar o endereço IP e as coordenadas do usuário no arquivo storeAnalytics.json. Sempre que o usuário solicitar o cálculo da geolocalização com base nas coordenadas fornecidas, o endereço IP será incluído na solicitação na forma de uma consulta.

Agora que obtivemos o endereço IP do req.query objeto, podemos escrever o código para verificar se o endereço IP fornecido no objeto req.query é o mesmo que o endereço IP armazenado no arquivo storeAnalytics.json:

para (let i=0; i No código acima, estamos usando forloop para fazer um loop pela matriz reportAnalytics. Inicializamos a variável i, que representa o índice do elemento atual no array reportAnalytics, para 0. Se i for menor que o comprimento do array reportAnalytics, nós o incrementamos.

Em seguida, verificamos se o reportAnalytics a propriedade do endereço IP do array é igual ao endereço IP fornecido no req.query.

Vamos calcular a localização dos endereços IP armazenados apenas na última hora:

const hourAgo=new Date (); hourAgo.setHours (hourAgo.getHours ()-1); const getReport=reportAnalytics.filter (el=> el.ip===ip && new Date (el.createdAt)> hourAgo)

No bloco de código acima, criamos uma constante chamada hourAgo e a definimos como um objeto Date. Usamos o método setHours () para definir hourAgo para a última hora getHours ()-1.

Quando os endereços IP atuais no arquivo reportAnalytics são equivalentes ou iguais aos endereços IP passados ​​no req.query , significando que os dados foram criados na última hora, getReport cria um conjunto constante para uma nova matriz.

Crie uma constante chamada coordinatesArray, que armazenará apenas as coordenadas que foram salvas na matriz getReport:

const coordinatesArray=getReport.map (element=> element.coordinates)

Em seguida, precisamos calcular a localização com as coordenadas. Precisamos iterar por coordinatesArray e calcular a localização passando os dois valores salvos como coordenadas:

let totalLength=0; for (deixe i=0; i No código acima, totalLength representa a distância total calculada a partir das duas coordenadas. Para iterar por meio do array coordenado, precisamos inicializar o resultado do nosso cálculo. Definir totalLength como zero inicializa a distância total.

A segunda linha contém o código de iteração que estamos usando para o loop. Inicializamos a variável i deixando i=0. A variável i representa o índice do elemento atual no coordinatesArray.

i

A seguir, verificaremos se o índice do elemento atual é igual ao número do último elemento na matriz. Em seguida, pausamos a execução do código de iteração e passamos para a próxima usando a palavra-chave break.

Finalmente, criamos uma função chamada calcularDistance que aceita dois argumentos, o primeiro e o segundo valores de coordenadas (longitude e latitude). Criaremos CalculeDistance em outro módulo e o exportaremos para o arquivo fetchController.js, então salvaremos nosso resultado final na variável totalLength que inicializamos.

Observe que toda solicitação precisa de uma resposta. Responderemos com um statusCode de 200 e um JSON contendo o valor da distância que calcularemos. A resposta só será exibida se o código for bem-sucedido:

res.status (200).json ({distance: totalLength})

Seu arquivo fetchController.js deve ser semelhante aos dois blocos de código a seguir:

O fetchController. arquivo js fetchController.js file continuation

Construa a função CalculeDistance

Em seu diretório de trabalho, crie uma nova pasta chamada utilitários e crie um arquivo chamado CalculeDistance.js dentro. Abra o arquivo CalculeDistance.js e adicione a seguinte função:

const CalculeDistance=(coordinate1, coordinate2)=> {const distance=Math.sqrt (Math.pow (Number (coordinate1.x)-Number (coordinate2.x ), 2) + Math.pow (Número (coordenada1.y)-Número (coordenada2.y), 2)); distância de retorno; } module.exports=CalculeDistance;

Na primeira linha, criamos uma função chamada calcularDistância que aceita dois argumentos: coordenada1 e coordenada2. Ele usa as seguintes equações:

Math.sqrt: raiz quadrada em matemática Math.pow: eleva um número a uma potência Número (): converte um valor em uma coordenada numérica1.x: o segundo valor da primeira coordenada ( longitude) coordinate2.x: o primeiro valor da primeira coordenada (longitude) coordinate1.y: o segundo valor da segunda coordenada (latitude) coordinate2.y: primeiro valor da segunda coordenada (latitude)

Agora que nós criou a função CalculeDistance, precisamos exigir a função em nosso código no arquivo fetchController.js. Adicione o código abaixo após o módulo fs:

const calculDistance=require (‘../utilities/recoverDistance’);

Implementando o tratamento de erros

É importante implementar o tratamento de erros caso nosso código falhe ou uma implementação específica não funcione da maneira que foi projetada. Adicionaremos tratamento para erros tanto no desenvolvimento quanto na produção.

Abra seu arquivo config.env e execute NODE_ENV=development para definir o ambiente para desenvolvimento.

Na pasta de controladores, crie um novo arquivo chamado errorController.js. O snippet de código abaixo cria uma função chamada sendErrorDev para lidar com os erros encontrados no ambiente de desenvolvimento:

const sendErrorDev=(err, res)=> {res.status (err.statusCode).json ({status: err.status , erro: err, mensagem: err.mensagem, pilha: err.stack,}); }

Criaremos uma função chamada sendErrorDev que aceita dois parâmetros, err para o erro e res para a resposta. O response.status obtém o statusCode do erro e responde com dados JSON.

Além disso, criaremos uma função chamada sendErrorProd que tratará os erros encontrados quando a API estiver no ambiente de produção:

const sendErrorProd=(err, res)=> {if (err.isOperational) {res.status (err.statusCode).json ({status: err.status, mensagem: err.message}); } else {console.error (‘Erro’, err); res.status (500).json ({status:’erro’, mensagem:’Algo deu errado’})}}

Na pasta de utilitários, crie um arquivo chamado appError.js e digite o seguinte código:

classe AppError extends Error {construtor (mensagem, statusCode) {super (mensagem); this.statusCode=statusCode; this.status=`$ {statusCode}`.startsWith (‘4′)?’falha’:’erro’; this.isOperational=true; Error.captureStackTrace (this, this.constructor); }} module.exports=AppError;

Criaremos uma classe chamada AppError que estende o objeto de erro .

Em seguida, criaremos um construtor que inicializará o objeto da classe. Ele aceita dois parâmetros chamados message e statusCode. O super método chama o construtor com um argumento, passa-o para a mensagem e obtém acesso às propriedades e métodos do construtor.

A seguir, definiremos a propriedade statusCode do construtor para statusCode. Definimos a propriedade status do construtor para qualquer statusCode que comece com 4, por exemplo, o 404 statusCode para falha ou erro.

Crie outro arquivo chamado catchAsync.js e adicione o seguinte código nele:

module.exports=fn=> {return (req, res, next)=> {fn (req, res, next).catch (next); }}

Adicionar tratamento de erros aos arquivos do controlador

Requer o arquivo appError.js e o arquivo catchAsync.js em seus arquivos storeController.js e fetchController.js. Coloque essas duas instruções de importação no topo do seu código em ambos os arquivos:

const catchAsync=require (‘../utilities/catchAsync’); const AppError=require (‘../utilities/appError’);

Nos arquivos storeController.js e fetchController.js, envolva suas funções com o método catchAsync () da seguinte maneira:

//Para arquivo storeController.js exports.postAnalytics=catchAsync (async (req, res, next )=> {…}//Para o arquivo fetchController.js exports.getAnalytics=catchAsync (async (req, res, next)=> {…}

Em seguida, em seu arquivo fetchController.js, execute o Classe AppError:

para (let i=0; i Em seguida, execute a classe AppError em seu arquivo storeController.js:

if (fs.existsSync (`$ {__ dirname}/storeAnalytics.json`)) {const reportFile=await fsp.readFile (`$ {__ dirname}/storeAnalytics.json`,’utf-8′) reportAnalytics=JSON.parse (reportFile)} else {return next (new AppError (‘Arquivo não existe’, 404)) ;}

O código em seu arquivo storeController.js e fetchController.js s devem ser semelhantes às seguintes capturas de tela:

Captura de tela do arquivo storeController.js linhas 1-32 do arquivo fetchController.js linhas do arquivo fetchController.js 33-37

Configurando a validação

Precisamos validar os dados recebidos no req.body , que inclui os endereços IP e as coordenadas, está correto e no formato certo. As coordenadas devem ter no mínimo dois valores, representando longitude e latitude.

Na pasta utilitários, crie uma nova pasta chamada Validação. Na pasta Validation, crie um arquivo chamado schema.js. O arquivo schema.js conterá o formato desejado para todos os dados fornecidos no req.body. Usaremos o validador joi :

npm install joi

​​Digite o seguinte código no esquema. arquivo js:

const Joi=require (‘joi’); esquema const=Joi.object (). keys ({ip: Joi.string (). ip (). required (), coordinates: Joi.object ({x: Joi.number (). required (), y: Joi.number (). required ()}). required ()}) module.exports=schema;

No bloco de código acima, exigimos o validador joi e o usamos para criar nosso esquema. Em seguida, definimos o endereço IP para ser sempre uma string e validamos o endereço IP exigindo-o no corpo da solicitação.

Definimos as coordenadas como um objeto. Definimos os valores xey, que representam os valores de longitude e latitude, como um número e os exigimos para que nosso código seja executado. Por fim, exportamos o esquema.

Na pasta de validação, crie outro arquivo chamado validateIP.js. Dentro, vamos escrever o código para validar o endereço IP usando o pacote npm is-ip . Vamos exportar o pacote para o nosso código.

No arquivo validateIP.js, adicione o seguinte código:

const isIp=require (‘is-ip’); const fsp=require (‘fs’). promessas; const fs=require (‘fs’); export.validateIP=(req, res, next)=> {if (isIp (req.query.ip)!==true) {return res.status (404).json ({status:’fail’, data: { mensagem:’IP inválido, não encontrado.’}})} next (); }

Execute o seguinte comando para instalar as dependências necessárias para nossa API:

npm install body-parser cors dotenv express fs is-ip joi morgan ndb nodemon

Seu arquivo app.js deve ser semelhante à captura de tela abaixo:

app.js

Na seção de scripts do arquivo package.json, adicione o seguinte snippet de código:

“start: dev”:”node server.js”,”debug”:”ndb server.js”

Seu arquivo package.json deve ser semelhante à imagem abaixo:

arquivo package.json

Atualize seu arquivo analyticsRoute.js com o seguinte código:

const express=require (‘express’); const app=require (‘../app’); roteador const=express.Router (); const validateIP=require (‘../utilities/Validation/validateIP’); const storeController=require (‘../controllers/storeController’); const fetchController=require (‘../controllers/fetchController’); router.route (‘/analytics’). post (storeController.postAnalytics).get (validateIP.validateIP, fetchController.getAnalytics); módulo.exportações=roteador;

Agora, concluímos a construção de nossa API de análise de localização! Agora, vamos testar nosso código para ter certeza de que funciona.

Testando a API

Usaremos Postman para testar nossa API. Vamos iniciar nossa API para garantir que esteja sendo executada em nosso terminal:

node server.js

Você verá a seguinte saída em seu terminal:

Terminal

A saída final de nossa API, que está hospedada em Heroku , deve ser semelhante à saída abaixo:

Você pode teste você mesmo esta API na documentação hospedada.

Conclusão

A análise de localização é uma ótima ferramenta para empresas. As informações de localização podem permitir que as empresas atendam melhor aos clientes atuais e em potencial.

Neste tutorial, aprendemos a construir uma ferramenta que obtém informações de localização na forma de endereços IP e coordena e calcula a distância. Configuramos nossa estrutura de arquivos em Node.js, criamos rotas para lidar com solicitações GET e POST, adicionamos tratamento de erros e, finalmente, testamos nosso aplicativo.

Você pode usar as informações que aprendeu neste tutorial para configurar sua própria API de relatório de localização, que você pode personalizar para suas necessidades comerciais.