1. Introdução
Neste artigo, gostaria de compartilhar minhas emoções e habilidades adquiridas no desenvolvimento da primeira API REST no Node.js usando o TypeScript, como se costuma dizer, do zero. A história é bastante banal:
“Me formei na universidade, recebi um diploma. Onde ir trabalhar? Como você deve ter adivinhado, o problema não passou por mim, embora eu não tivesse que pensar muito. O desenvolvedor (um graduado da mesma especialidade) pediu um estágio. Acredito que essa é uma prática bastante comum e há muitas histórias semelhantes. Sem pensar duas vezes, decidi tentar minha mão e fui ...

Primeiro dia Apresentando o Node.js
Eu vim para o desenvolvimento de back-end. Essa empresa de TI usa a plataforma
Node.js. , com a qual eu não estava familiarizado. Avancei um pouco, esquecendo de dizer ao leitor que nunca havia desenvolvido nada em JavaScript (exceto alguns scripts com código de cópia). Em geral, entendi o algoritmo de trabalho e a arquitetura de aplicativos da Web, pois desenvolvi CRUD em Java, Python e Clojure, mas isso não foi suficiente. Portanto, no primeiro dia em que me dediquei completamente ao estudo do Node.js, esse
screencast realmente ajudou.
Enquanto estudava a estrutura da Web
Express , o
gerenciador de pacotes
npm , bem como arquivos como package.json e tsconfig.json, minha cabeça simplesmente contornou a quantidade de informações. Outra lição é que dominar todo o material ao mesmo tempo é quase uma tarefa impossível. No final do dia, eu ainda conseguia configurar o ambiente e consegui executar o servidor expresso da Web! Mas era muito cedo para se alegrar, porque ele foi para casa com um sentimento total de mal-entendido. A sensação de que eu estava me afogando no vasto mundo de JS não me deixou por um minuto, então foi necessária uma reinicialização.
Segundo dia. Apresentando o TypeScript
A mesma reinicialização ocorreu no mesmo dia. Neste ponto, eu reconheci completamente o meu problema, passaremos a ele um pouco mais baixo. Sabendo que não era necessário escrever em JavaScipt puro, o treinamento do Node.js fluiu suavemente para a linguagem TypeScript, a saber, seus recursos e sintaxe. Aqui vi os
tipos há muito esperados, sem os quais a programação era literalmente 2 dias atrás,
não nas linguagens de programação funcionais. Esse foi o meu maior equívoco, o que me impediu de entender e aprender o código escrito em JavaScript no primeiro dia.
Ele escreveu anteriormente em grande parte em linguagens de programação orientadas a objetos, como Java, C ++, C #. Percebendo as possibilidades do TypeScript, me senti à vontade. Essa linguagem de programação literalmente respirou em mim a vida desse ambiente complexo, como me pareceu naquele momento. No final do dia, configurei completamente o ambiente, iniciei o servidor (já no TypeScript) e conectei as bibliotecas necessárias, as quais discutirei abaixo. Conclusão: pronto para desenvolver a API. Passamos diretamente para o desenvolvimento ...
Desenvolvimento de API
Uma explicação do princípio do trabalho e outras explicações sobre o que é a API REST, deixaremos, porque o fórum possui muitos artigos sobre isso com exemplos e desenvolvimento em várias linguagens de programação.
A tarefa foi a seguinte:Faça um serviço com uma API REST. Autorização pelo token do portador (/ info, / latency, / logout). CORS configurado para acesso de qualquer domínio. DB - MongoDB. Crie um token em cada chamada.
Descrição da API:- / signin [POST] - solicita ao portador do token por ID e senha // recebe dados em json
- / signup [POST] - registro de um novo usuário: // recebe dados em json
- / info [GET] - retorna o ID do usuário e o tipo de ID, requer o token emitido pelo portador na autenticação
- / latency [GET] - retorna um atraso (ping), requer o token emitido pelo portador na autenticação
- / logout [GET] - com o parâmetro all: true - exclui todos os tokens do portador do usuário ou false - exclui apenas o token do portador atual
Percebo imediatamente, a tarefa parece incrivelmente simples para um desenvolvedor de aplicativos da web. Mas a tarefa deve ser implementada em uma linguagem de programação, sobre a qual há 3 dias não sabia nada! Mesmo para mim, parece completamente transparente no papel e, no Python, a implementação demorou um pouco, mas eu não tinha essa opção. A pilha de desenvolvimento pressagiava problemas.
Meios de implementação
Mencionei que no segundo dia eu já estudei várias bibliotecas (frameworks), vamos começar com isso. Para roteamento, escolhi
controladores de roteamento , guiados por muitas semelhanças com decoradores do Spring Framework (Java). Como ORM, eu escolhi
typeorm , embora trabalhe com o MongoDB no modo experimental, é o suficiente para essa tarefa. Eu usei o
uuid para gerar tokens, as variáveis são carregadas usando o
dotenv .
Inicialização do servidor da Web
Geralmente, o express é usado em sua forma pura, mas mencionei a estrutura dos controladores de roteamento, que nos permite criar um servidor expresso da seguinte maneira:
Como você pode ver, não há nada complicado. De fato, a estrutura possui muito mais recursos, mas não havia necessidade deles.
- routePrefix é apenas um prefixo no seu URL após o endereço do servidor, por exemplo: localhost : 3000 / prefix
- padrões - nada de interessante, basta inicializar os códigos de erro
- notificationChecker - uma ótima oportunidade para a estrutura verificar a autorização do usuário; consideraremos com mais detalhes
- controllers é um dos principais campos em que especificamos os controladores usados em nossa aplicação
Conexão de banco de dados
Anteriormente, já havíamos lançado o servidor da Web, portanto, continuaremos a nos conectar ao banco de dados do MongoDB, depois de implantá-lo no servidor local. A instalação e configuração são descritas em detalhes na
documentação oficial . Vamos considerar diretamente a conexão usando typeorm:
Tudo é bastante simples, você precisa especificar vários parâmetros:
- type - DB
- host - endereço IP em que você implantou o banco de dados
- database - o nome do banco de dados que foi criado anteriormente no mongodb
- sincronizar - sincronização automática com o banco de dados (Nota: era difícil dominar a migração naquele momento)
- entidades - aqui indicamos as entidades com as quais a sincronização é realizada
Agora, conectamos o lançamento do servidor e a conexão ao banco de dados. Observo que a importação de recursos é diferente da clássica usada no Node.js. Como resultado, obtemos o seguinte arquivo executável, no meu caso main.ts:
import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { createExpressServer } from 'routing-controllers'; import { createConnection } from 'typeorm'; import { authorizationChecker } from './auth/authorizationChecker'; import { UserController } from './controllers/UserController'; import { User } from './models/User'; dotenv.config();
Entidades
Deixe-me lembrá-lo de que a tarefa é autenticar e autorizar usuários, respectivamente, precisamos de uma entidade: Usuário. Mas isso não é tudo, já que cada usuário tem um token e não um! Portanto, é necessário criar uma entidade Token.
Usuário import { ObjectID } from 'bson'; import { IsEmail, MinLength } from 'class-validator'; import { Column, Entity, ObjectIdColumn } from 'typeorm'; import { Token } from './Token';
Na tabela Usuário, criamos um campo - uma matriz dos mesmos tokens para o usuário. Também
habilitamos o calss-validator , pois é necessário que o usuário efetue login por email.
Token import { Column, Entity } from 'typeorm';
A base é a seguinte:

Autorização do Usuário
Para autorização, usamos o
authorChecker (um dos parâmetros ao criar o servidor, veja acima); por conveniência, colocamos em um arquivo separado:
import { Action, UnauthorizedError } from 'routing-controllers'; import { getMongoRepository } from 'typeorm'; import { User } from '../models/User'; export async function authorizationChecker(action: Action): Promise<boolean> { let token: string; if (action.request.headers.authorization) {
Após a autenticação, cada usuário possui seu próprio token, para que possamos obter o token necessário dos cabeçalhos da resposta, é algo como:
Portador 046a5f60-c55e-11e9-af71-c75526de439e . Agora podemos verificar se esse token existe, após o qual a função retorna informações de autorização: true - o usuário está autorizado, false - o usuário não está autorizado. No aplicativo, podemos usar um decorador muito conveniente no controlador: @Authorized (). Nesse momento, a função authenticationChecker será chamada, o que retornará uma resposta.
Lógica
Para começar, gostaria de descrever a lógica de negócios, uma vez que o controlador é uma linha de chamada de método abaixo da classe apresentada. Além disso, no controlador, aceitaremos todos os dados; no nosso caso, serão JSON e Query. Consideraremos os métodos para tarefas individuais e, no final, formaremos o arquivo final, chamado UserService.ts. Observo que naquele momento simplesmente não havia conhecimento suficiente para eliminar dependências. Se você não encontrou o termo injeção de dependência, recomendo a leitura. No momento, eu uso a estrutura DI, ou seja, uso contêineres, ou seja, injeção através de construtores. Aqui, eu acho, é um bom
artigo para revisão. Voltamos à tarefa.
- / signin [POST] - autenticação do usuário registrado. Tudo é muito simples e transparente. Só precisamos encontrar esse usuário no banco de dados e emitir um novo token. Para leitura e escrita, o MongoRepository é usado.
async userSignin(user: User): Promise<string> {
- / signup [POST] - registra um novo usuário. Um método muito semelhante, pois, inicialmente, também estamos procurando um usuário para que não tenhamos usuários registrados com um e-mail. Em seguida, escrevemos o novo usuário no banco de dados, depois de emitir o token.
async userSignup(newUser: User): Promise<string> {
- / info [GET] - retorna a identificação do usuário e o tipo de identificação, requer o token emitido pelo portador na autenticação. A imagem também é transparente: primeiro obtemos o token atual do usuário nos cabeçalhos da solicitação, depois procuramos no banco de dados e determinamos em quem ele se encontra, e retornamos o usuário encontrado.
async getUserInfo(req: express.Request): Promise<User> {
- / latency [GET] - retorna um atraso (ping), requer o token emitido pelo portador na autenticação. Um parágrafo completamente desinteressante do artigo, no entanto. Aqui eu usei apenas uma biblioteca pronta para verificar o atraso do tcp-ping.
getLatency(): Promise<IPingResult> { function update(progress: number, total: number): void { console.log(progress, '/', total); } const latency = ping({ address: process.env.PING_ADRESS, attempts: Number(process.env.PING_ATTEMPTS), port: Number(process.env.PING_PORT), timeout: Number(process.env.PING_TIMEOUT) }, update).then(result => { console.log('ping result:', result); return result; }); return latency; }
- / logout [GET] - com o parâmetro all: true - exclui todos os tokens do portador do usuário ou false - exclui apenas o token do portador atual. Só precisamos encontrar o usuário, verificar o parâmetro de consulta e remover os tokens. Eu acho que tudo deve ficar claro.
async userLogout(all: boolean, req: express.Request): Promise<void> {
Controlador
Muitos não precisam explicar o que é necessário e como o controlador é usado no padrão MVC, mas ainda direi duas palavras. Em resumo, o controlador é o link entre o usuário e o aplicativo que redireciona os dados entre eles. A lógica foi completamente descrita acima, cujos métodos são chamados de acordo com as rotas, consistindo em um URI e um servidor IP
(exemplo: localhost: 3000 / signin) . Eu mencionei anteriormente sobre os decoradores no controlador:
Get ,
POST , @Authorized e o mais importante deles é o @JsonController. Outra característica muito importante dessa estrutura é que, se queremos enviar e receber JSON, usamos esse decorador em vez do
Controller .
import * as express from 'express'; import { Authorized, Body, Get, Header, JsonController, NotFoundError, Post, QueryParam, Req, UnauthorizedError } from 'routing-controllers'; import { IPingResult } from '@network-utils/tcp-ping'; import { User } from '../models/User'; import { UserService } from '../services/UserService';
Conclusão
Neste artigo, eu queria refletir não mais o componente técnico do código correto ou algo parecido, mas simplesmente compartilhar o fato de que uma pessoa pode criar um aplicativo Web usando um banco de dados e contendo pelo menos alguma lógica de um zero absoluto em
cinco dias. Apenas pense, nenhum instrumento era familiar, lembre-se de si mesmo ou apenas coloque-o no meu lugar. Em nenhum caso é o caso que diz: "Eu sou o melhor, você nunca pode fazer isso". Pelo contrário, este é um grito da alma de uma pessoa que atualmente está completamente encantada com o mundo do Node.js e compartilha isso com você. E o fato de que nada é impossível, você só precisa pegar e fazer!
Obviamente, não se pode negar que o autor não sabia nada e sentou-se para escrever o código pela primeira vez. Não, o conhecimento de OOP, os princípios da API REST, ORM e o banco de dados estavam presentes em quantidades suficientes. E isso só pode dizer que os meios para alcançar o resultado absolutamente não desempenham nenhum papel e dizer no estilo: "Eu não vou para este trabalho, há uma linguagem de programação que eu não aprendi", para mim agora é apenas a manifestação de uma pessoa, não de fraqueza, mas sim proteção contra um ambiente externo desconhecido. Mas o que há para esconder, o medo estava presente comigo.
Para resumir. Quero aconselhar estudantes e pessoas que ainda não começaram sua carreira em TI, a não terem medo de ferramentas de desenvolvimento e tecnologias desconhecidas. Os camaradas seniores certamente o ajudarão (se você tiver sorte, assim como eu), eles explicarão em detalhes e responderão a perguntas, porque cada um deles estava nessa posição. Mas não esqueça que seu desejo é o aspecto mais importante!
Link para o
projeto