Skip to content

Bem-vindo ao repositório Estudando Node.js, este repositório foi criado para fornecer um ponto de partida abrangente utilizando roadmap para o estudo e prática de Node.js.

Notifications You must be signed in to change notification settings

RickLustri/EstudandoNodeJs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 

Repository files navigation

Estudando NodeJs

Introdução ao Node.js

# O que é Node.js?

Node.js é um ambiente de execução JavaScript no lado do servidor, permitindo que você use JavaScript para construir aplicações que rodam fora do navegador. Baseado no motor V8 do Google Chrome, ele permite a execução rápida e eficiente de código JavaScript no servidor.

# Por que Node.js?

Node.js é popular por seu alto desempenho, graças ao modelo de E/S não-bloqueante e baseado em eventos, que permite a gestão eficiente de múltiplas conexões simultâneas. Ele também unifica a linguagem usada tanto no frontend quanto no backend, simplificando o desenvolvimento. Além disso, possui um vasto ecossistema de pacotes e uma comunidade ativa que facilita a adição de funcionalidades e suporte.

# História do Node.js

Node.js foi criado por Ryan Dahl em 2009 para superar as limitações dos servidores web tradicionais, que não gerenciavam bem grandes volumes de conexões simultâneas. A solução foi criar um ambiente de execução com E/S não-bloqueante, o que tornou o Node.js ideal para aplicações escaláveis.

# Node.js vs Navegador

Apesar de ambos utilizarem JavaScript, o Node.js e o ambiente do navegador têm diferenças fundamentais. O Node.js é usado para operações no servidor, como manipulação de arquivos e redes, enquanto o navegador se concentra na interação com o DOM. No Node.js, o JavaScript tem acesso completo ao sistema, exigindo cuidados adicionais com segurança, enquanto no navegador, o JavaScript é executado em um ambiente seguro.

# Executando Código Node.js

Para executar código no Node.js, você precisa primeiro instalá-lo em seu sistema. Após a instalação, você pode criar um arquivo JavaScript, por exemplo app.js, e escrever o código que deseja executar:

console.log("Hello, Node.js!");

Depois, no terminal, você pode executar esse código usando o comando:

node app.js

Isso irá executar o código dentro do arquivo app.js e exibir "Hello, Node.js!" no terminal.

Se você quiser criar um servidor web simples, poderia escrever algo assim:

const http = require('http');

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello, World!\n');
});

server.listen(3000, () => {
  console.log('Server running at http://localhost:3000/');
});

E ao rodar node app.js, você terá um servidor rodando localmente, que responde com "Hello, World!" para qualquer requisição.


Introdução aos Módulos no Node.js

O Node.js é uma plataforma popular para desenvolver aplicações no lado do servidor. Uma das suas principais características é o suporte a módulos, que permitem dividir o código em partes reutilizáveis, organizadas e de fácil manutenção. No Node.js, existem dois sistemas principais de módulos: **CommonJS** e **ESM (ECMAScript Modules)**. Além disso, você pode criar e importar módulos personalizados, bem como utilizar a palavra-chave `global` para definir variáveis globais.

# CommonJS

O sistema de módulos CommonJS foi o primeiro a ser implementado no Node.js. Ele é amplamente utilizado e tem uma sintaxe simples para exportar e importar módulos.

  • Exportando Módulos: Usamos module.exports ou exports para exportar funções, objetos, ou valores.
  • Importando Módulos: Usamos require para importar um módulo.

Exemplo de Módulo CommonJS:

// Nome do arquivo: math.js
function somar(a, b) {
    return a + b;
}

function subtrair(a, b) {
    return a - b;
}

// Exportando as funções
module.exports = {
    somar,
    subtrair
};

Exemplo de Importação no CommonJS:

// Nome do arquivo: app.js

// Aqui estamos puxando o arquivo 'math.js' para dentro do 'app.js'
const math = require('./math');

console.log(math.somar(2, 3)); // Saída: 5
console.log(math.subtrair(5, 3)); // Saída: 2

# ESM (ECMAScript Modules)

O ESM é o sistema de módulos padrão definido na especificação ECMAScript (ES6+). Ele é mais moderno e também é compatível com os navegadores, o que torna o código mais consistente entre o frontend e o backend.

  • Exportando Módulos: Usamos export para exportar funções, objetos ou valores.
  • Importando Módulos: Usamos import para importar módulos.

Exemplo de Módulo ESM:

// math.mjs
export function somar(a, b) {
    return a + b;
}

export function subtrair(a, b) {
    return a - b;
}

Exemplo de Importação no ESM:

// app.mjs
import { somar, subtrair } from './math.mjs';

console.log(somar(2, 3)); // Saída: 5
console.log(subtrair(5, 3)); // Saída: 2

# Diferenças Principais entre CommonJS e ESM:

  1. Sintaxe: CommonJS usa require e module.exports, enquanto ESM usa import e export.
  2. Suporte Nativo: CommonJS é o sistema de módulos padrão no Node.js, enquanto o ESM foi adicionado em versões mais recentes e requer o uso da extensão .mjs ou a definição de "type": "module" no package.json.
  3. Assincronismo: ESM suporta importações assíncronas, o que permite carregar módulos de maneira mais eficiente.

# Módulos Personalizados: Criação e Importação

Os módulos personalizados são simplesmente arquivos JavaScript que exportam funções, objetos ou valores que podem ser reutilizados em outras partes do seu código.

Criando um Módulo Personalizado (CommonJS):

// utils.js
function apresentar(nome) {
    return `Olá, ${nome}!`;
}

module.exports = apresentar;

Importando e Usando o Módulo Personalizado:

// app.js
const apresentar = require('./utils');

console.log(apresentar('Henrique')); // Saída: Olá, Henrique!

Criando um Módulo Personalizado (ESM):

// utils.mjs
export function apresentar(nome) {
    return `Olá, ${nome}!`;
}

Importando e Usando o Módulo Personalizado:

// app.mjs
import { apresentar } from './utils.mjs';

console.log(apresentar('Rick')); // Saída: Olá, Rick!

# Palavara-Chave global

No Node.js, global é um objeto especial que pode ser usado para definir variáveis ou funções que estão disponíveis globalmente em toda a aplicação, semelhante ao objeto window no navegador.

Exemplo de Uso do global:

// Definindo uma variável global
global.minhaVarGlobal = 'Este é um valor global';

console.log(global.minhaVarGlobal); // Saída: Este é um valor global

// Definindo uma função global
global.apresentar = function(nome) {
    return `Olá, ${nome}!`;
};

console.log(global.apresentar('Node.js')); // Saída: Olá, Node.js!

Cuidados ao Usar global:

  1. Evitar Conflitos: Como as variáveis e funções no objeto global são acessíveis em toda a aplicação, isso pode causar conflitos de nome e tornar o código mais difícil de depurar.
  2. Boa Prática: É uma boa prática evitar o uso de global e preferir passar variáveis e funções explicitamente como argumentos ou importá-las de módulos específicos.
Você pode aprender mais sobre no artigo: MDN - Modules

Introdução ao NPM

O NPM (Node Package Manager) é a ferramenta padrão para gerenciar pacotes no ecossistema Node.js. Ele permite que você instale, compartilhe e gerencie dependências (bibliotecas e ferramentas) que seu projeto precisa. Além disso, o NPM facilita a criação de scripts para automatizar tarefas e suporta a criação e publicação de pacotes. Neste guia, exploraremos detalhadamente os conceitos de npx, Versionamento Semântico, instalações globais e locais, atualização de pacotes, execução de scripts, workspaces e a criação de pacotes.

# npx

O npx é uma ferramenta que vem com o NPM e permite executar pacotes diretamente da linha de comando sem instalá-los globalmente no seu sistema. É útil para testar pacotes ou executar comandos únicos.

Exemplo de Uso do npx:

# Usando npx para criar um novo projeto React
npx create-react-app meu-projeto

Explicação:

  • npx baixa e executa o pacote create-react-app temporariamente.
  • O comando cria um novo diretório chamado meu-projeto com uma aplicação React configurada.
  • Sem necessidade de instalação global: O create-react-app não precisa ser instalado globalmente, economizando espaço e evitando conflitos com outras versões.

Vantagens do npx:

  • Executa pacotes temporariamente: Ideal para usar ferramentas sem poluir o ambiente global.
  • Versão mais recente: Sempre usa a versão mais atual do pacote.

# Versionamento Semântico (Semantic Versioning)

O Versionamento Semântico (SemVer) é um sistema para versionar pacotes, composto por três números: MAJOR.MINOR.PATCH.

Exemplo de Versionamento Semântico:

"dependencies": {
  "express": "^4.17.1"
}

Explicação:

  • 4 (MAJOR): Mudanças que quebram a compatibilidade. Atualizações que podem exigir alterações no seu código.
  • 17 (MINOR): Novas funcionalidades que são compatíveis com versões anteriores. Adições que não quebram a compatibilidade.
  • 1 (PATCH): Correções de bugs que não afetam a compatibilidade. Atualizações que apenas resolvem problemas sem introduzir novas funcionalidades.

Regras de Atualização:

  • ^4.17.1 permite atualizações para 4.18.0 ou 4.17.2, mas não para 5.0.0.
  • ~4.17.1 permite atualizações para 4.17.2, mas não para 4.18.0.

Importância do SemVer:

  • Controle de Compatibilidade: Garante que as atualizações de dependências não quebrem seu código.
  • Gestão de Dependências: Ajuda a escolher quando adotar novas funcionalidades ou aplicar correções de bugs.

# Instalação Global vs Local

Você pode instalar pacotes no nível do projeto (local) ou no nível do sistema (global).

Instalação Local: O pacote é instalado no diretório node_modules do seu projeto. Usado para dependências específicas do projeto.

# Instalando o pacote 'lodash' localmente
npm install lodash

Explicação:

  • O pacote lodash é instalado na pasta node_modules do projeto.
  • Adicionado ao arquivo package.json como uma dependência local.

Instalação Global: O pacote é instalado em uma localização global no seu sistema, acessível de qualquer lugar. Usado para ferramentas de linha de comando.

# Instalando o pacote 'nodemon' globalmente
npm install -g nodemon

Explicação:

  • O pacote nodemon é instalado globalmente, permitindo que você o execute de qualquer diretório.
  • É útil para ferramentas que você usa em múltiplos projetos ou diretamente na linha de comando.

# Atualização de Pacotes

Manter suas dependências atualizadas é crucial para segurança e funcionalidade.

Atualizando um Pacote Específico:

# Atualizando o pacote 'lodash' para a versão mais recente permitida pela SemVer
npm update lodash

Explicação:

  • Atualiza o pacote lodash para a versão mais recente compatível com a especificada em package.json.

Atualizando Todos os Pacotes:

# Atualizando todas as dependências do projeto
npm update

Explicação:

  • Atualiza todos os pacotes listados no package.json para a versão mais recente permitida pela SemVer.

Atualização Forçada:

# Atualizando para a última versão disponível, mesmo que quebre a compatibilidade
npm install lodash@latest

Explicação:

  • Instala a versão mais recente do pacote lodash, ignorando a compatibilidade com a versão especificada anteriormente.

# Executando Scripts

No package.json, você pode definir scripts personalizados para automatizar tarefas.

Definindo Scripts no package.json:

"scripts": {
  "start": "node app.js",
  "test": "jest",
  "build": "webpack --config webpack.config.js"
}

Explicação:

  • start: Executa node app.js para iniciar o aplicativo.
  • test: Executa jest para rodar testes automatizados.
  • build: Executa webpack para construir o projeto usando uma configuração específica.

Executando Scripts:

# Executando o script 'start'
npm run start

# Executando o script 'build'
npm run build

Explicação:

  • npm run start e npm run build executam os scripts definidos no package.json.
  • O script start pode ser executado apenas com npm start (sem run).

# npm Workspaces

Os workspaces permitem gerenciar múltiplos pacotes em um único repositório (monorepo).

Configuração de Workspaces:

{
  "name": "meu-monorepo",
  "private": true,
  "workspaces": [
    "pacote-a",
    "pacote-b"
  ]
}

Explicação:

  • workspaces: Lista os diretórios pacote-a e pacote-b que contêm seus próprios package.json.
  • private: true: Garante que o repositório não será publicado no NPM.

Benefícios:

  • Gerenciamento Centralizado: Permite instalar e atualizar dependências para todos os pacotes de uma vez.
  • Compartilhamento de Dependências: Facilita o compartilhamento de dependências entre pacotes.

# Criando Pacotes

Criar pacotes permite compartilhar código reutilizável com outros projetos ou com a comunidade.

Passos para Criar um Pacote:

  1. Inicialize o pacote:
    npm init
  • Gera um package.json onde você define o nome, versão, descrição e outras informações do pacote.
  1. Escreva o código:
  • Crie os arquivos e implemente a funcionalidade do seu pacote.
  1. Teste o pacote:
  • Teste seu código localmente para garantir que tudo funciona como esperado.
  1. Publique o pacote:
    npm publish
  • Publica seu pacote no repositório do NPM para que outros possam instalá-lo e usá-lo.

Exemplo de package.json para um Pacote:

{
  "name": "meu-pacote",
  "version": "1.0.0",
  "description": "Um pacote exemplo",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["exemplo", "npm", "pacote"],
  "author": "Seu Nome",
  "license": "ISC"
}

Explicação:

  • name: Nome do pacote.
  • version: Versão inicial do pacote.
  • description: Descrição breve do que o pacote faz.
  • main: O ponto de entrada principal do pacote (arquivo principal).
  • scripts: Scripts relacionados ao desenvolvimento e teste.
  • keywords: Palavras-chave que ajudam na busca do pacote.
  • author e license: Informações sobre o autor e a licença do pacote.
Você pode aprender mais sobre no artigo: NPM - docs

Tratamento de erros

# System Errors (Erros de Sistema)

Erros de sistema em Node.js são aqueles que resultam de falhas no ambiente de execução, como problemas de I/O (entrada/saída), falhas de rede, ou falta de recursos do sistema. Esses erros geralmente ocorrem durante operações assíncronas, como ao tentar acessar arquivos, se conectar a bancos de dados, ou comunicar-se com APIs externas.

Exemplo:

Imagine que você tem um servidor Node.js que precisa ler um arquivo de configuração no momento em que inicia. Se o arquivo não estiver disponível (por exemplo, foi excluído ou o caminho está errado), isso resultará em um erro de sistema.

const fs = require('fs');

// Tentativa de ler um arquivo que não existe no sistema
fs.readFile('/path/to/config.json', 'utf8', (err, data) => {
   if (err) {
       // Código do erro 'ENOENT' significa 'Error NO ENTry', indicando que o arquivo não foi encontrado
       console.error('Erro ao ler o arquivo:', err.code, '-', err.message);
       process.exit(1); // Encerra o processo devido a um erro crítico
   } else {
       // O arquivo foi lido com sucesso
       console.log('Configurações carregadas:', data);
   }
});
  • fs.readFile tenta ler um arquivo de configuração.
  • Se o arquivo não for encontrado, um erro ENOENT será lançado, interrompendo o processo do servidor para evitar comportamento imprevisível.

# User Specified Errors (Erros Especificados pelo Usuário)

Erros especificados pelo usuário são aqueles que você, como desenvolvedor, define explicitamente no código para indicar que uma condição específica não foi atendida. Isso é útil para validar dados ou garantir que certas pré-condições sejam satisfeitas antes de prosseguir.

Exemplo:

Considere que você tem uma função que deve dividir dois números, mas a divisão por zero é indefinida. Você pode lançar um erro especificado para evitar que isso aconteça.

function divide(a, b) {
    if (b === 0) {
        // Lança um erro se a tentativa for de dividir por zero
        throw new Error('Divisão por zero não é permitida!');
    }
    return a / b;
}

try {
    // Tentativa de dividir 10 por 0, o que lançará um erro
    const result = divide(10, 0);
    console.log('Resultado:', result);
} catch (err) {
    // Captura o erro e exibe uma mensagem amigável
    console.error('Erro ocorrido:', err.message);
}
  • A função divide verifica se o divisor é zero.
  • Se for zero, um erro é lançado com uma mensagem clara.
  • O try...catch captura o erro e impede que o programa falhe sem controle.

# Assertion Errors (Erros de Asserção)

Asserções são condições que você espera que sejam verdadeiras durante a execução do código. No Node.js, você pode usar o módulo assert para garantir que essas condições sejam atendidas. Se uma asserção falhar, um erro de asserção é lançado, geralmente indicando que algo está muito errado no código.

Exemplo:

Vamos supor que você está desenvolvendo uma função para somar dois números. Você quer garantir que a função sempre retorne o resultado correto.

const assert = require('assert');

function add(a, b) {
    return a + b;
}

// Teste de asserção para garantir que a função 'add' está funcionando corretamente
const result = add(2, 2);
assert.strictEqual(result, 4, 'O resultado esperado é 4, mas obtivemos um valor diferente');

console.log('Todos os testes passaram!');
  • A função add retorna a soma de dois números.
  • assert.strictEqual compara o resultado com o valor esperado.
  • Se o resultado não for 4, um erro de asserção é lançado com uma mensagem explicativa.

# JavaScript Errors (Erros de JavaScript)

Erros de JavaScript são os erros padrão que ocorrem no código JavaScript, como TypeError, ReferenceError, SyntaxError, entre outros. Em Node.js, esses erros podem ocorrer por diversas razões, como chamar uma função inexistente, acessar variáveis indefinidas, ou escrever código com sintaxe incorreta.

Exemplo:

Imagine que você tenta chamar uma função que não foi definida.

try {
    // Tentativa de chamar uma função que não existe
    nonExistentFunction();
} catch (err) {
    // Captura o erro ReferenceError e exibe uma mensagem de erro
    console.error('Erro capturado:', err.name, '-', err.message);
}
  • nonExistentFunction() tenta chamar uma função que não foi declarada.
  • O try...catch captura o ReferenceError e exibe uma mensagem clara para o desenvolvedor.

# Uncaught Exceptions (Exceções Não Capturadas)

Exceções não capturadas ocorrem quando um erro é lançado e não é tratado dentro de um bloco try...catch. No Node.js, isso pode causar o término do processo, o que pode ser desastroso em um ambiente de produção. Para evitar isso, você pode usar process.on('uncaughtException'), mas isso deve ser usado com cuidado.

Exemplo:

Suponha que você tenha um servidor que, por alguma razão, faz uma chamada a uma função que não existe, e esse erro não é capturado.

process.on('uncaughtException', (err) => {
    // Captura exceções não tratadas
    console.error('Exceção não capturada:', err.message);
    // Finaliza o processo para evitar inconsistências
    process.exit(1);
});

// Função inexistente que causará um erro
nonExistentFunction();
  • process.on('uncaughtException') captura exceções que não foram tratadas.
  • É importante encerrar o processo para evitar que o servidor fique em um estado inconsistente.

# Handling Async Errors (Tratamento de Erros Assíncronos)

Erros assíncronos ocorrem frequentemente em Node.js devido à natureza não bloqueante do ambiente. Ao trabalhar com callbacks, promessas, ou async/await, é crucial lidar corretamente com erros para evitar que o aplicativo falhe silenciosamente ou tenha comportamentos inesperados.

Exemplo com Callback:

Suponha que você está lendo um arquivo que pode não existir.

const fs = require('fs');

// Tentativa de ler um arquivo que pode não existir
fs.readFile('/path/to/file.txt', 'utf8', (err, data) => {
    if (err) {
        // Erro na leitura do arquivo
        console.error('Erro ao ler o arquivo:', err.message);
        return;
    }
    console.log('Conteúdo do arquivo:', data);
});

Exemplo com Promises:

Agora, usando promessas para lidar com o mesmo caso.

const fs = require('fs').promises;

async function readFile() {
    try {
        // Tentativa de ler um arquivo
        const data = await fs.readFile('/path/to/file.txt', 'utf8');
        console.log('Conteúdo do arquivo:', data);
    } catch (err) {
        // Lida com o erro de forma assíncrona
        console.error('Erro ao ler o arquivo:', err.message);
   }
}

readFile();
  • fs.readFile com callbacks e promessas demonstra duas abordagens para lidar com erros assíncronos.
  • Em ambos os casos, o erro é capturado e tratado para evitar falhas silenciosas.

# Callstack / Stack Trace (Pilha de Chamadas / Rastreamento de Pilha)

A pilha de chamadas, ou stack trace, é um relatório que mostra a sequência de chamadas de função que levou a um erro. Em Node.js, a stack trace é extremamente útil para depuração, pois ajuda a identificar onde exatamente o erro ocorreu e como o fluxo do programa chegou a esse ponto.

Exemplo:

Imagine que você tem várias funções aninhadas e uma delas lança um erro.

function functionOne() {
    functionTwo();
}

function functionTwo() {
    functionThree();
}

function functionThree() {
    // Lança um erro propositalmente
    throw new Error('Algo deu errado na função três!');
}

try {
    functionOne();
} catch (err) {
    // Exibe a stack trace completa para ajudar na depuração
    console.error('Stack Trace:', err.stack);
}
  • As funções functionOne, functionTwo, e functionThree são chamadas em sequência.
  • Quando functionThree lança um erro, a stack trace mostra todas as funções chamadas antes do erro ocorrer.

# Using Debugger (Usando Depurador)

Node.js inclui ferramentas de depuração que permitem aos desenvolvedores examinar o estado do programa durante a execução. O depurador pode ser usado para definir pontos de interrupção, inspecionar variáveis, e seguir o fluxo do código passo a passo. Isso é essencial para entender o comportamento do código e localizar erros.

Exemplo:

Vamos ativar o depurador em um script Node.js.

function add(a, b) {
    debugger; // Define um ponto de interrupção
    return a + b;
}

const result = add(5, 3);
console.log('Resultado:', result);
  • O debugger é uma palavra-chave que, ao ser atingida, pausa a execução do código.
  • Ao rodar o código com node inspect script.js, o depurador é ativado, permitindo examinar o estado do programa no ponto de interrupção.
Você pode aprender mais sobre no artigo: MDN - Error

Introdução a Programação Assíncrona

Programação assíncrona é uma técnica que permite que um programa execute tarefas sem precisar esperar que uma tarefa seja concluída antes de iniciar a próxima. Em JavaScript, isso é fundamental para garantir que a aplicação continue responsiva, especialmente em ambientes de execução como navegadores e servidores.

# Event Emitter

Um Event Emitter é um padrão que permite que objetos emitam eventos e outros objetos escutem e respondam a esses eventos. Esse padrão é amplamente utilizado em Node.js para a comunicação entre diferentes partes de uma aplicação.

Exemplo:

// Importa o módulo EventEmitter
const EventEmitter = require('events'); 

// Cria uma nova instância do EventEmitter
const myEmitter = new EventEmitter();

// Define um ouvinte para o evento 'event'
myEmitter.on('event', () => {
  console.log('Um evento ocorreu!');
});

// Emite o evento 'event'
myEmitter.emit('event');
  1. Importamos o módulo events e criamos uma instância do EventEmitter.
  2. Usamos o método on para registrar um ouvinte para o evento chamado 'event'.
  3. O ouvinte registrado executará uma função que imprime uma mensagem no console.
  4. Em seguida, usamos o método emit para emitir o evento 'event', o que aciona o ouvinte registrado e imprime a mensagem.

# Event Loop

O Event Loop é um mecanismo que permite ao JavaScript executar código de forma assíncrona e gerenciar o tempo de execução de tarefas. Ele é responsável por garantir que o JavaScript continue a processar operações enquanto espera por operações assíncronas.

Exemplo:

console.log('Início');

setTimeout(() => {
  console.log('Executando após 2 segundos');
}, 2000);

console.log('Fim');

/*
Saída:
        Início
        Fim
        Executando após 2 segundos
*/
  1. O console.log('Início') é executado imediatamente e imprime 'Início'.
  2. O setTimeout agenda a execução de uma função após 2 segundos, mas não bloqueia o restante do código.
  3. O console.log('Fim') é executado imediatamente após o início do setTimeout.
  4. Após 2 segundos, a função dentro do setTimeout é executada, imprimindo 'Executando após 2 segundos'.
  • O Event Loop garante que o código assíncrono seja executado após o código síncrono.

Writing Async Code

Escrever código assíncrono é essencial para garantir que operações demoradas não bloqueiem o restante do código. Existem várias maneiras de escrever código assíncrono em JavaScript: Promises, async/await, Callbacks, e métodos de temporização como setTimeout e setInterval.

# Promises

Promises são objetos que representam a eventual conclusão (ou falha) de uma operação assíncrona e permitem encadear ações a serem realizadas após a conclusão da operação.

Exemplo:

const minhaPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Operação concluída com sucesso!');
  }, 2000);
});

minhaPromise.then(result => {
  console.log(result); // Imprime: 'Operação concluída com sucesso!'
}).catch(error => {
  console.log('Erro:', error);
});
  1. Criamos uma nova Promise que resolve após 2 segundos com a mensagem 'Operação concluída com sucesso!'.
  2. Usamos o método then para definir o que fazer quando a Promise é resolvida.
  3. Usamos o método catch para tratar possíveis erros.

# async/await

async e await são uma maneira de escrever código assíncrono que parece síncrono, tornando-o mais fácil de ler e entender.

Exemplo:

function espera(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function executar() {
  console.log('Início');

  await espera(2000); // Espera 2 segundos

  console.log('2 segundos se passaram');
}

executar();
  1. Definimos uma função espera que retorna uma Promise que resolve após um tempo especificado.
  2. Usamos a palavra-chave async para criar uma função assíncrona executar.
  3. Dentro de executar, usamos await para esperar a resolução da Promise retornada por espera.
  4. O código parece síncrono, mas realmente está aguardando a Promise ser resolvida.

# Callbacks

Callbacks são funções passadas como argumentos para outras funções e são executadas quando a operação assíncrona é concluída.

Exemplo:

function executarDepoisDe(ms, callback) {
  setTimeout(callback, ms);
}

executarDepoisDe(2000, () => {
  console.log('2 segundos se passaram');
});
  1. Definimos a função executarDepoisDe, que recebe um tempo em milissegundos e uma função callback.
  2. Usamos setTimeout para executar a função callback após o tempo especificado.
  3. Passamos uma função de callback para executarDepoisDe que imprime uma mensagem após 2 segundos.

# setTimeout

setTimeout é usado para executar uma função após um atraso especificado em milissegundos.

Exemplo:

console.log('Início');

setTimeout(() => {
  console.log('Executado após 3 segundos');
}, 3000);

console.log('Fim');
  1. O console.log('Início') é executado imediatamente.
  2. setTimeout agenda a execução de uma função após 3 segundos.
  3. O console.log('Fim') é executado imediatamente após o setTimeout.
  4. Após 3 segundos, a função do setTimeout é executada, imprimindo 'Executado após 3 segundos'.

# setInterval

setInterval é usado para executar uma função repetidamente com um intervalo de tempo específico.

Exemplo:

let contador = 0;

const intervalo = setInterval(() => {
  console.log('Executado a cada 2 segundos');
  contador++;

  if (contador === 3) {
    clearInterval(intervalo); // Para o intervalo após 3 execuções
  }
}, 2000);
  1. Definimos um contador para rastrear o número de execuções.
  2. setInterval executa a função a cada 2 segundos e incrementa o contador.
  3. Quando o contador atinge 3, usamos clearInterval para parar o intervalo.

# setImmediate

setImmediate é usado para executar uma função imediatamente após o ciclo de eventos atual. Está disponível apenas em ambientes Node.js.

Exemplo:

console.log('Início');

setImmediate(() => {
  console.log('Executado imediatamente após o ciclo de eventos');
});

console.log('Fim');
  1. O console.log('Início') é executado imediatamente.
  2. setImmediate agenda a execução de uma função assim que o ciclo de eventos atual termina.
  3. O console.log('Fim') é executado imediatamente após o setImmediate.
  4. Após o ciclo de eventos, a função do setImmediate é executada, imprimindo 'Executado imediatamente após o ciclo de eventos'.

# process.nextTick

process.nextTick é usado para executar uma função na próxima iteração do loop de eventos, antes que qualquer outro código assíncrono seja executado. Também disponível apenas em Node.js.

Exemplo:

console.log('Início');

process.nextTick(() => {
  console.log('Executado na próxima iteração do loop de eventos');
});

console.log('Fim');
  1. O console.log('Início') é executado imediatamente.
  2. process.nextTick agenda a execução de uma função na próxima iteração do loop de eventos.
  3. O console.log('Fim') é executado imediatamente após o process.nextTick.
  4. A função do process.nextTick é executada na próxima iteração do loop de eventos, imprimindo 'Executado na próxima iteração do loop de eventos'.
Você pode aprender mais sobre no artigo: JavaScript info

Manipulando Arquivos em Node.js

No desenvolvimento com Node.js, a manipulação de arquivos é uma tarefa comum e essencial. Seja para ler dados de um arquivo, salvar informações ou monitorar mudanças em diretórios, o Node.js oferece diversas ferramentas e módulos que facilitam o trabalho com arquivos. Além dos recursos internos da própria linguagem, como __dirname e __filename, o Node.js possui módulos como fs e path, que tornam as operações com o sistema de arquivos mais simples e eficientes. Além disso, a comunidade também contribuiu com pacotes de código aberto como fs-extra e chokidar, que oferecem funcionalidades avançadas para manipulação de arquivos e diretórios.

# Constantes Globais: __dirname e __filename

As constantes globais __dirname e __filename são duas ferramentas fundamentais do Node.js, utilizadas para obter o caminho do diretório e o nome do arquivo atualmente em execução, respectivamente. Elas são especialmente úteis para trabalhar com sistemas de arquivos e para criar caminhos absolutos, independentemente de onde o script foi executado.

O Que é __dirname e quando usar?

A constante global __dirname retorna o caminho absoluto do diretório onde o script Node.js atualmente em execução está localizado. Ela é definida automaticamente pelo Node.js e está disponível em todos os módulos.

__dirname é útil quando você precisa trabalhar com arquivos ou criar caminhos relativos ao diretório onde o seu script está localizado, em vez do diretório de onde o Node.js foi iniciado.

Exemplo:

// Exibe o caminho absoluto do diretório onde este script está localizado
console.log('Diretório do script atual:', __dirname);

// Exemplo: Criar um caminho absoluto para um arquivo localizado no mesmo diretório que o script
const path = require('path');
const fullPathToFile = path.join(__dirname, 'meuArquivo.txt');

console.log('Caminho absoluto para o arquivo:', fullPathToFile);
  • Independência de Plataforma: __dirname garante que o caminho do diretório seja obtido de forma consistente, independentemente do sistema operacional (Windows, macOS, Linux).
  • Confiabilidade: Como __dirname sempre aponta para o diretório onde o script reside, você pode usá-lo para construir caminhos de forma segura e confiável, evitando problemas com caminhos relativos que podem ocorrer dependendo de onde o Node.js é executado.

O Que é __filename e quando usar?

A constante global __filename retorna o caminho absoluto do arquivo atualmente em execução. Ela inclui o nome do arquivo junto com o caminho completo.

__filename é útil quando você precisa do caminho completo do script que está sendo executado, por exemplo, para registrar logs, ou para manipular o próprio script em tempo de execução.

Exemplo:

// Exibe o caminho absoluto do arquivo atualmente em execução
console.log('Caminho completo do script atual:', __filename);

// Exemplo: Separar o nome do arquivo e o caminho do diretório
const path = require('path');
const fileName = path.basename(__filename);
const directoryName = path.dirname(__filename);

console.log('Nome do arquivo:', fileName);
console.log('Diretório do arquivo:', directoryName);
  • Extração de Informação: Usando path.basename e path.dirname (do módulo path), você pode facilmente extrair o nome do arquivo e o diretório do caminho completo obtido através de __filename.
  • Contexto Completo: __filename é particularmente útil em cenários onde é necessário ter o contexto completo de onde o código está sendo executado, o que pode ser essencial para fins de depuração e análise.

Diferença entre __dirname e process.cwd()

__dirname:

  • Descrição: Aponta para o diretório onde o arquivo JavaScript atual está localizado.
  • Uso: Ideal para trabalhar com caminhos relativos ao próprio script.

process.cwd():

  • Descrição: Retorna o diretório de trabalho atual de onde o Node.js foi executado.
  • Uso: Mais útil para caminhos relativos ao diretório de execução do comando Node.js.

Exemplo:

console.log('__dirname:', __dirname);  // Caminho do diretório onde este script está localizado
console.log('process.cwd():', process.cwd());  // Diretório de onde o Node.js foi iniciado
  • Contexto Diferente: __dirname é estático e se refere sempre ao diretório do script, enquanto process.cwd() é dinâmico e depende de onde o Node.js foi executado. Saber quando usar um ou outro pode ajudar a evitar problemas com caminhos relativos.

Conclusão

As constantes globais __dirname e __filename são ferramentas poderosas no Node.js para trabalhar com caminhos de arquivos e diretórios. Elas garantem que você tenha acesso ao caminho absoluto do diretório e do arquivo em execução, o que é crucial para criar aplicações seguras e independentes de plataforma. Entender como e quando usá-las permite que você manipule caminhos de arquivos de forma eficaz e confiável, evitando erros comuns relacionados a caminhos relativos e execução em diferentes ambientes.

# Processo de Trabalho Atual: process.cwd()

O método process.cwd() é uma função do Node.js que retorna o diretório de trabalho atual da aplicação. Isso significa que ele fornece o caminho absoluto do diretório em que o Node.js foi executado. Esse método é particularmente útil quando você precisa trabalhar com arquivos e diretórios relativos ao diretório de execução da sua aplicação.

O que é process.cwd() e sua importância?

O método process.cwd() retorna uma string que representa o caminho absoluto do diretório de trabalho atual da aplicação Node.js. cwd significa "Current Working Directory" (Diretório de Trabalho Atual).

Saber o diretório de trabalho atual é importante para garantir que você esteja acessando arquivos e diretórios no local correto, especialmente ao lidar com caminhos relativos.

Exemplo:

// Importa o módulo 'fs' para trabalhar com o sistema de arquivos
const fs = require('fs');

// Obtém o diretório de trabalho atual
const currentWorkingDirectory = process.cwd();

// Exibe o diretório de trabalho atual
console.log('Diretório de trabalho atual:', currentWorkingDirectory);

// Exemplo de uso prático: Listar todos os arquivos e diretórios no diretório de trabalho atual
fs.readdir(currentWorkingDirectory, (err, files) => {
    if (err) {
        return console.error('Erro ao ler o diretório:', err);
    }
    console.log('Conteúdo do diretório atual:', files);
});
  • Diretório de Trabalho: Quando você executa o comando node script.js em um terminal, o process.cwd() retornará o diretório no qual você estava ao rodar esse comando.
  • Uso Prático: Como mostrado no exemplo, você pode usar o diretório de trabalho atual para realizar operações de leitura ou manipulação de arquivos, como listar o conteúdo do diretório.

Diferença entre __dirname e process.cwd()

__dirname:

  • Descrição: __dirname é uma variável global que contém o caminho absoluto do diretório onde o script atualmente em execução está localizado.
  • Uso: Usado quando você quer saber onde o arquivo JavaScript atual está fisicamente localizado.

process.cwd():

  • Descrição: process.cwd() retorna o diretório de trabalho atual da aplicação, que é o diretório de onde o comando Node.js foi iniciado.
  • Uso: Usado quando você precisa trabalhar com caminhos relativos ao local de execução da aplicação.

Exemplo comparativo:

console.log('__dirname:', __dirname);  // Retorna o caminho do diretório onde este script está localizado
console.log('process.cwd():', process.cwd());  // Retorna o diretório de trabalho atual de onde o script foi executado
  • Contexto: __dirname é mais específico para o arquivo em execução, enquanto process.cwd() é mais geral para a aplicação inteira. Dependendo do que você está fazendo, pode ser importante escolher entre um ou outro.

Mudando o Diretório de Trabalho

Embora o process.cwd() retorne o diretório de trabalho atual, ele não permite alterá-lo diretamente. No entanto, você pode usar o método process.chdir() para mudar o diretório de trabalho durante a execução de um programa Node.js.

Exemplo:

// Muda o diretório de trabalho para o diretório 'user/documents'
process.chdir('/user/documents');

// Exibe o novo diretório de trabalho
console.log('Novo diretório de trabalho:', process.cwd());
  • Flexibilidade: Usar process.chdir() permite que seu programa mude seu contexto de trabalho, o que pode ser útil em scripts que precisam navegar por diferentes diretórios para executar tarefas.
  • Cuidado: Alterar o diretório de trabalho pode causar confusão, especialmente se outros módulos ou funções do seu código dependem do diretório original.

Conclusão

O método process.cwd() é uma ferramenta essencial para gerenciar o contexto de trabalho da sua aplicação Node.js. Ele permite que você saiba exatamente de onde a aplicação está sendo executada, o que é crucial ao lidar com caminhos relativos e manipulação de arquivos. Ao entender as diferenças entre process.cwd() e __dirname, e como usar process.chdir() para alterar o diretório de trabalho, você pode escrever código mais robusto e flexível para lidar com diferentes cenários de execução.

# Manipulação de Caminhos: path Module

O módulo path do Node.js é utilizado para trabalhar com caminhos de arquivos e diretórios de forma segura e eficiente. Ele oferece uma variedade de métodos para manipular e resolver caminhos de arquivos, garantindo que você possa construir, normalizar e manipular caminhos de maneira consistente, independentemente do sistema operacional.

Construção de Caminhos (path.join)

path.join é utilizado para unir vários segmentos de caminho em um único caminho completo. Ele garante que os separadores corretos de diretório sejam usados, dependendo do sistema operacional.

Exemplo:

const path = require('path');

const directory = 'user';
const subdirectory = 'documents';
const filename = 'file.txt';

const fullPath = path.join(directory, subdirectory, filename);
console.log('Caminho completo:', fullPath);
  • Plataforma Cruzada: path.join garante que o caminho gerado seja válido em qualquer sistema operacional, seja Windows (que usa \) ou Unix (que usa /).
  • Facilidade de Uso: Ao invés de concatenar strings manualmente, o que pode gerar erros, path.join cuida dos detalhes para você.

Resolução de Caminhos Absolutos (path.resolve)

path.resolve resolve uma sequência de caminhos ou segmentos de caminho em um caminho absoluto. A resolução é feita da direita para a esquerda, então é possível "saltar" entre diretórios de forma eficiente.

Exemplo:

const path = require('path');

const fullPath = path.resolve('user', 'documents', 'file.txt');
console.log('Caminho absoluto:', fullPath);
  • Caminho Absoluto: path.resolve começa a resolução a partir do diretório atual (ou de um caminho absoluto fornecido) e vai até a raiz do sistema, garantindo que o resultado seja sempre um caminho absoluto.
  • Controle Total: Você pode misturar caminhos relativos e absolutos para gerar um caminho final completamente resolvido.

Extrair o Nome do Arquivo (path.basename)

path.basename retorna a última parte de um caminho, que normalmente é o nome do arquivo. É possível também remover a extensão do arquivo no resultado.

Exemplo:

const path = require('path');

const fullPath = '/user/documents/file.txt';

const basename = path.basename(fullPath);
console.log('Nome do arquivo:', basename);

const basenameWithoutExtension = path.basename(fullPath, '.txt');
console.log('Nome do arquivo sem extensão:', basenameWithoutExtension);
  • Nome do Arquivo: Este método é útil quando você precisa isolar o nome do arquivo de um caminho completo.
  • Remover Extensão: Ao passar a extensão como segundo argumento, você pode obter apenas o nome do arquivo sem a extensão, o que é útil em muitas situações.

Extrair o Diretório (path.dirname)

path.dirname retorna o caminho do diretório pai de um caminho especificado, removendo o nome do arquivo.

Exemplo:

const path = require('path');

const fullPath = '/user/documents/file.txt';

const dirname = path.dirname(fullPath);
console.log('Diretório pai:', dirname);
  • Diretório Pai: Útil para obter o caminho do diretório que contém o arquivo, especialmente quando você precisa navegar pelo sistema de arquivos em relação ao local de um arquivo específico.

Extrair a Extensão do Arquivo (path.extname)

path.extname retorna a extensão do arquivo de um caminho. A extensão é a parte do nome do arquivo a partir do último ponto até o final.

Exemplo:

const path = require('path');

const fullPath = '/user/documents/file.txt';

const extname = path.extname(fullPath);
console.log('Extensão do arquivo:', extname);
  • Identificação de Tipos de Arquivo: Saber a extensão de um arquivo pode ser crucial para determinar como ele deve ser tratado, por exemplo, ao decidir se deve ser lido como texto ou binário.

Normalização de Caminhos (path.normalize)

path.normalize ajusta um caminho para um formato padrão, removendo segmentos redundantes como .. ou . e corrigindo separadores de diretório.

Exemplo:

const path = require('path');

const messyPath = '/user/./documents/../documents/file.txt';

const normalizedPath = path.normalize(messyPath);
console.log('Caminho normalizado:', normalizedPath);
  • Simplificação: path.normalize é útil para garantir que o caminho seja o mais direto e simples possível, o que pode evitar erros em operações subsequentes.
  • Correção Automática: Ele corrige automaticamente quaisquer problemas comuns em caminhos, como múltiplos separadores consecutivos ou segmentos que não são necessários.

Conclusão

O módulo path do Node.js é uma ferramenta essencial para manipulação de caminhos de arquivos e diretórios de forma segura, consistente e independente de plataforma. Desde a construção e resolução de caminhos até a extração de partes específicas como nomes de arquivos ou extensões, path oferece uma solução robusta para lidar com caminhos de maneira eficiente. Com a prática, essas operações se tornarão naturais, permitindo que você manipule caminhos de arquivos e diretórios com confiança em qualquer ambiente Node.js.

# Manipulação de Arquivos: fs Module

O módulo fs (abreviação de "file system") do Node.js é um dos módulos centrais mais importantes, permitindo a interação com o sistema de arquivos de maneira direta e eficaz. Ele oferece uma ampla gama de funções para ler, escrever, abrir, fechar, renomear, excluir arquivos, entre outras operações. Abaixo, exploraremos as principais funcionalidades deste módulo, com exemplos completos e comentados para ajudar na compreensão.

Leitura de Arquivos (fs.readFile)

fs.readFile é usado para ler o conteúdo de um arquivo de forma assíncrona. Essa função carrega o conteúdo do arquivo completo na memória antes de disponibilizá-lo ao callback.

Exemplo:

const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('Erro ao ler o arquivo:', err);
        return;
    }
    console.log('Conteúdo do arquivo:', data);
});
  • Codificação: No exemplo, a codificação utf8 é especificada, garantindo que o conteúdo seja retornado como uma string legível. Se não for especificada, o conteúdo será retornado como um buffer.
  • Assíncrono: A função é assíncrona, então outras operações podem continuar sendo executadas enquanto o arquivo é lido.

Escrita de Arquivos (fs.writeFile)

fs.writeFile permite escrever dados em um arquivo de forma assíncrona. Se o arquivo já existir, seu conteúdo será substituído; caso contrário, o arquivo será criado.

Exemplo:

const fs = require('fs');

const data = 'Este é o conteúdo a ser escrito no arquivo.';

fs.writeFile('output.txt', data, 'utf8', (err) => {
    if (err) {
        console.error('Erro ao escrever no arquivo:', err);
        return;
    }
    console.log('Arquivo escrito com sucesso!');
});
  • Criação/Substituição: fs.writeFile cria um novo arquivo ou substitui o conteúdo de um arquivo existente.
  • Codificação: A codificação utf8 garante que o conteúdo seja escrito como uma string. Se omitir essa codificação, os dados serão escritos como bytes brutos.

Atualização de Arquivos (fs.appendFile)

fs.appendFile é usado para adicionar dados ao final de um arquivo existente, em vez de substituí-lo. Se o arquivo não existir, ele será criado.

Exemplo:

const fs = require('fs');

const additionalData = '\nEste texto será adicionado ao final do arquivo.';

fs.appendFile('output.txt', additionalData, 'utf8', (err) => {
    if (err) {
        console.error('Erro ao adicionar conteúdo ao arquivo:', err);
        return;
    }
    console.log('Conteúdo adicionado com sucesso!');
});
  • Preservação de Dados: Ao contrário de fs.writeFile, que substitui o conteúdo, fs.appendFile preserva o conteúdo existente, apenas adicionando novos dados ao final.

Renomear Arquivos (fs.rename)

fs.rename permite renomear um arquivo ou movê-lo para outro diretório. Se o novo nome incluir um caminho diferente, o arquivo será movido.

Exemplo:

const fs = require('fs');

fs.rename('output.txt', 'new_output.txt', (err) => {
    if (err) {
        console.error('Erro ao renomear o arquivo:', err);
        return;
    }
    console.log('Arquivo renomeado com sucesso!');
});
  • Renomear e Mover: fs.rename pode ser usado tanto para renomear quanto para mover arquivos, dependendo do caminho fornecido.

Exclusão de Arquivos (fs.unlink)

fs.unlink é usado para excluir um arquivo. Esta operação é permanente e remove o arquivo do sistema de arquivos.

Exemplo:

const fs = require('fs');

fs.unlink('new_output.txt', (err) => {
    if (err) {
        console.error('Erro ao excluir o arquivo:', err);
        return;
    }
    console.log('Arquivo excluído com sucesso!');
});
  • Permanente: A exclusão é permanente, portanto, certifique-se de que o arquivo realmente precisa ser removido antes de usar essa função.

Leitura de Diretórios (fs.readdir)

fs.readdir permite ler o conteúdo de um diretório, retornando uma lista de nomes de arquivos e subdiretórios.

Exemplo:

const fs = require('fs');

fs.readdir('.', (err, files) => {
    if (err) {
        console.error('Erro ao ler o diretório:', err);
        return;
    }
    console.log('Conteúdo do diretório:', files);
});
  • Exploração de Diretórios: Essa função é útil para listar arquivos e pastas em um diretório, permitindo operações como iteração ou manipulação dos itens.

Verificação de Existência de Arquivos (fs.existsSync)

Embora métodos assíncronos sejam preferidos na maioria dos casos, fs.existsSync é uma função síncrona que verifica se um arquivo ou diretório existe.

Exemplo:

const fs = require('fs');

if (fs.existsSync('output.txt')) {
    console.log('O arquivo existe.');
} else {
    console.log('O arquivo não existe.');
}
  • Síncrono: Como é uma função síncrona, ela bloqueia o processo até que a verificação seja concluída. Deve ser usada com cautela em ambientes de produção, onde o desempenho é crítico.

Conclusão

O módulo fs do Node.js é extremamente poderoso e versátil, oferecendo uma gama completa de operações para manipulação de arquivos e diretórios. A partir de funções básicas, como leitura e escrita, até operações mais avançadas, como renomear e excluir arquivos, fs é uma ferramenta essencial para qualquer desenvolvedor Node.js. Com a prática, essas operações tornam-se ferramentas poderosas no seu arsenal de desenvolvimento, permitindo uma manipulação eficaz e eficiente do sistema de arquivos.

# Pacotes de Código Aberto (Opensource Packages)

Quando se trata de manipulação de arquivos no Node.js, além dos módulos nativos como fs e path, a comunidade Node.js oferece pacotes de código aberto que expandem e simplificam muitas dessas operações. Esses pacotes adicionam funcionalidades adicionais, facilitam o trabalho com arquivos e diretórios, e permitem uma integração mais fluida com o sistema de arquivos. Abaixo estão alguns dos pacotes mais populares e suas funcionalidades detalhadas.

glob

O pacote glob permite buscar arquivos no sistema de arquivos que correspondam a padrões específicos (chamados de "globs"). Esses padrões são semelhantes à expansão de arquivos usada em shells Unix, permitindo, por exemplo, encontrar todos os arquivos com uma determinada extensão ou nome em uma estrutura de diretórios.

Exemplo:

const glob = require('glob');

// Encontra todos os arquivos .js no diretório atual
glob('*.js', (err, files) => {
    if (err) {
       console.error('Erro ao buscar arquivos:', err);
        return;
    }
    console.log('Arquivos encontrados:', files);
});
  • Flexibilidade: glob permite usar padrões poderosos para buscar arquivos em diretórios complexos. Por exemplo, o padrão **/*.js pode ser usado para encontrar todos os arquivos JavaScript em uma árvore de diretórios.
  • Praticidade: Este pacote é útil para tarefas como busca de arquivos para processamento em massa, linting ou compilação.

globby

globby é uma versão mais avançada e moderna do glob. Ele suporta Promises, o que facilita a escrita de código assíncrono e oferece mais funcionalidades, como a capacidade de lidar com múltiplos padrões ao mesmo tempo e manipular diretórios com mais flexibilidade.

Exemplo:

const globby = require('globby');

// Encontra todos os arquivos .js e .txt no diretório atual usando Promises
(async () => {
    const paths = await globby(['*.js', '*.txt']);
    console.log('Arquivos encontrados:', paths);
})();
  • Promessas e Async/Await: globby é completamente compatível com Promises, o que facilita a integração em fluxos de trabalho assíncronos. Isso torna o código mais limpo e gerenciável.
  • Várias Funções: Além de buscar arquivos, globby pode ser configurado para manipular links simbólicos, retornar diretórios ou até mesmo excluir arquivos após serem encontrados.

fs-extra

fs-extra é uma extensão do módulo nativo fs do Node.js. Ele adiciona funções adicionais que são frequentemente necessárias, mas que não estão incluídas no módulo fs padrão. Isso inclui operações comuns, como copiar, mover e remover arquivos e diretórios, com suporte nativo a Promises.

Exemplo:

const fs = require('fs-extra');

// Copia um arquivo de 'source.txt' para 'destination.txt'
fs.copy('source.txt', 'destination.txt')
  .then(() => {
      console.log('Arquivo copiado com sucesso!');
  })
  .catch(err => {
      console.error('Erro ao copiar o arquivo:', err);
  });
  • Facilidade de Uso: Com fs-extra, você pode realizar operações complexas de arquivos com menos código e maior clareza.
  • Funcionalidades Adicionais: fs-extra inclui métodos como ensureDir() para garantir que um diretório existe (criando-o se necessário), emptyDir() para esvaziar um diretório, entre outros. Isso simplifica muitas tarefas comuns que exigiriam várias etapas com fs.

chokidar

chokidar é um pacote poderoso para observar mudanças em arquivos e diretórios. Ele permite monitorar o sistema de arquivos em tempo real e executar ações quando certos eventos ocorrem, como a criação, modificação ou exclusão de arquivos.

Exemplo:

const chokidar = require('chokidar');

// Observa o diretório atual por mudanças
const watcher = chokidar.watch('.', {
    ignored: /(^|[\/\\])\../, // Ignora arquivos ocultos (começando com .)
    persistent: true
});

// Evento: arquivo adicionado
watcher.on('add', path => console.log(`Arquivo ${path} foi adicionado.`));

// Evento: arquivo alterado
watcher.on('change', path => console.log(`Arquivo ${path} foi modificado.`));

// Evento: arquivo removido
watcher.on('unlink', path => console.log(`Arquivo ${path} foi removido.`));
  • Monitoramento em Tempo Real: chokidar é ideal para aplicações que precisam reagir a mudanças no sistema de arquivos, como servidores de desenvolvimento que precisam reiniciar ou reconstruir arquivos ao detectar alterações.
  • Alto Desempenho: chokidar é otimizado para monitorar um grande número de arquivos sem sobrecarregar o sistema, utilizando internamente a API fs.watch() ou fsevents no macOS.

Conclusão

Esses pacotes de código aberto fornecem ferramentas essenciais e funcionalidades avançadas para trabalhar com arquivos e diretórios no Node.js. Desde a busca de arquivos com glob e globby, até a manipulação aprimorada de arquivos com fs-extra, e o monitoramento em tempo real com chokidar, essas ferramentas expandem o poder do Node.js, tornando-o ainda mais versátil e eficiente para manipulação de arquivos.

Você pode aprender mais sobre no artigo: MDN - Guia NodeJs

Aplicações de linha de comando

# Exiting and Exit Codes

Exiting a Node.js Application

Para sair de uma aplicação Node.js, você pode usar a função process.exit(). Este método encerra o processo atual com um código de saída opcional.

Exemplo:

console.log("Aplicação iniciada.");

// Encerra a aplicação com código de saída 0 (sucesso)
process.exit(0);

Exit Codes

Os códigos de saída são números que o processo retorna ao sistema operacional quando ele termina. O código 0 geralmente indica sucesso, enquanto qualquer outro código indica um erro.

Exemplo:

console.log("Aplicação com erro.");

process.exit(1); // Código 1 indica um erro

# Environment Variables

process.env

process.env é um objeto que contém todas as variáveis de ambiente do sistema onde a aplicação está sendo executada.

Exemplo:

// Acessa uma variável de ambiente chamada 'MY_VARIABLE'
const myVariable = process.env.MY_VARIABLE || 'valor padrão';
console.log(`O valor da variável MY_VARIABLE é: ${myVariable}`);

dotenv Package

O pacote dotenv permite carregar variáveis de ambiente de um arquivo .env para process.env.

Instalação:

npm install dotenv

Exemplo:

  1. Crie um arquivo .env:
MY_VARIABLE=valor_importado
  1. Utilize o dotenv no seu código:
require('dotenv').config();

const myVariable = process.env.MY_VARIABLE;
console.log(`O valor da variável MY_VARIABLE é: ${myVariable}`);

# Taking Input

process.stdin

process.stdin é um fluxo de leitura que permite ler a entrada do usuário via terminal.

Exemplo:

process.stdin.setEncoding('utf8');

console.log('Digite algo e pressione Enter:');

process.stdin.on('data', (input) => {
    console.log(`Você digitou: ${input.trim()}`);
    process.exit();
});

Inquirer Package

O pacote inquirer facilita a criação de prompts interativos.

Instalação:

npm install inquirer

Exemplo:

const inquirer = require('inquirer');

inquirer.prompt([
    {
        type: 'input',
        name: 'name',
        message: 'Qual é o seu nome?'
    }
]).then(answers => {
    console.log(`Olá, ${answers.name}!`);
});

prompts Package

O pacote prompts também é utilizado para criar prompts interativos.

####Instalação:

npm install prompts

Exemplo:

const prompts = require('prompts');

(async () => {
    const response = await prompts({
        type: 'text',
        name: 'name',
        message: 'Qual é o seu nome?'
    });

    console.log(`Olá, ${response.name}!`);
})();

# Printing Output

stdout / stderr

stdout e stderr são fluxos de saída padrão e de erro padrão, respectivamente.

Exemplo:

// Saída padrão
process.stdout.write('Mensagem para stdout\n');

// Saída de erro
process.stderr.write('Mensagem para stderr\n');

chalk Package

O pacote chalk permite colorir o texto no terminal.

Instalação:

npm install chalk

Exemplo:

const chalk = require('chalk');

console.log(chalk.green('Texto em verde'));
console.log(chalk.red.bold('Texto em vermelho e negrito'));

figlet Package

O pacote figlet gera texto em estilo de arte ASCII.

Instalação:

npm install figlet

Exemplo:

const figlet = require('figlet');

figlet('Olá Mundo!', (err, data) => {
    if (err) {
        console.log('Algo deu errado...');
        console.dir(err);
        return;
    }
    console.log(data);
});

cli-progress Package

O pacote cli-progress é utilizado para criar barras de progresso no terminal.

Instalação:

npm install cli-progress

Exemplo:

const cliProgress = require('cli-progress');

// Cria uma nova barra de progresso
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);

// Inicia a barra de progresso
bar.start(100, 0);

// Atualiza a barra de progresso
for (let i = 0; i <= 100; i++) {
    bar.update(i);
    // Simula um atraso
   require('deasync').sleep(50);
}

// Finaliza a barra de progresso
bar.stop();

# Command Line Args

process.argv

process.argv é um array que contém os argumentos da linha de comando passados para o processo Node.js.

Exemplo:

// Acessa argumentos da linha de comando
const args = process.argv.slice(2);

console.log('Argumentos da linha de comando:');
args.forEach((arg, index) => {
    console.log(`Argumento ${index + 1}: ${arg}`);
});

commander

O pacote commander facilita o gerenciamento de argumentos e opções da linha de comando.

Instalação:

npm install commander

Exemplo:

const { program } = require('commander');

program
    .version('1.0.0')
    .description('Descrição da minha aplicação CLI')
    .option('-n, --name <type>', 'Nome do usuário')
    .parse(process.argv);

const options = program.opts();
if (options.name) {
    console.log(`Olá, ${options.name}!`);
} else {
    console.log('Olá, mundo!');
}
Você pode aprender mais sobre nesse repositorio: awesome-nodejs

Introdução ao Desenvolvimento de APIs

APIs (Application Programming Interfaces) são fundamentais no desenvolvimento moderno de software. Elas permitem a comunicação entre diferentes sistemas e aplicações, facilitando a troca de dados e funcionalidades. O desenvolvimento e consumo de APIs envolvem a criação de endpoints que outros serviços podem usar para acessar recursos ou funcionalidades específicas e a realização de requisições a essas APIs para consumir dados ou executar operações.

Vamos explorar as seguintes categorias:

  1. Frameworks para Construção de APIs: Ferramentas que facilitam o desenvolvimento de APIs robustas e escaláveis.
  2. Realizando Chamadas de API: Métodos para interagir com APIs e consumir seus dados.
  3. Autenticação em APIs: Mecanismos para garantir a segurança e controle de acesso nas APIs.

# Frameworks para Construção de APIs

1. Express.js

O Express.js é um framework minimalista e flexível para Node.js que fornece um conjunto robusto de recursos para construir aplicativos web e APIs. Ele é amplamente utilizado devido à sua simplicidade e extensibilidade.

Exemplo:

const express = require('express');
const app = express();
const port = 3000;

// Endpoint básico
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// Iniciando o servidor
app.listen(port, () => {
  console.log(`Servidor rodando em http://localhost:${port}`);
});

Este código cria um servidor básico com o Express.js que responde com "Hello World!" ao acessar a raiz (/). O método app.get() define uma rota para o endpoint /, enquanto app.listen() inicia o servidor na porta especificada.

2. Fastify

Fastify é um framework web altamente focado em performance e baixo overhead. Ele oferece uma API similar ao Express.js, mas com um foco maior em velocidade e eficiência.

Exemplo:

const fastify = require('fastify')({ logger: true });
const port = 3000;

// Endpoint básico
fastify.get('/', async (request, reply) => {
  return { message: 'Hello Fastify!' };
});

// Iniciando o servidor
fastify.listen(port, (err, address) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`Servidor rodando em ${address}`);
});

Fastify cria um servidor com um endpoint que retorna uma resposta JSON com a mensagem "Hello Fastify!". O servidor é inicializado na porta especificada, e a logica para lidar com erros de inicialização também é implementada.

3. NestJS

NestJS é um framework para construir aplicações Node.js escaláveis e eficientes. Ele utiliza TypeScript por padrão e é inspirado por arquiteturas como a do Angular, oferecendo um desenvolvimento estruturado e modular.

Exemplo:

import { Module } from '@nestjs/common';
import { Controller, Get } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';

@Controller()
class AppController {
  @Get()
  getHello(): string {
    return 'Hello NestJS!';
  }
}

@Module({
  controllers: [AppController],
})
class AppModule {}

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

Este exemplo configura uma aplicação básica em NestJS com um único controlador (AppController) que responde a requisições GET na raiz com a mensagem "Hello NestJS!". O método bootstrap() inicializa o aplicativo.

4. Hono

Hono é um micro framework para construir aplicações web ultraleves e rápidas, com foco em simplicidade e desempenho.

Exemplo:

import { Hono } from 'hono';

const app = new Hono();

// Endpoint básico
app.get('/', (c) => c.text('Hello Hono!'));

// Iniciando o servidor
app.listen({ port: 3000 });

Este código cria um servidor básico usando Hono que responde com "Hello Hono!" ao acessar a raiz. O método app.listen() inicia o servidor na porta especificada.

# Realizando Chamadas de API

1. HTTP Module

O módulo http do Node.js é utilizado para criar servidores HTTP e realizar requisições HTTP. Ele é uma ferramenta fundamental e de baixo nível para manipulação de protocolos HTTP.

Exemplo:

const http = require('http');

// Realizando uma requisição GET
http.get('http://example.com', (res) => {
  let data = '';

  // A medida que os dados chegam, eles são acumulados
  res.on('data', (chunk) => {
    data += chunk;
  });

  // Quando a resposta completa é recebida
  res.on('end', () => {
    console.log(data);
  });
}).on('error', (err) => {
  console.error(`Erro: ${err.message}`);
});

Este exemplo mostra como realizar uma requisição GET usando o módulo http. Os dados da resposta são recebidos em partes (chunks) e acumulados até que a resposta completa seja processada.

2. Axios

Axios é uma biblioteca popular para realizar requisições HTTP de forma simples e eficiente. Ela suporta promessas e permite uma fácil integração com APIs.

Exemplo:

const axios = require('axios');

// Realizando uma requisição GET
axios.get('http://example.com')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('Erro:', error);
  });

Neste exemplo, o Axios realiza uma requisição GET para o http://example.com e lida com a resposta e possíveis erros utilizando promessas (then e catch).

3. Ky

Ky é uma biblioteca para realizar requisições HTTP focada em simplicidade e leveza, baseada no fetch. Ela é otimizada para ser utilizada em aplicações frontend.

Exemplo:

import ky from 'ky';

(async () => {
  try {
    const json = await ky.get('http://example.com').json();
    console.log(json);
  } catch (error) {
    console.error('Erro:', error);
  }
})();

Este exemplo utiliza o Ky para realizar uma requisição GET e automaticamente tratar a resposta como JSON. A função assíncrona (async) é usada para esperar a resposta e lidar com possíveis erros.

4. Fetch API

A Fetch API é uma interface moderna para realizar requisições HTTP. Ela é nativa em navegadores modernos e também pode ser utilizada no Node.js com bibliotecas adicionais.

Exemplo:

fetch('http://example.com')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Erro:', error));

O exemplo utiliza a Fetch API para realizar uma requisição GET e processar a resposta como JSON. Promessas são utilizadas para lidar com a resposta e erros.

5. Got Package

Got é uma biblioteca HTTP moderna e robusta para Node.js, que oferece suporte a recursos avançados como interceptadores de requisições, cancelamento e recuperação de erros.

Exemplo:

const got = require('got');

(async () => {
  try {
    const response = await got('http://example.com');
    console.log(response.body);
  } catch (error) {
    console.error('Erro:', error.response.body);
  }
})();

Got realiza uma requisição GET e a resposta é manipulada utilizando uma função assíncrona. O corpo da resposta é exibido, e erros são tratados de maneira específica.

# Autenticação em APIs

1. JsonWebToken (JWT)

JWT é uma maneira compacta e segura de transmitir informações entre as partes como um objeto JSON. É amplamente utilizado para autenticação em APIs.

Exemplo:

const jwt = require('jsonwebtoken');

const token = jwt.sign({ userId: 123 }, 'secreto', { expiresIn: '1h' });
console.log(token);

// Verificando o token
try {
  const decoded = jwt.verify(token, 'secreto');
  console.log(decoded);
} catch (err) {
  console.error('Token inválido', err);
}

Este exemplo cria um token JWT assinado com uma chave secreta e com um tempo de expiração de uma hora. O token gerado é então verificado, e se for válido, seus dados são exibidos.

2. Passport.js

Passport.js é um middleware de autenticação para Node.js, que oferece suporte a diversos mecanismos de autenticação, como JWT, OAuth, e login tradicional.

Exemplo:

const express = require('express');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

passport.use(new LocalStrategy(
  function(username, password, done) {
    // Aqui você realizaria a verificação do usuário
    if (username === 'user' && password === 'pass') {
      return done(null, { id: 1, name: 'user' });
    } else {
      return done(null, false, { message: 'Credenciais incorretas' });
    }
  }
));

const app = express();
app.use(passport.initialize());

// Endpoint de login
app.post('/login', 
  passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })
);

app.listen(3000, () => console.log('Servidor rodando na porta 3000'));

Neste exemplo, Passport.js é configurado para usar a estratégia LocalStrategy para autenticar usuários com um nome de usuário e senha. Se as credenciais estiverem corretas, o usuário é autenticado com sucesso.

Conclusão

Compreender como construir e consumir APIs, junto com a implementação de autenticação, é essencial para o desenvolvimento de aplicações modernas. Ferramentas como Express.js, Fastify, NestJS, e Hono facilitam a construção de APIs, enquanto Axios, Fetch, e Got ajudam no consumo dessas APIs. Para segurança, JWT e Passport.js são poderosos aliados na implementação de autenticação em APIs.

Você pode aprender mais sobre nesse artigo: MDN - API's

About

Bem-vindo ao repositório Estudando Node.js, este repositório foi criado para fornecer um ponto de partida abrangente utilizando roadmap para o estudo e prática de Node.js.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published