Como escrever aplicativos Node.js em Typescript #iniciante

Tempo de leitura: 8 minutos

Desde que descobri o TypeScript pela primeira vez, eu o uso para todos os meus aplicativos JavaScript. Há tantas vantagens em usar o TypeScript que você precisará de um argumento bem convincente para não usar, se tratando claro de projetos grandes.

Neste artigo, mostrarei como configurar um ambiente de desenvolvimento simples para codar um aplicativo Node.js em TypeScript.

Deixe-me começar dizendo que provavelmente existem milhares de maneiras diferentes de criar aplicativos Node.js em TypeScript. Eu só vou compartilhar o jeito que eu mais gosto até o momento de fazer isso.

Package.json

Como eu disse antes, existem várias maneiras diferentes de fazer isso. Eu gosto de usar o webpack para os meus projetos em TypeScript. Com isso a primeira coisa a ser fazer é criar o package.json em nosso projeto.

Você pode gerar um package.json usando o comando npm init ou copiar o código abaixo e alterá-lo.

// package.json
{
  "name": "node-typescript",
  "version": "0.0.1",
  "author": "Freek Mencke",
  "homepage": "https://blog.uebile.com",
  "license": "MIT",
  "scripts": {},
  "dependencies": {},
  "devDependencies": {}
}

Vamos começar com uma configuração simples de webpack para nosso projeto Node.js. Depois de concluir a configuração básica, adicionarei o TypeScript.

A aplicação

Como mencionado anteriormente, começaremos com um aplicativo JavaScript e depois converteremos isso em TypeScript.

Vamos criar um diretório src/ com um arquivo main.js e information-logger.js com algumas funcionalidades do Node.js:


// src/information-logger.js const os = require('os'); const { name, version} = require('../package.json'); module.exports = { logApplicationInformation: () => console.log({ application: { name, version, }, }), logSystemInformation: () => console.log({ system: { platform: process.platform, cpus: os.cpus().length, }, }), }; // src/main.js const informationLogger = require('./information-logger'); informationLogger.logApplicationInformation(); informationLogger.logSystemInformation();

Este script básico registrará algumas informações do aplicativo e do sistema no console. Eu posso ver a seguinte saída depois de executar o arquivo main.js.

{ application: { name: 'node-typescript', version: '0.0.1' } }
{ system: { platform: 'macos', cpus: 8 } }

Webpack

A primeira coisa que precisamos fazer antes de podermos usar o webpack é instalar as dependências necessárias. Não se esqueça de usar o sinalizador -D, já que eles são devDependencies.

npm i -D webpack webpack-cli

Você deve ter notado que eu não instalei o webpack-dev-server. Isso porque estamos criando um aplicativo Node.js. Mais tarde, vou usar o nodemon, que serve para o mesmo propósito.

webpack.config.js

O próximo passo é criar um arquivo webpack.config.js onde nós informaremos ao webpack exatamente como ele deve processar nosso código.

// webpack.config.js
'use strict';
module.exports = (env = {}) => {
  const config = {
    entry: ['./src/main.js'],
    mode: env.development ? 'development' : 'production',
    target: 'node',
    devtool: env.development ? 'cheap-eval-source-map' : false,
  };
return config;
};

Como você pode ver, não é necessário muito para começar com o webpack. As duas únicas opções necessárias são entrada e destino. Declaramos nosso ponto de entrada usando seus determinados campos e informamos ao webpack que estamos trabalhando no Node.js com o campo de destino.

Podemos usar o campo mode para dizer ao webpack que ele deve se concentrar na velocidade de compilação (desenvolvimento) ou ofuscação e minimização (produção). Para ajudar com a depuração, usamos o campo devtool para indicar que queremos mapas de origem, se rodarmos em desenvolvimento. Dessa forma, se houver um erro, poderemos encontrar facilmente o local em que ocorreu em nosso código.

Para usar o webpack, vamos criar alguns comandos npm:

// package.json
...  
  "scripts": {
    "start": "webpack --progress --env.development",
    "start:prod": "webpack --progress"
  },
...

Agora podemos construir nosso aplicativo executando qualquer comando. Ele irá criar um diretório dist/ com o arquivo de saída main.js dentro. Poderíamos especificar um nome diferente usando a configuração de saída no webpack.config.js.

Nosso projeto deve ficar assim agora:

dist/
  main.js
node_modules/
src/
  information_logger.js
  main.js
package-lock.json
package.json
webpack.config.js

nodemon

Você deve ter notado que depois de executar um comando start, o webpack pára após construir o aplicativo. Não persiste às alterações de arquivo que fizemos. E como estamos trabalhando com o Node.js, não podemos usar o webpack-dev-server.

Felizmente podemos usar o nodemon para resolver este problema. É uma ferramenta criada especificamente para essa finalidade: reiniciar aplicativos do Node.js durante o desenvolvimento.

Vamos começar instalando o plugin nodemon-webpack. Não se esqueça do sinalizador -D, pois é uma devDependency.

npm i -D nodemon-webpack-plugin

Vamos criar um novo sinalizador nodemon e adicionar o plugin ao nosso webpack.config.js.

// webpack.config.js
'use strict';
const NodemonPlugin = require('nodemon-webpack-plugin');
module.exports = (env = {}) => {
  const config = {
    entry: ['./src/main.js'],
    mode: env.development ? 'development' : 'production',
    target: 'node',
    devtool: env.development ? 'cheap-eval-source-map' : false,  
    resolve: { // diz ao Webpack quais arquivos assistir.
      modules: ['node_modules', 'src', 'package.json'],
    },   
    plugins: [] // necessário para config.plugins.push(...);
  };
if (env.nodemon) {
    config.watch = true;
    config.plugins.push(new NodemonPlugin());
  }
return config;
};

Quando passarmos a bandeira do nodemon, vamos configurar o webpack watch config, e nós iremos adicionar o plugin nodemon. A configuração de time do webpack reconstruirá o aplicativo quando alterarmos um arquivo. O plugin nodemon irá reiniciar o aplicativo quando a reconstrução for concluída.

Também precisamos atualizar nossos comandos npm. Eu também criei alguns comandos de compilação, sem o sinalizador nodemon.

// package.json
...
  scripts: [
    "start": "webpack --progress --env.development --env.nodemon",
    "start:prod": "webpack --progress --env.nodemon",
    "build": "webpack --progress --env.development",
    "build:prod": "webpack --progress",
    "build:ci": "webpack"
  ],
...

Lá vamos nós, uma configuração básica do webpack para o aplicativo Node.js. O próximo passo é adicionar o TypeScript!

TypeScript

E agora, o momento em que todos nós esperávamos, vamos adicionar finalmente o TypeScript!
Vamos começar instalando as dependências de que precisamos. Como este é um projeto em Node.js, também precisamos instalar as tipificações associadas. Estou trabalhando na versão LTS do Node.js, que até o memento é a 10. Por isso, estou instalando a versão ^ 10.0.0 das tipificações.

npm i -D typescript ts-loader @types/[email protected]^10.0.0

ts-loader

Vamos usar o plug-in webpack do ts-loader para compilar nosso TypeScript. Para fazer isso, temos que dizer ao webpack para processar arquivos TypeScript com o plugin ts-loader.

Precisamos mudar nossa extensão de arquivo de entrada para .ts e dizer ao webpack que ele tem que resolver também os arquivos .ts (o webpack só funciona com arquivos .js por padrão).

// webpack.config.js
...
  const config = {
    entry: ['./src/main.ts'],
    mode: env.development ? 'development' : 'production',
    target: 'node',
    devtool: env.development ? 'cheap-eval-source-map' : false,
    resolve: {
      // Diz Webpack quais arquivos para assistir      
      extensions: ['.ts', '.js'],
      modules: ['node_modules', 'src', 'package.json'],
    },
    module: {
      rules: [
        {
          test: /\.ts$/,
          use: 'ts-loader',
        },
      ],
    },
    plugins: [], // Obrigatório para config.plugins.push(...);
  };
...

tsconfig.json

Se tentarmos executar nosso aplicativo agora, ele irá falhar. Ainda estamos perdendo um arquivo tsconfig.json. Então vamos criar um.

// tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": ["dom", "es2018"],
    "allowSyntheticDefaultImports": true,
    "noUnusedLocals":true,
    "removeComments": true,    
    "resolveJsonModule": true,
    "strict": true,
    "typeRoots": ["node_modules/@types"]
  },
  "exclude": ["node_modules"],
  "include": ["src/**/*.ts"]
}

Eu prefiro arquivos strict do tsconfig, como você pode ver acima. Você não tem que fazer isso. Também gosto de definir meu alvo alto (esnext ou es2018) porque o Node.js tem excelente suporte para novos recursos JavaScript.

A aplicação

Ainda precisamos alterar a extensão dos arquivos JavaScript de .js para .ts. Vamos fazer isso e tentar executar o projeto. Depois de executar o projeto, podemos ver imediatamente que cometemos um “erro” no aplicativo de teste que criamos. Não devemos desestruturar nosso campo de nome package.json porque ele já existe no escopo global. Ao fazer isso, nós substituiríamos. Então, vamos fazer algumas alterações nos arquivos.

// information-logger.ts
import os from 'os';
import { name, version } from '../package.json';
export class InformationLogger {
  static logApplicationInformation(): void {
    console.log({
      application: {
        name,
        version,
      },
    });
  }
static logSystemInformation(): void {
    console.log({
      system: {
        platform: process.platform,
        cpus: os.cpus().length,
      },
    });
  }
}
// main.ts
import { InformationLogger } from './information-logger';
InformationLogger.logApplicationInformation();
InformationLogger.logSystemInformation();

Se você seguiu tudo, é assim que a estrutura do nosso projeto deve ser:

dist/
  main.js
node_modules/
src/
  information-logger.ts
  main.ts
package-lock.json
package.json
tsconfig.json
webpack.config.js

E estamos prontos para escrever aplicativos Node.js no TypeScript!

Nota final

Tenho certeza de que existem milhares de maneiras diferentes de escrever aplicativos Node.js em TypeScript. Está forma aqui que foi apresentada é apenas uma delas. Os próximos passos para você ir complementando seus estudos é serem adicionadas integrações com TSLint, adicionar um Dockerfile, configurar um pipeline de CI… Enfim esses são temas para os próximos posts.

Gostou do conteúdo? não deixe de seguir a uebile nas redes sociais, pois toda semana tem post novo aqui no blog com mais dicas para o seu impulso digital.