Node e TypeScript

Criação de uma API com Node e TypeScript.

Hoje veremos como podemos trabalhar com o TypeScript e o Node, para esse post iremos utilizar o ExpressJS para facilitar no momento de expor as nossas rotas. Bom nosso primeiro passo será selecionar um diretório para a nossa aplicação, depois disso vamos criar o nosso arquivo que será responsável por fazer o transpile de .ts para .js. Para isso, crie um diretório com o nome node-typescript e dentro dele um arquivo chamado tsconfig.json, abra o seu editor de preferência e cole o código a baixo dentro do arquivo tsconfig.

{
"compilerOptions": {
"target": "es6",
"module": "commonjs"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}

Vamos entender o código a cima:

Em compilerOptions: nós estamos passando para o TypeScript utilizar o ES2015 e o CommonJS como output, esse e o mesmo modulo que o Node utiliza.

Em Include nós estamos passando onde ele deve buscar os nossos arquivos .ts e em Exclude onde ele não deve buscar, assim nós garantimos o escopo para ele trabalhar e garantimos que ele não irá fazer um transpile de arquivos indesejados.

Seguindo o nosso arquivo acima, agora precisamos criar o nosso diretório src, como dito antes, será nele que iremos criar os nosso arquivos .ts. Feito isso, precisamos iniciar o nosso projeto com o comando npm init -y, esse arquivo irá criar um novo arquivo chamado package.json para o nosso projeto. Até esse momento nós temos a estrutura a baixo:

Agora vamos baixar o pacote do TypeScript. Para isso, execute o comando a baixo dentro no cmd dentro do caminho do seu projeto:

npm install typescript@2.0.6 --save-dev

Para que possamos validar se tudo está OK, vamos criar um novo arquivo chamado test.js dentro do diretório src e cole o código a baixo dentro dele:

console.log('Hello World!');

Agora para ficar legal e padrão, adicione a seguinte linha de código "outDir": "dist" dentro do seu arquivo tsconfig.json. Para que você possa comparar segue como a baixo como o arquivo deve ficar:

{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "dist"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}

Agora execute o comando node_modules/.bin/ts na sua console, ele deve irá criar um novo diretório chamado dist e dentro dele um aquivo test.js. Podemos ver esse resultado na imagem a baixo:

A primeira parte do nosso código está OK ;) Mas não seria melhor ter algo que em todas as nossas alterações já criasse os nossos arquivos ou atualizasse eles sem a necessidade de estarmos rodando o comando acima? Se a sua resposta for sim ;) Podemos utilizar o Gulp para isso, ele irá nos auxiliar nessa task, de um modo automático, nós somente iremos precisar baixar ele e configurar uma vez. Para isso, execute o comando a baixo na sua console:

npm install gulp@3.9.1 gulp-typescript@3.1.1 --save-dev

Agora iremos precisar criar o nosso arquivo responsável por automatizar essas builds. Para isso, crie um arquivo chamado gulpfile.js na raiz do seu projeto e cole o código a baixo dentro dele:

const gulp = require('gulp');
const ts = require('gulp-typescript');
const JSON_FILES = ['src/*.json', 'src/**/*.json'];
const tsProject = ts.createProject('tsconfig.json');
gulp.task('scripts', () => {
const tsResult = tsProject.src()
.pipe(tsProject());
return tsResult.js.pipe(gulp.dest('dist'));
});
gulp.task('watch', ['scripts'], () => {
gulp.watch('src/**/*.ts', ['scripts']);
});
gulp.task('assets', function() {
return gulp.src(JSON_FILES)
.pipe(gulp.dest('dist'));
});
gulp.task('default', ['watch', 'assets']);

Eu não irei entrar em detalhes do código acima por ele ser padrão, mas em resumo ele está pegando as configurações em tsProject do nosso arquivo tsconfig e depois indo no diretório src da mesma forma que estávamos trabalhando antes, a novidade é a ultima linha do nosso arquivo que irá ficar monitorando o nosso diretório a procura de novos arquivos ou alterações. Para testar delete o diretório dist que nós criamos no teste anterior, em seguida execute o comando gulp no seu terminal. Podemos ver o resultado da execução desse comando na imagem a baixo:

Show ;) Agora que esta tudo configurado vamos as nossas APIS. Para isso, iremos precisar instalar o pacote express-generator junto com um pacote de debug para que possamos ver os nossos logs no terminal.

npm install express@4.14.0 debug@2.2.0 --save

No TypeScript quando nós instalamos um pacote de terceiros, nos devemos também puxar os tipos de definições dos pacotes. Com isso nós conseguimos passar para o compilador a estrutura do modulo que estamos utilizando, passando para ele as informações necessárias para que ele possas compilar corretamente. Bom, sem mais bla bla bla para que ele possa importar os pacotes de terceiros nós iremos precisar instalar mais um pacote para o nosso projeto. Para isso, execute o comando a baixo na sua console:

npm install @types/node@6.0.46 @types/express@4.0.33 @types/debug@0.0.29 --save-dev

Para que possamos testar e validar os pacotes, vamos criar o nosso primeiro server HTTP. Delete o arquivo test.ts e o diretório dist, agora crie um novo arquivo dentro de src chamado index.ts e cole dentro dele o código a baixo:

import * as http from 'http';
import * as debug from 'debug';
import App from './App';
debug('ts-express:server');
const port = normalizePort(process.env.PORT || 3000);
App.set('port', port);
const server = http.createServer(App);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
function normalizePort(val: number|string): number|string|boolean {
let port: number = (typeof val === 'string') ? parseInt(val, 10) : val;
if (isNaN(port)) return val;
else if (port >= 0) return port;
else return false;
}
function onError(error: NodeJS.ErrnoException): void {
if (error.syscall !== 'listen') throw error;
let bind = (typeof port === 'string') ? 'Pipe ' + port : 'Port ' + port;
switch(error.code) {
case 'EACCES':
console.error(`${bind} requires elevated privileges`);
process.exit(1);
break;
case 'EADDRINUSE':
console.error(`${bind} is already in use`);
process.exit(1);
break;
default:
throw error;
}
}
function onListening(): void {
let addr = server.address();
let bind = (typeof addr === 'string') ? `pipe ${addr}` : `port ${addr.port}`;
debug(`Listening on ${bind}`);
}

Como eu estou utilizando o Visual Studio code, ele está passando que eu não tenho o ./App no meu código, por hora podemos ignorar essa parte. Agora quanto ao restante do código nos temos o debug para que possamos ver os logs na nossa console, a configuração da porta que estamos utilizando, que para esse exemplo é a 3000. Em seguida nós estamos criando um server HTTP e passando o app para ele “calma iremos criar esse arquivo nos próximos passos ;) ele será responsável por configurar as nossas rotas.”. Por fim, nos estamos configurando para que em caso de erro apareça uma mensagem na console.

Vamos agora criar o nosso arquivo App.ts dentro do diretório src, em seguida vamos adicionar o pacote body-parser para nos auxiliar com os objetos em json.

npm install express@4.14.0 body-parser@1.15.2 morgan@1.7.0 --save npm install @types/body-parser@0.0.33 @types/morgan@1.7.32 --save-dev

Notem que estamos seguindo a mesma linha que a dos pacotes acima, estamos baixando o pacote e as referencias para o TypeScript.

import * as path from 'path';
import * as express from 'express';
import * as logger from 'morgan';
import * as bodyParser from 'body-parser';
// Criando as configurações para o ExpressJS
class App {
// Instancia dele
public express: express.Application;
  constructor() {
this.express = express();
this.middleware();
this.routes();
}
// Configuração para o nosso middler
  private middleware(): void {
this.express.use(logger('dev'));
this.express.use(bodyParser.json());
this.express.use(bodyParser.urlencoded({ extended: false }));
}
//Configuração da nossa API e nossos EndPoint e o famoso Hello 
  private routes(): void {   
let router = express.Router();

router.get('/', (req, res, next) => {
res.json({
message: 'Hello World!'
});
});
this.express.use('/', router);
}
}
export default new App().express;

Agora vamos executar o comando gulp scripts, ele irá criar a pasta dist e dentro dela os arquivos app.js e o index.js. Podemos ver o resultado desse comando na image a baixo:

Para que possamos executar o projeto, vamos adicionar o código a baixo dentro do nosso package.json dentro de start.

“start”: “node dist/index.js”

Agora para que possamos executar o nosso código para validarmos esse ponto só precisamos executar npm start na nossa console e abrir no nosso navegador o endereço http://localhost:3000/, notem que quando abrimos no navegador conseguimos pegar a requisição na nossa console conforme está na image a baixo:

Bom, até aqui nós conseguimos criar o nosso arquivo index.ts que é responsável pelas configurações do nosso projeto como porta, log … etc, criamos a nossa class App.ts que será responsável pelo bootstrap do nosso projeto, será a class de start. Nós fizemos esses passos para deixar tudo no esquema para criação das API’s ;). Para começar vamos criar um arquivo que será responsável pelas nossas rotas, crie uma nova pasta chamada router dentro de src, após isso crie um novo arquivo chamado personRouter.ts e cole o código a baixo dentro dele:

import { Router, Request, Response, NextFunction } from 'express';
export class PersonRouter {
router: Router
constructor() {
this.router = Router();
this.init();
}
public get(req: Request, res: Response, next: NextFunction) {
res.send("Person");
}
init() {
this.router.get('/', this.get);
}
}
const personRouter = new PersonRouter();
personRouter.init();
export default personRouter.router;

Vamos entender o código acima:

1º Estamos importando os nossos pacotes na primeira linha do nosso arquivo;

2º em seguida estamos criando uma classe chamada PersonRouter com um construtor referenciando router e a o método init;

3º Podemos observar que o método init esta passando uma rota para o nosso método get que irá retornar a mensagem Person para o nosso navergador.

Por fim exportamos a nossa classe. Agora para que possamos testar, basta importarmos a nossa rota na nossa classe App.ts em seguida depois da nossa rota padrão, vamos adicionar a nossa nova rota. Podemos ver essa implementação a baixo:

this.express.use(‘/’, router);
this.express.use(‘/api/v1/person’, PersonRouter);

Agora execute o comando gulp scripts para que ele possa criar os nossos arquivos, assim que ele atualizar a nossa pasta dist, execute o comando npm start e abra no seu navegador o endereço abaixo:

http://localhost:3000/api/v1/person

Caso tudo esteja OK, ele irá apresentar a mensagem person conforme está na imagem a baixo:

Com isso nós conseguimos dar os nossos primeiros passos com TypeScript e o Node.js, espero que tenham gostado e até a próxima pessoal ;)

Caso queria ver o código desse artigo, nós subimos ele no github no link.