Node.js e TypeScript: O como e com testes

Todas as vantagens do TypeScript no seu servidor!

Image for post
Image for post
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:

Image for post
Image for post
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:

Image for post
Image for post
Image for post
Image for post

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:

Image for post
Image for post

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:

Image for post
Image for post
Image for post
Image for post
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! 🤗

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store