Módulos Internos (Core Modules) - Parte 2

Módulos Internos (Core Modules) - Parte 2

Seja bem vindo a parte dois dos módulos internos 😁

Na lição passada, você aprendeu um pouco mais sobre:

Por hora, vamos continuar nosso processo de aprendizado conhecendo outros módulos como:

  • events para escutar eventos na aplicação,
  • fs (file system) para manipulação de arquivos,
  • url para manipulação e tratamento de urls,
  • os para interagir com o sistema operacional.

Vamos nessa? 😉

Arquivos da lição

Os arquivos que veremos durante o decorrer desta lição, podem ser encontrados nos links abaixo:

Módulo events

No mundo do NodeJS, nós temos acesso a um módulo chamado de events, que fornece uma infraestrutura para se trabalhar com eventos que acontecem dentro da sua aplicação.

Eventos estes capazes de lidar com operações assíncronas, permitindo a escuta e manipulação desses eventos de maneira eficiente, e isso inclui:

  • Interações do usuário,
  • Leitura de arquivos,
  • Conexões em rede,
  • Além de gatilhos que podemos acionar em algum ponto durante a execução do nosso código.

É importante ressaltar que a classe principal que existe por trás do módulo events, é a conhecida como Event Emitter, onde ela comporta de forma similar aos eventos do navegador (Cliente Side).

Sabendo que o módulo events possui uma classe por trás, devemos saber que não basta apenas importar o módulo e usar seus métodos (como vinhamos fazendo anteriormente), como também precisarémos instanciar a classe.

Vamos ver como tudo isso funciona na prática 🤓

Como de praxe, vamos começar criando um arquivo chamado de app.js dentro da pasta do seu projeto.

Em importar o módulo do Event Emitter:

/* Aplicação de Testes (app.js) */

const eventEmitter = require('events');

Como dito anteriormente, o módulo em sí é uma classe do Javascript, e como toda boa classe, ela precisa ser instanciada para ser usada, logo:

/* Aplicação de Testes (app.js) */

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();//Instancia a classe Event Emitter

Legal, para começar a usar o Event Emitter primeiro nós temos que registrar um evento a ser escutado.

Vamos supor, que queriamos mostrar uma mensagem no console toda vez que um evento chamado runner seja executado.

Para isso, precisamos fazer o uso do método on presente na classe do Event Emitter da seguinte forma:

/* Aplicação de Testes (app.js) */

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();//Instancia a classe Event Emitter

eventEmitter.on('runner', () => {
 console.log('Você já viu o filme do Blade Runner 2044?');
});

Traduzindo para o bom português, o on significa quando, sendo assim: "Quando o evento chamado runner for executado, eu vou executar esse bloco, que por sua vez vai mostrar uma mensagem no console".

Para executar esse evento, nós fazemos o uso de um outro método da classe do Event Emitter chamado de emit().

Ele é responsável por emitir um determinado evento ao sistema, e se o nome do evento existir, bem, o bloco do on é acionado, caso contrário, nada acontece.

/* Aplicação de Testes (app.js) */

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();//Instancia a classe Event Emitter

eventEmitter.on('runner', () => {
 console.log('Você já viu o filme do Blade Runner 2044?');
});

console.log('Eu sempre quis te fazer uma pergunta...');

eventEmitter.emit('runner');

console.log('Eu já assisti, e é muuuito legal!!!');

De acordo com o código acima, a aplicação vai registrar um novo evento chamado runner, e em seguida vai mostrar a mensagem "Eu sempre quis te fazer uma pergunta...", depois ela vai emitir o evento runner, que por sua vez vai perguntar ao usuário sobre o filme do blade runner, para depois mostrar a mensagem final dizendo que o filme é muito legal.

A primeira vista parece um exemplo bem bobo e sem nenhuma aplicabilidade, mas se você for parar para pensar, você pode definir seus eventos globalmente para serem ativados em partes internas do seu código, facilitando a comunicação entre elas.

Principalmente quando temos funções assíncronas, que precisamos chamar determinada parte do código mais tarde.

Métodos do event emitter

Além dos métodos on e emit que acabamos de conhecer acima, a classe ainda conta com algumas particularidades, vejamos:

on(eventName, listener): é o método usado para registrar um ouvinte. Nós já vimos ele em ação logo acima, mas eu resolvi trazer ele novamente para te mostrar que é possível não só registrar um evento, como também receber argumentos dentro desse evento.

eventEmitter.on('eventoPersonalizado', (arg1, arg2) => {
 console.log(`Evento Personalizado foi acionado com argumentos ${arg1} e ${arg2}`);
});

emit(eventName, [args]): é o método usado para emitir um evento específico. Também já fizemos o uso dele, mas para dar contexto ao que fizemos anteriormente, também podemos passar alguns argumentos junto a ele.

eventEmitter.emit('eventoPersonalizado', 'Portal', 'Micilini');

removeListener(eventName, listener): é o método usado para remover um determinado ouvinte.

const meuOuvinte = (arg1, arg2) => {
 console.log(`Evento Personalizado foi acionado com argumentos ${arg1} e ${arg2}`);
};

eventEmitter.on('eventoPersonalizado', meuOuvinte);

// Removendo o ouvinte
eventEmitter.removeListener('eventoPersonalizado', meuOuvinte);

Observe no código acima, que o ouvinte foi definido dentro de uma variável específica, e não diretamente dentro do método como vinhamos fazendo antes.

once(eventName, listener): é o método usado para registrar um ouvinte de modo que ele seja acionado apenas uma única vez.

eventEmitter.once('eventoOnce', () => {
 console.log('Este ouvinte será acionado apenas uma vez.');
});

eventEmitter.emit('eventoOnce');//Resultado: 'Este ouvinte será acionado apenas uma vez.'

eventEmitter.emit('eventoOnce');//Nada vai acontecer...

Para saber com mais riqueza de detalhes como funciona o módulo events, não deixe de consultar a documentação.

Módulo fs (File System)

Um dos módulos bastante utilizados pelos desenvolvedores NodeJS é o fs (File System), ele é considerado um dos módulos principais que nos dá a possibilidade de se trabalhar com a criação de diretórios e arquivos.

Uma coisa que o Javascript (Client Side) foi incapaz de fazer (por questões de segurança).

Vamos começar criando o nosso arquivo app.js na pasta do seu projeto, e após isso fazer a implementação do nosso módulo fs:

/* Aplicação de Testes (app.js) */

const fs = require('fs');

Agora vamos aprender um pouco mais sobre os seus métodos 😉

Métodos do fs (file system)

fs.readFile(): é um método usado para ler um determinado arquivo que esta salvo localmente de forma assíncrona.

fs.readFile('meu-arquivo.txt', 'utf8', (err, data) => {
 if (err) {
 console.error('Ocorreu um erro durante a abertura do arquivo: ' + err);
 return;
 }
 console.log('Conteúdo do Arquivo: ' + data);
});

Como podemos ver, estamos executando um comando responsável por abrir um arquivo chamado 'meu-arquivo.txt', que existe na mesma pasta do nosso projeto.

Observação: Caso esse arquivo existisse dentro de uma pasta específica, você deverá informar o caminho dentro do primeiro parâmetro, exemplo: 'minha/pasta/meu-arquivo.txt'.

O segundo parâmetro 'utf8', refere-se ao charset que será usado durante a abertura do arquivo (de modo a evitar caracteres estranhos, oferecendo compatibilidade).

Já o terceiro parâmetro, refere-se a função de callback, que por sua vez retorna dois parâmetros:

err: representa algum tipo de erro caso o arquivo não consiga abrir.

data: representa o conteúdo existente dentro desse arquivo.

Como faço para ler um arquivo JSON usando o fs.readfile()?

Para abrir um arquivo JSON, basta executar a mesma lógica acima, adicionando outras funções do próprio Javascript como o JSON.PARSE por exemplo.

const fs = require('fs');

const nomeArquivo = 'dados.json';

fs.readFile(nomeArquivo, 'utf8', (err, data) => {
 if (err) {
 console.error(err);
 return;
 }

 try {
 const arrayDeDados = JSON.parse(data);

 console.log('Array obtido do arquivo JSON:', arrayDeDados);
 } catch (error) {
 console.error('Erro ao analisar o JSON:', error);
 }
});

É importante ressaltar que o método fs.readfile() é assíncrono, e isso significa dizer que a sua aplicação continua rodando normalmente enquanto o arquivo ainda está sendo carregado.

Caso você precisa travar a aplicação enquanto o arquivo é aberto, você pode fazer o uso do método abaixo.

fs.readFileSync(): é um método síncrono que bloqueia a execução da sua aplicação até que a leitura do arquivo seja concluída.

try {
 const conteudo = fs.readFileSync('meu-arquivo.txt', 'utf8');
 console.log('Conteúdo do arquivo:', conteudo);
} catch (error) {
 console.error('Erro ao ler o arquivo:', error);
}

Observe que esse método não possui uma função de callback, pois ele é síncrono.

Observe também, que abaixo temos um outro exemplo de como abrir e interpretar um arquivo JSON:

try {
 const conteudoJSON = fs.readFileSync('dados.json', 'utf8');
 const objetoDeDados = JSON.parse(conteudoJSON);
 console.log('Objeto obtido do arquivo JSON:', objetoDeDados);
} catch (error) {
 console.error('Erro ao ler o arquivo JSON:', error);
}

É importante ressaltar que os métodos readFile e readFileSync, também podem abrir outros tipos de arquivos como imagens, vídeos e afins.

fs.writeFile(): é um método usado para salvar um novo arquivo, ou sobreescrever o conteúdo de um arquivo existente de forma assíncrona. (Esse método não cria diretórios)

fs.writeFile('novo-arquivo.txt', 'Conteúdo que será salvo dentro do arquivo', 'utf8', (err) => {
 if (err) {
 console.error('Ocorreu um erro na escrita: ' + err);
 return;
 }
 console.log('Arquivo foi escrito com sucesso.');
});

Observe que o segundo parâmetro é o conteúdo que será salvo dentro desse arquivo. Note que esse método é assíncrono, e não trava a execução do seu programa enquanto salva o arquivo.

fs.writeFileSync(): é um método usado para salvar um novo arquivo , ou quem sabe, sobreescrever o conteúdo de um arquivo existente de forma síncrona.(Esse método não cria diretórios)

try {
 const dadosParaEscrever = 'Este é um exemplo de conteúdo para o arquivo.';
 fs.writeFileSync('novo-arquivo.txt', dadosParaEscrever, 'utf8');
 console.log('Conteúdo foi escrito no arquivo com sucesso.');
} catch (error) {
 console.error('Erro ao escrever no arquivo:', error);
}

fs.mkdir(): é um método usado para criar pastas (diretórios).

fs.mkdir('minha-pasta', (err) => {
 if (err) {
 console.error('Erro ao criar um diretório: ' + err);
 return;
 }
 console.log('Diretório criado com sucesso.');
});

fs.readdir(): é um método usado para ler e retornar o conteúdo existente em um diretório.

fs.readdir('.', (err, files) => {
 if (err) {
 console.error('Erro ao ler diretório' + err);
 return;
 }
 console.log('Arquivos no diretório atual:', files);
});

Observe que o primeiro parâmetro é o caminho do diretório que será lido, nesse caso, estamos lendo a pasta atual do projeto, mas poderia ser 'minha-pasta' ou 'minha-pasta/icones/' por exemplo.

fs.unlink(): é um método usado para apagar um determinado arquivo.

fs.unlink('meu-arquivo.txt', (err) => {
 if (err) {
 console.error('Erro durante a remoção do arquivo: ' + err);
 return;
 }
 console.log('Arquivo removido com sucesso.');
});

fs.rmdir(): é um método usado para apagar um determinado diretório.

const diretorio = 'minha-pasta';

fs.rmdir(diretorio, {
 recursive: true
}, (err) => {
 if (err) {
 console.error('Erro ao remover o diretório:', err);
 } else {
 console.log('Diretório removido com sucesso.');
 }
});

Observe que o segundo parâmetro diz se a remoção será recursiva (true) ou não (false). Caso o parâmetro estiver setado como true, ele irá remover o diretório e todos os seus conteúdos de forma recursiva.

fs.rename(): é um método usado para renomear arquivo e diretórios.

fs.rename('novo-arquivo.txt', 'novo-novo-arquivo.txt', (err) => {
 if (err) {
 console.error('Erro durante o processo: ' + err);
 return;
 }
 console.log('Arquivo renomeado com sucesso.');
});

fs.stat(): é um método usado para recuperar as estatísticas de um determinado arquivo.

fs.stat('novo-novo-arquivo.txt', (err, stats) => {
 if (err) {
 console.error('Erro durante o processo: ' + err);
 return;
 }
 console.log('Estatísticas do arquivo: ', stats);
});

Observação: O retorno deste método, representa um objeto de elementos.

fs.copy(): é um método usado para copiar um determinado arquivo.

fs.copyFile('origem.txt', 'destino.txt', (err) => {
 if (err) {
 console.error('Erro durante o processo: ' + err);
 return;
 }
 console.log('Arquivo copiado com sucesso.');
});

fs.watch(): é um método bastante similar ao módulo events, onde permite que você assista ás mudanças em arquivos ou diretórios.

const watcher = fs.watch('novo-novo-arquivo.txt');

watcher.on('change', (event, filename) => {
 console.log(`O arquivo ${filename} foi alterado.`);
});

fs.existsSync(): é um método usado para verificar se um arquivo ou diretório existe de forma síncrona.

const caminhoArquivo = 'caminho/do/seu/arquivo.txt';
const caminhoDiretorio = 'caminho/do/seu/diretorio';

// Verificando a existência de um arquivo de forma síncrona
if (fs.existsSync(caminhoArquivo)) {
 console.log('O arquivo existe.');
} else {
 console.log('O arquivo não existe.');
}

// Verificando a existência de um diretório de forma síncrona
if (fs.existsSync(caminhoDiretorio)) {
 console.log('O diretório existe.');
} else {
 console.log('O diretório não existe.');
}

fs.promises.access(): é um método usado para verificar se um arquivo ou diretório existe de forma assíncrona.

const caminhoArquivo2 = 'caminho/do/seu/arquivo.txt';
const caminhoDiretorio2 = 'caminho/do/seu/diretorio';

// Verificando a existência de um arquivo de forma assíncrona
fs.access(caminhoArquivo2)
 .then(() => {
 console.log('O arquivo existe.');
 })
 .catch((err) => {
 console.log('O arquivo não existe.');
 });

// Verificando a existência de um diretório de forma assíncrona
fs.access(caminhoDiretorio2)
 .then(() => {
 console.log('O diretório existe.');
 })
 .catch((err) => {
 console.log('O diretório não existe.');
 });

Criando arquivos e diretórios de forma automática

Caso você queria criar arquivos existentes em diretórios de forma automática, você pode fazer isso em conjunto com os módulos path e fs da seguinte forma:

const fs = require('fs');
const path = require('path');

const diretorio = 'caminho/do/seu/diretorio';
const nomeArquivo = 'arquivo.txt';
const caminhoCompleto = path.join(diretorio, nomeArquivo);

// Garantindo a existência do diretório
if (!fs.existsSync(diretorio)) {
 fs.mkdirSync(diretorio, { recursive: true });
}

// Escrevendo no arquivo
const novosDados = 'Estes são os novos dados para o arquivo.';
fs.writeFileSync(caminhoCompleto, novosDados, 'utf8');

console.log('Arquivo atualizado com sucesso.');

Para saber mais sobre o funcionamento do módulo fs, não deixe de consultar a documentação.

Módulo url

Como o próprio nome já nos diz, o módulo url nos oferece uma série de de funções para trabalhar com URLs (Uniform Resource Locators).

Ele permite que você faça validações, formatações e manipulações de strings que representam URLs.

Para começarmos, crie um novo arquivo chamado app.js dentro da pasta do seu projeto, e comece importando o módulo url:

/* Aplicação de Testes (app.js) */

const url = require('url');

Feito isso, partiu colocar a mão na massa 😉

Métodos do url

url.parse(urlString, [parseQueryString], [slashesDenoteHost]): é um método que analisa uma URL, e retorna um objeto com suas partes componentes.

const minhaURL = 'https://www.micilini.com/path?query=123#fragmento';

const parsedUrl = url.parse(minhaURL, true);
console.log(parsedUrl);

url.format(urlObject): é um método que gera uma URL formatada a partir de um objeto de URL.

const minhaURLCustomizada = {
 protocol: 'https:',
 host: 'www.micilini.com',
 pathname: '/path',
 query: { query: '123' },
 hash: 'fragmento'
};

const formattedUrl = url.format(minhaURLCustomizada);
console.log(formattedUrl);

url.resolve(from, to): é um método que resolve uma URL alternativa em relação a outra URL.

const baseUrl = 'https://www.micilini.com/path/';
const relativeUrl = '../cursos.html';

const resolvedUrl = url.resolve(baseUrl, relativeUrl);
console.log(resolvedUrl);

url.resolveObject(from, to): é um método que funciona similar ao resolve(), mas que retorna um objeto de URL.

//resolveObject
const baseUrl2 = 'https://www.micilini.com/path/';
const relativeUrl2 = '../curso.html';

const resolvedUrlObject2 = url.resolveObject(baseUrl2, relativeUrl2);
console.log(resolvedUrlObject2);

Como verificar se uma URL é válida ou não?

Para isso, você precisa instanciar a classe do módulo URL dentro de uma estrutura de try..catch da seguinte forma:

function isValidURL(url) {
 try {
 // Tenta analisar a URL
 const parsedUrl = new URL(url);
 return true;
 } catch (error) {
 // Se ocorrer um erro, a URL não é válida
 return false;
 }
}

// Exemplo de uso:
const urlToCheck = 'https://www.micilini.com';
if (isValidURL(urlToCheck)) {
 console.log('A URL é válida.');
} else {
 console.log('A URL não é válida.');
}

Para mais informações sobre o módulo url, não deixe de consultar a documentação.

Módulo os

Um dos módulos mais interessantes já criados pela equipe de desenvolvimento do NodeJS é o os, ele é capaz de extrair informações variadas do sistema operacional onde a aplicação está rodando.

Para utiliza-lo, comece criando o arquivo app.js dentro da pasta do seu projeto, e em seguida basta importar o módulo os da seguinte forma:

/* Aplicação de Testes (app.js) */

const os = require('os');

Feito isso, vamois testar alguns de seus métodos.

Métodos do os

os.platform(): é um método que retorna a plataforma do sistema operacional (win32, linux, darwin)

console.log('Plataforma Atual:', os.platform()); // Exibe a plataforma do sistema (ex: win32, linux, darwin)

os.arch(): é um método que retorna a arquitetura do processador.

console.log('Arquitetura:', os.arch()); // Exibe a arquitetura do processador (ex: x64, arm64)

os.totalmem(): é um método capaz de retornar a quantidade de memoria RAM instalada na máquina.

console.log('Memória total (bytes):', os.totalmem()); // Retorna a quantidade total de memória do sistema

os.freemem(): é um método capaz de retornar a quantidade de memória livre na máquina.

console.log('Memória livre (bytes):', os.freemem()); // Retorna a quantidade de memória livre no sistema

os.cpus(): é um método que retorna as informações relacionadas a CPU.

console.log('Informações da CPU:', os.cpus()); // Retorna um array com informações sobre os núcleos da CPU

os.userInfo(): é um método que retorna informações relacionadas ao usuário atual (uid, gid, username, homedir, e shell).

console.log('Usuário atual:', os.userInfo()); // Retorna informações sobre o usuário atual

os.tmpdir(): é um método que retorna o diretório temporário do sistema atual.

console.log('Diretório temporário:', os.tmpdir()); // Retorna o diretório temporário do sistema

Para mais informações sobre o funcionamento do módulo os, não deixe de consultar a documentação.

Conclusão

Ufa  😅

Nesta lição aprendemos bastante coisa sobre alguns dos módulos principais do NodeJS.

Não chegamos a ver toooodos os métodos de cada um dos módulos, até porque são milhares deles, e a documentação está aí pra isso 😄 (E eu ficaria horas e horas falando sobre eles).

Na próxima lição, entraremos de cabeça em um dos módulos mais utilizados, o famoso módulo http, responsável por dar a vida em algumas (se não a maioria) das aplicações web.

Te encontro lá 😉