Novidades do Node.js (versão 18)

Novidades do Node.js (versão 18)

Introdução

Quando desenvolvemos um programa, seja para uso pessoal ou para vários usuários, é sempre importante mantermos as dependências desse projeto atualizadas para que ele acompanhe as mudanças da tecnologia e não se torne um programa fora de uso.

Isso parece se tornar cada vez mais difícil, por exemplo, quando precisamos usar bibliotecas externas para testar nossos códigos ou fazer requisições HTTP. Fora a sensação de estar “usando um canhão para matar uma barata” ao ter que instalar uma biblioteca externa para fazer coisas simples.

E quando falamos de Node.JS logo pensamos que poderíamos ter módulos nativos para lidar com isso. E acredito que a equipe do Node.js também pense assim, pois, na última semana, o Node.js ganhou uma nova versão LTS, a versão 18 que desde o seu lançamento trouxe funcionalidades incríveis que podem tornar nosso código menos dependente de bibliotecas externas e a escrita cada vez mais simples!

Mas, desde o anúncio dessa versão até ela se tornar a versão LTS, você sabe o que ela trouxe de novo?

Então vem comigo.

Entendendo as versões do Node.js

Entender as versões do Node.js é o ponto de partida para sabermos quando teremos novas funcionalidades e prevermos futuras manutenções em nosso código (seja por conta de uma versão que não receberá mais atualizações, seja por uma otimização de uma funcionalidade em uma nova versão). Além de saber se uma versão é estável e seu tempo de manutenção, conceitos vitais para manter o seu projeto.

O Node.js possui um calendário fiel de releases (em português, lançamentos) das versões major, atualizações de versão com potencial para “quebrar” códigos que utilizem as versões anteriores. Se temos, por exemplo, a versão 14.17.5, estamos falando de atualizações acerca do primeiro número da sequência, o 14.

E por qual motivo digo que possui um calendário fiel? Isso se dá por sempre termos lançamento de versões major a cada seis meses (em outubro e abril). O que é excelente para devs que usam o Node.js em suas aplicações se adequarem às novas versões.

A tabela adaptada da documentação do Node.js nos informa alguns atributos do cronograma de lançamento das versões do Node.js, em que temos o “apelido” da versão (coluna codename, ou codinome em português), a data de lançamento (coluna Initial Release) e fim da versão (coluna End-of-life), mas vamos focar nas colunas Release e Status, que nos trazem muita informação sobre como funciona o cronograma de versões do Node.js.

ReleaseStatusCodenameInitial ReleaseActive LTS StartMaintenance StartEnd-of-life
14.xMaintenanceFermium2020-04-212020-10-272021-10-192023-04-30
16.xMaintenanceGallium2021-04-202021-10-262022-10-182023-09-11
18.xCurrent-2022-04-192022-10-252023-10-182025-04-30
19.xCurrent-2022-10-18-2023-04-012023-06-01
20.xPending-2023-04-182023-10-242024-10-222026-04-30

Release

Agora vamos “destrinchar” alguns termos da tabela apresentada anteriormente. O primeiro deles é referente à coluna “Release”, que se refere ao número da versão major do Node.js. O fato de um release ser par ou ímpar, diz muito sobre o seu comportamento:

Release
14.x
16.x
18.x
19.x
20.x

Versões ímpares são lançadas em outubro e possuem um tempo de vida bem curto (cerca de 6 meses). Elas são criadas para testar funcionalidades e aplicar mudanças mais drásticas (isso quer dizer que não são versões estáveis para produção). Podemos compará-las com as versões beta, onde são testadas várias funcionalidades, sendo que o conjunto de funcionalidades aprovado se torna uma versão par.

Versões pares: como você deve estar imaginando, são as versões que possuem vida longa (cerca de 3 anos), lançadas em abril. As versões pares recebem continuamente atualizações de bugs, segurança, erros críticos e outras. Por esses motivos (atualizações constantes e longa vida), essas versões são consideradas estáveis.

Porém, como dito anteriormente, a cada seis meses é lançada uma nova versão major e, com isso, o status das versões se altera e isso traz um novo significado.

Status

Quando uma versão é lançada, ela recebe o status Current (“Atual”, em português), ou seja, é considerada a última versão do Node.js, permanecendo com esse status durante seis meses, até o lançamento de uma nova versão. Isso vale tanto para uma versão ímpar (como a v19, que permanece como current de 10/2022 até 04/2023) quanto a versão par (como a v20, que permanece como current de 04/2023 até 10/2023), conforme podemos ver na tabela abaixo:

ReleaseStatusInitial ReleaseActive LTS StartMaintenance StartEnd-of-life
19.xCurrent2022-10-18-2023-04-012023-06-01
20.xPending2023-04-182023-10-242024-10-222026-04-30

Em uma versão ímpar, após seis meses do seu lançamento (Initial Release), a versão recebe o status ‘Maintenance’ (Manutenção, em português), recebendo assim atualizações por 2 meses até chegar o fim de sua vida (End-of-life), quando receberá o status unstable e se tornará uma versão descontinuada (passa a não receber mais atualizações).

Em uma versão par, como mostrado na tabela acima após os 6 meses de lançamento, o status mudará para active (ativo, em português) que é quando uma versão terá um suporte de manutenção durante um longo prazo; ou seja, as famosas versões LTS que você encontra ao baixar o Node.js:

Tela inicial do site oficial do Node.js. Na parte superior da tela encontra-se uma imagem com o símbolo do Node.js ao centro da imagem e escrito abaixo do símbolo: “Node.js 19 is now live!”;  fundo há uma transição de gradiente da cor azul para verde, com formas triangulares à esquerda e à direita da imagem. Abaixo da imagem temos  um fundo preto e a seguinte frase na cor branca:“Download for Windows (x64)”. Abaixo da frase temos dois botões de fundo verde e frases na cor branca, um escrito “16.18.0 LTS Recommended for Most Users” e outro botão com a frase “19.0.0 Current Latest Features”. Abaixo de cada botão temos os seguintes links na cor verde:“Other Downloads”, “Changelo”, “API Docs” separados pelo símbolo.

As versões LTS (Long-term support ou suporte a longo prazo, em português) permanecem com esse status por mais 1 ano e ainda possuem 18 meses de manutenção, o que nos traz uma garantia de cerca de 3 anos de atualizações para essa versão desde o seu lançamento.

Ao entrar em manutenção, a versão receberá apenas atualizações críticas de desempenho, bugs ou segurança.

Esse status, diferente das versões ímpares, dura 18 meses. A versão continua sendo considerada estável e pronta para a produção. Porém, você pode receber alguns avisos que em breve ela chegará ao seu último status, de unstable, que significa que a versão chegou ao fim de sua vida e não receberá mais atualizações.

Devido a esse cronograma super preciso, teremos sempre 3 versões em manutenção e 1 versão como LTS. Em resumo, o status atual das versões é:

  • Desde o lançamento da versão 18, a versão 10 chegou ao fim de sua vida. Daí você já sabe que qualquer versão anterior a 10 também está descontinuada;
  • Na semana em que escrevemos este artigo (dia 25/10/2022), a versão 18 se tornou a versão LTS e assim permanecerá até entrar em manutenção daqui a 1 ano;
  • A versão 19 continuará como current até 04/2023, onde será lançada a versão 20 do Node.js.

Vale se atentar que com o lançamento dessa nova versão em abril de 2023, a versão 12 do Node chegará ao fim de sua vida e a própria documentação do Node.js recomenda que você atualize seus projetos para uma versão LTS.

Mas vamos falar então das novidades da versão 18? Bora lá!

Banner da Escola de Programação: Matricula-se na escola de Programação. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

O que vem de novo

fetch (experimental)

Na versão 17 do Node.js foi anunciado que a API fetch que já existia nos navegadores chegou ao Node.js, como um módulo experimental, e com isso tivemos uma série de mudanças na forma de fazer requisições HTTP, e você pode conferir mais sobre essa revolução nesse artigo.

A novidade na versão 18 é que, diferente da versão 17, na qual era necessária uma flag para ativar essa funcionalidade, ela agora está disponível no escopo global por padrão!

Veja como está simples fazer uma requisição:

const res = await fetch('https://nodejs.org/api/documentation.json');
if (res.ok) {
  const data = await res.json();
  console.log(data);
}

Agora você pode realizar requisições de uma forma simples, sem se preocupar com possíveis alterações em bibliotecas externas e consequentemente com comprometimento de suas aplicações.

Caso o Node.js tenha reclamado sobre o await, inclua no seu arquivo package.json a linha "type": "module".

Além do fetch, as variáveis FormData, Headers, Request e Response se tornaram globais.

Outras APIs globais

  • Um novo tipo de Buffer, o Blob, foi adicionado às APIs globais;
  • Pensando em comunicação assíncrona, o BroadcastChannel também está exposto globalmente;

Ambas APIs não são mais experimentais.

  • Foi adicionada uma API experimental do Web Streams permitindo acesso a streams recebidos pela rede para serem processados de acordo com a necessidade.

Test runner nativo

Talvez a novidade mais legal da versão 18 é a possibilidade de escrever testes sem a necessidade de uma biblioteca externa.

Graças ao módulo node:test (ainda experimental) teremos um test runner do próprio Node.js!

Gif animado do lutador profissional John Cena, um homem branco, cabelo raspado, sem camisa e com braçadeira laranja no braço esquerdo. Ele está com os olhos bem abertos, parado em um ring de luta e boquiaberto, demonstrando estar surpreso. O fundo desfocado apresenta algumas pessoas em um evento de luta.

Talvez tenham surgido algumas dúvidas como: “Mas já não conseguimos fazer testes com o módulo assert?”, “já não existem bibliotecas como o jest, mocha e outros para testes?” e a principal “o que faz um test runner?”

Calma! Vamos esclarecer aqui cada uma dessas dúvidas.

Com o módulo nativo assert do Node.js nós conseguimos avaliar se dada sentença está conforme o esperado ou não:

import  assert from "assert";

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

assert.ok(soma(1,2) === 3, "soma conforme o esperado") 
assert.ok(soma(1,2) === 4, "1 + 2 não é 4, um AssertionError foi lançado")

Do exemplo acima, temos que o primeiro caso o método assert.ok() retorna o valor true e nada acontece. No segundo caso, o método assert.ok() retorna o valor false e a pessoa recebe um AssertionError.

Caso queira saber o que é o AssertionError e demais erros que enfrentamos no Node.js, recomendo a leitura deste artigo.

Como você pode ver no código acima, é um simples teste que criei em um único arquivo. Mas o que acontece quando temos um número muito grande de arquivos? Como podemos automatizar e organizar a execução dos nossos testes?

Daí que temos bibliotecas externas famosas com Jest, Mocha, dentre outras que organizam os casos de teste e os executam para nós. E é exatamente esse o papel de um test runner, gerenciar a execução de diferentes testes de forma eficiente e organizada.

Com essa nova atualização do Node.js, o módulo node:test implementa um test runner nativo, diminuindo assim a barreira para devs usarem testes e tendo uma maior padronização, visto que cada biblioteca tem sua sintaxe, sua forma de comunicar com APIs e seus métodos.

Lembrando que é uma funcionalidade experimental, então ainda é cedo para afirmar que essa funcionalidade irá substituir tais bibliotecas de terceiros que existem há anos! Cena para os próximos capítulos as próximas atualizações do Node.js.

Com o módulo node:test podemos criar testes desta forma já familiar para quem usa frameworks de teste como o Jest:

import test from "node:test";
import assert from "assert";

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

test("1 + 1 = 2", () => {
 const resultado = soma(1, 1);
 const esperado = 2;
 assert.strictEqual(resultado, esperado);
});

Você pode executar este código direto com o comando node <nome do arquivo.js> no terminal; o test runner também pode ser chamado com a flag node --test caso existam no diretório arquivos nomeados de acordo com a convenção .test. (por exemplo, index.test.js).

No caso acima, a saída no terminal será algo parecido com isso:

<user@computador>$ node --test
TAP version 13
# Subtest: /home/<nome do diretório>/index.test.js
ok 1 - /home/<nome do diretório>/index.test.js
  ---
  duration_ms: 46.794508
  ...
1..1
# tests 1
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 51.208806

Este output do relatório de testes segue o protocolo TAP (Test Anything Protocol), uma forma mais “limpa” e padronizada de exibir relatórios de testes e bem diferente do que se costuma ver, por exemplo, no Jest. Você pode ver com mais detalhes a que se propõe o TAP no link indicado acima.

Esse módulo nos traz a possibilidade de fazer testes síncronos, assíncronos, subtestes e várias outras facilidades que você pode conferir na própria documentação, que está bem detalhada.

E algo que me chamou a atenção é que, diferente de módulos nativos como assert e fs, em que o uso do prefixo node é optativo, no módulo de teste do Node.js é obrigatória a importação do módulo com o prefixo node:

import test from 'node:test';

Isso pode ser o início do que chamamos de prefix-only core modules, onde os módulos nativos obrigatoriamente devem ter um prefixo, no caso, node:.

Isso é algo que divide a opinião na comunidade, pois alguns enxergam positiva a possibilidade de criar módulos com o mesmo nome, visto que os módulos com o prefixo terão prioridades sobre os userland modules, módulos criados pela comunidade. Outros enxergam como possíveis armadilhas, visto que, por um descuido do usuário, ele pode fazer a importação de um módulo malicioso que possui o mesmo nome que um módulo nativo.

Atualização da V8 para a versão 10.1

V8 é o interpretador JavaScript, também chamado de máquina virtual JavaScript e é através dele que conseguimos executar códigos JavaScript no Node.js. De forma bem simplista, é como se o Node.js fosse um carro e o motor para esse carro funcionar fosse a V8. A nova versão da V8 traz a possibilidade de usarmos os seguintes recursos:

  • O método findLast(), que percorre um array na ordem inversa e retorna o valor do primeiro elemento que satisfaz a função de teste fornecida. Se nenhum elemento satisfizer a função de teste, undefined é retornado;

No exemplo abaixo estamos usando o findLast() para buscar o último número para do vetor arrayDeNumeros.

const arrayDeNumeros = [1,2,3,4,5,6,7];

const ultimoElementoPar = arrayDeNumeros.findLast((element) => element % 2 === 0);

console.log(ultimoElementoPar);
//Teremos como saída o valor 6
  • Semelhante à funcionalidade anterior, temos o findLastIndex() que irá retornar o índice do elemento de um vetor que atenda a função de teste fornecida. Se nenhum elemento satisfizer a função de teste, undefined é retornado.

No exemplo abaixo, vamos percorrer uma lista de frutas e buscar o índice do vetor onde temos a última ocorrência da fruta banana. Perceba que banana está na terceira posição (índice igual a 2) e na sétima posição (índice igual a 6). Teremos como o valor 6, sendo o índice da última aparição da fruta “banana” no array.

const arrayDeFrutas = ["uva", "maçã", "banana", "abacaxi", "abacate", "morango", "banana", "melão"];

const fruta = arrayDeFrutas.findLastIndex((element) => element === "banana");

console.log(fruta);
  • Melhorias na API Intl.Locale;
  • Foi adicionada a função Intl.supportedValuesOf(code) que retorna a matriz de identificadores suportados na v8 para as APIs internacionais e permite que se descubra facilmente qual valor é suportado pela implementação;
  • Desempenho aprimorado para inicialização de propriedades de classe e métodos privados (a inicialização deles agora é tão rápida quanto os armazenamentos de propriedades comuns).

Conclusão

Neste artigo vimos as principais novidades do Node.js versão 18 desde o seu lançamento. Uma versão cheia de boas novas como test runner nativo, fetch global, novos métodos para trabalharmos com arrays e diversas melhorias relacionadas ao desempenho da V8. Existem alguns outros ajustes feitos que você pode conferir nos releases do Node.js.

Aqui na Alura, você pode dar o seu primeiro mergulho em programação back-end com a formação JavaScript para back-end, onde você verá desde as partes fundamentais de qualquer linguagem de programação até o consumo de APIs e criação de bibliotecas com Node.js. E para mergulhos em regiões mais profundas com o Node.js, confira a formação Node.js com Express para aprender a construir backends para sites escaláveis.

Emerson Laranja
Emerson Laranja

Sou monitor da Alura e granduando em engenharia de computação (Ufes).Minha dedicação está centrada no desenvolvimento de conteúdos voltados para a área de backend, com enfoque especial em JavaScript e TypeScript. Estou comprometido em proporcionar uma experiência de aprendizado envolvente e enriquecedora para todos os alunos, contribuindo assim para o sucesso de suas jornadas no universo do desenvolvimento web.

Veja outros artigos sobre Programação