Node.js e TypeScript: O como e com testes

Todas as vantagens do TypeScript no seu servidor!

Eduardo Rabelo
Aug 31, 2018 · 6 min read
Unindo o melhor dos dois mundos!

Você já leu por aqui como converter React.js para TypeScript. Falamos também sobre como configurar React Native e TypeScript! Você se lembra que até cobrimos como utilizar TypeScript sem utilizar TypeScript? Vou te dizer, é tanto <T>(a: T): U & F que dói a cabeça! 🤕🤒… Pensando nisso, nós tivemos um artigo completo de como entender a notação de tipos em TypeScript.

Falamos da sintaxe do TypeScript, front-end com React, aplicações nativas com React Native e, dentro do ecossistema JavaScript, só ficou de fora o nosso back-end. 😭


Atualização - 19 Outubro 2018: Após o comentário do leitor Bruno Dutra Franco, atualizei o repositório adicionando exemplos de “Debugging”. Você pode usar debug in-editor com o VSCode ou a CLI do Node.js e abrir um aba do Google Chrome. Informações completas no README do repositório!

Atualização - 04 Setembro 2018: Coloquei o exemplo descrito nesse artigo em um repositório, ficando mais fácil para ver o código completo:


Node.js ao resgate

Iremos instalar os pacotes básicos para ter uma aplicação Node.js rodando com TypeScript e todas suas vantagens:

$ mkdir node-typescript
$ cd node-typescript
$ npm init -y

Vamos instalar as primeiras depêndencias:

$ npm i typescript nodemon ts-node

E rodar o comando init no recém instalado módulo do typescript:

$ ./node_modules/.bin/tsc --init

Isso irá gerar um arquivo tsconfig.json na sua pasta. Essas são as configurações que serão passadas para o compilador do TypeScript. Ao abrir esse arquivo, ele irá conter comentários e várias opções, parecido com:

Valores padrões do `tsconfig.json`

Vamos editar esse arquivo, ficando dessa maneira:

// node-typescript/tsconfig.json{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"strict": true, "noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
}
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}

Tem alguma dúvida sobre as chaves e seus valores? Recomendo primeiro tentar pesquisar/Google. Persistindo a dúvida comente aqui embaixo :)

Agora, iremos editar nosso package.json :

// node-typescript/package.json{
"name": "node-typescript",
"version": "1.0.0",
"private": true,
"scripts": {
// [A]
"dev": "nodemon --watch "src/" --exec \"ts-node src/entry.ts\" -e ts"
},
"dependencies": {
"nodemon": "^1.18.4",
"ts-node": "^7.0.1",
"typescript": "^3.0.3"
}
}
  • [A]: nodemon irá observar, --watch, o diretório src e executar, --exec , o script, "ts-node src/entry.ts", a qualquer mudança de arquivo com extensão ts, -e ts

Antes de seguir em frente, vamos instalar o framework mais querido do Node.js, express:

$ npm i express @types/express

Seguindo o comando "dev" acima, vamos criar src/entry.ts:

// node-typescript/src/entry.tsimport server from './server';server.listen(3000, () => {
console.log(`[SERVER] Running at http://localhost:3000`);
});

Opa! Esse arquivo está buscando server em "./server". Então, vamos criar nosso src/server.ts:

// node-typescript/src/server.tsimport express from "express";const server = express();server.get("/", (_, res) => {
res.send("Hello ts-node!");
});
export default server;

Agora, podemos utilizar npm run dev e:

Hooray! 🎉🎉

Se você usa VSCode, a integração com o editor é fantástica, auto-completando seus métodos e acessos de objetos. Ah! Você percebeu que estamos utilizando a sintaxe ES6? ;)

No cap, bro! 😎

Adicionando testes com Jest

Jest é a maneira mais divertida de escrever testes. E em Node.js com TypeScript, não é diferente! Vamos instalar as depêndencias:

$ npm i @types/jest jest ts-jest

Agora precisamos dizer ao Jest para utilizar o compilador do TypeScript e também procurar por arquivos .ts. Vamos adicionar essa configuração extra do Jest no package.json :

// node-typescript/package.json{
// ...outras regras do seu `package.json`
"jest": {
"transform": {
"^.+\\.ts$": "ts-jest" // [A]
},
"testRegex": "\\.test\\.ts", // [B]
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node" // [C]
]
}
}
  • [A]: estamos dizendo ao Jest que, ao encontrar arquivos .ts utilize o ts-jest como parte da transformação/resolução do módulo
  • [B]: minha preferência pessoal são arquivos <nome>.test.js, sempre co-locados com o arquivo testado, você pode mudar essa RegEx
  • [C]: precisamos declarar todas essas extensões (podemos omitir .tsx/jsx nesse exemplo) pois arquivos de módulos (dentro de node_modules/) precisam ser importados pelo Jest e ou qualquer outra dependência

Para testar nossa aplicação express, iremos utilizar a biblioteca supertest:

$ npm i @types/supertest supertest

Dentro do nosso package.json , vamos adicionar um script chamado test:

// node-typescript/package.json{
// ...outras regras do seu `package.json`
"scripts": {
// ...outros scripts
"test": "jest --no-cache"
}
}

Agora já temos todas as dependências e o comando correto, vamos criar nosso arquivo src/server.test.ts e escrever nosso primeiro teste:

// node-typescript/src/server.test.tsimport req from "supertest";
import server from "./server";
test("[GET] /", async () => {
const res = await req(server).get("/");
expect(res.text).toBe("Hello ts-node!");
});

Ao executar npm run test temos como resultado:

Como você pode ver, ter dividido nossa aplicação express em entry.ts e server.ts, nos ajuda a importar toda a aplicação sem executá-la (essa abordagem é opcional). Também estamos utilizando sintaxe ES6 em nossos testes ;)

Isso funciona em produção?

Olhando de perto nossa configuração, só estamos utilizando ts-node para iniciar nosso servidor. Por padrão, ele executa o compilador do TypeScript, carrega todos os tipos, faz todas análises estáticas etc.

Como comentado no repositório do ts-node, ele não é recomendado para produção. Também não há nenhum benchmark dizendo que há problemas de performance ao usar ts-node, o ponto principal é o consumo de memória, devido ao uso conjunto com o compilador do TypeScript.

Tendo isso em mente, precisamos criar mais uma etapa, o famigerado build!

Porém, se você olhar nossa configuração do TypeScript, lá no começo, o tsconfig.json, nós temos a seguinte chave: "outDir": "./dist". Em outras palavras, já está tudo configurado! 🤓

Vamos adicionar mais 2 comandos ao nosso package.json , um para executar o compilador do TypeScript e imprimir arquivos .js e outro para executar o nosso servidor utilizando esse resultado.

// node-typescript/package.json{
// ...outras regras do seu `package.json`
"scripts": {
// ...outros scripts
"build": "tsc",
"prod": "npm run build && node dist/entry.js"
}
}

E ao executarmos npm run prod , teremos:

Prometo que não é a mesma imagem :P

Hooray! 🎉🎉

Agora estamos compilando para puro JavaScript e utilizando Node.js para rodar o servidor.

A partir daqui você pode adicionar todas as suas práticas, módulos e pacotes preferidos. Junto da segurança de ter seu código back-end com análise estáticas e todos os benefícios do ecossistema do TypeScript.

Você que chegou até aqui, meu muito obrigado! 🤗

Eduardo Rabelo

Written by

☕🇳🇿 - https://eduardorabelo.me

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade