@crawly/request-client
TypeScript icon, indicating that this package has built-in type declarations

1.4.11 • Public • Published

@crawly/request-client

A biblioteca é um wrapper para algumas libs de requests. Essa biblioteca provém uma API que serve para todas as bibliotecas que ela da suporte, ou seja, com a mesma API você pode alterar o provedor que deseja trabalhar. Atualmente os provedores disponíveis são:

Como usar

Como funciona?

Atualmente a biblioteca é um wrapper para algumas outras bibliotecas, que são descritas como provedores. A biblioteca provém uma API aplicando o padrão Fluent Interface nos métodos, com exceção apenas nos métodos get post put delete, pois são métodos que retornam resultados da página, ou seja, temos fluent interface na construção de um request.

Os métodos para construir um request são acessáveis após instanciar a classe, por exemplo:

import { Request } from '@crawly/request-client';

(new Request()).gzip()
  .headers({ 'x-api-key': 666 })
  .body({ foo: 'bar' });

Usando

Para começar a utilizar a biblioteca, você precisa instalá-la, para isso:

npm i @crawly/request-client

Após instalar, a biblioteca estará disponível para você importar em seu projeto e começar a utilizar. Para usar, você precisa instanciar a classe:

import { Request } from '@crawly/request-client';

const request = new Request();

A partir disso você está pronto para começar a trabalhar com a biblioteca.

Requisição

Mesmo com métodos aplicando Fluent Interface, nós temos um objeto que representa a requisição, esse objeto pode ser acessado nas respostas. O objeto de requisição, que representa o que você definiu que a sua requisição deve ter, tem a seguinte estrutura:

import { CookieJar } from '@crawly/request-client';

interface RequestContract {
  url: string;
  headers?: HeadersRequestContract;
  queryString?: QueryStringRequestContract;
  proxy?: string;
  form?: FormRequestContract;
  body?: any;
  gzip?: boolean;
  followAllRedirects?: boolean;
  timeout?: number;
  acceptUnauthorized?: boolean;
  cookieJar?: CookieJar;
  retry?: RetryRequestContract;
}

type FormRequestContract = {[key: string]: any} | string;

interface RetryRequestContract {
  attempts: number;
  validator: (...args: any[]) => boolean;
}

interface HeadersRequestContract {
  [key: string]: any;
}

type QueryStringRequestContract = {[key: string]: any};

Respostas

Para normalizar as respostas foi definido uma estrutura com as propriedades que decididas como importante. As respostas são dividas entre sucesso e erro.

Sucesso

A resposta de sucesso é recebida quando a promise dos métodos de requisição retornam sucesso. O retorno tem a seguinte estrutura:

interface HeadersResponseContract {
  [key: string]: any;
}

interface ResponseContract {
  statusCode: number;
  body: any;
  headers: HeadersResponseContract;
  request: RequestContract;
}

O objeto RequestContract é a estrutura de requisição, que pode ser encontrado no tópico de requisição.

Erro

A resposta de erro é recebida quando a promise dos métodos de requisição retornam um erro. Os erros são dividos em 2 classes: ClientResponseError e ServerResponseError; ambas as classes estendem a classe ResponseError.

A classe ClientResponseError é retornada quando acontece um erro 4XX, ou seja, um erro relacionado ao cliente. Já a classe ServerResponseError ocorre é retornada quando acontece um erro 5XX, ou seja, um erro relacionado ao servidor. As classes tem as seguintes estruturas:

type StatusErrorResponseContract = 'invalid' | 'error';

class ResponseError extends Error {
  name: string = 'ResponseError';

  response: ResponseContract;

  status: StatusErrorResponseContract;

  statusCode: number;

  __proto__: Error;
}

class ClientResponseError extends ResponseError {
  name: string = 'ClientResponseError';

  status: StatusErrorResponseContract = 'invalid';
}

class ServerResponseError extends ResponseError {
  name: string = 'ServerResponseError';

  status: StatusErrorResponseContract = 'error';
}

O objeto ResponseContract é a estrutura da resposta, que pode ser encontrada no tópico sucesso. Mesmo o objeto ResponseContract ser o objeto de resposta quando a resposta é um sucesso, ele também será presente nas respostas de erro na propriedade response.

Com essa estrutura, você pode fazer validações necessárias com cada cenário, por exemplo:

import {
  ClientResponseError,
  ServerResponseError,
  ResponseError,
} from '@crawly/request-client';

const isClientError = response instanceof ClientResponseError;
const isServerError = response instanceof ServerResponseError;
const isAnyError = response instanceof ResponseError; // Todas as vezes

Alterando o provedor de request

Atualmente por padrão a biblioteca define o provedor SuperAgent como padrão. Para alterar o provedor, basta chamar o método .changeRequestProvider() e passar como parâmetro o provedor que você quer usar. Para passar um valor correto, existe uma classe chamada AvailableProviders que contém os providers disponíveis.

Ao chamar o método .changeRequestProvider() você irá alterar o provedor apenas na instância da classe, para alterar de modo global, utilize em conjunto com o método .saveStateGlobally().

Os providers disponíveis na classe AvailableProvider são:

  • SUPERAGENT
  • REQUEST

Para utilizar, basta fazer:

import { Request, AvailableProviders } from '@crawly/request-client';

const requestOne = new Request();
const requestTwo = new Request();

requestOne.changeRequestProvider(AvailableProviders.REQUEST); // Altera o provedor da instancia requestOne para request
requestTwo.changeRequestProvider(AvailableProviders.SUPERAGENT); // Altera o provedor da instancia requestTwo para super agent
Cookies

É essencial utilizar cookies em requisições, mas ao invés de criarmos uma implementação inteira de um sistema de cookies, estamos utilizando a biblioteca tough-cookie.

A biblioteca não está totalmente exposta, mas as duas classes principais da biblioteca estão, que são Cookie e CookieJar. Para usá-las:

import { Cookie, CookieJar } from '@crawly/request-client';

Eventos

A biblioteca também implementa o Event Listener Pattern, os eventos representam alguma reação interna que acontece na biblioteca.

Para trabalhar com listeners, você pode usar os métodos:

Atualmente, os eventos disponíveis são:

.addEventListener()

Com esse método você pode adicionar um listener a um evento específico. Para isso, basta você passar o nome do evento e um callback, que é o listener, no qual será invocado quando o evento acontecer. Por exemplo:

request.addEventListener('responseError', (err: any) => console.error(err));
.removeEventListener()

Com esse método você pode remover um listener previamente adicionado. Para isso, basta você passar o nome do evento em que o listener se encontra e o callback, que é o listener. Por exemplo:

const logError = (err: any) => console.error(err);

request.addEventListener('requestError', logError);
request.removeEventListener('requestError', logError);
requestStarting

Esse evento acontece qunado o processo de fazer requisição está começando, em outras palavras, antes da requisição acontecer.

Mesmo que o processo contenha retentativas, ele será executado apenas uma vez quando chamar os métodos de execução (get, post, put, delete).

request.addEventListener('requestStarting', (request: RequestContract));
requestError

Esse evento acontece quando obtemos uma resposta que é um erro. O evento acontece antes de retornamos a resposta para a promise.

É importante ressaltar que o evento só acontece no final das requisições. Se temos um retry, mesmo que nas re-tentativas retorne um erro, esse evento só acontecerá na última resposta (se ela for um erro).

O evento tem a seguinte assinatura:

request.addEventListener('requestError', (error: ErrorResponse) => {});

A estrutura da mensagem de erro pode ser encontrado no tópico de erro.

requestSuccess

Esse evento acontece quando obtemos uma resposta que é um sucesso. O evento acontece antes de retornamos a resposta para a promise.

É importante ressaltar que o evento só acontece no final das requisições. Se temos um retry, mesmo que nas re-tentativas retorne um sucesso, esse evento só acontecerá na última resposta (se ela for um erro).

O evento tem a seguinte assinatura:

request.addEventListener('requestSuccess', (response: ResponseContract) => {});

A estrutura da mensagem de erro pode ser encontrado no tópico de sucesso.

requestRetry

Esse evento acontece sempre que uma retentiva acontece, sempre após obter o resultado da retentativa.

É importante ressaltar que o evento acontece sempre após obtermos a resposta de uma retentativa e os eventos requestSuccess e requestError continuam acontecendo normalmente em seu respectivos momentos.

O evento tem a seguinte assinatura:

type ErrorOrSuccessResponseContract = ResponseContract | ResponseErrorContract;

request.addEventListener('requestRetry', (response: ErrorOrSuccessResponseContract, attempt: number) => {});

Em caso de erro estrutura da mensagem pode ser encontrado no tópico de erro e em caso de sucesso pode ser encontrado no tópico de sucesso.

requestFinished

Esse evento acontece qunado o processo de fazer requisição finaliza, em outras palavras, após da requisição acontecer.

Mesmo que o processo contenha retentativas, ele será executado apenas uma vez quando o processo finalizar.

Esse evento é disparado independente se a resposta por um sucesso ou erro.

request.addEventListener('requestFinished', (response: ResponseContract) => {});

.headers()

Esse método define os cabeçalhos que serão enviados na requisição. Você precisa passar um objecto onde a chave é o nome do cabeçalho e o valor é o valor do cabeçalho em específico. Por exemplo:

request.headers({
  'x-api-key': 123,
  'content-type': 'application/json',
})

.queryString()

Esse método define os valores de query string na url. Você precisa passar um objeto onde a chave é o nome da propriedade que vai na url e o valor é o valor da propriedade. Por exemplo:

request.queryString({
  'page': 10,
  'limit': 5,
})

.proxy()

Esse método define o proxy que será usado na requisição. Você precisa passar o proxy que deseja utilizar. Por exemplo:

request.proxy('http://a-931221:b@proxy:22225')

.form()

Esse método define o formulário que será enviado como corpo na requisição. Você precisa passar ou um objeto com chave representando as prorpeidades e o valor o valor dessas propriedades, ou uma string. Por exemplo:

request.form({foo: 'bar'})

É importante definir também, que ao utilizar esse método, será definido o header content-type com application/x-www-form-urlencoded.

.body()

Esse método define o corpo que será enviado na requisição. Você precisa passar um Buffer, String, ReadStream ou um objeto. Por exemplo:

request.body('foo');

.gzip()

Esse método adiciona o suporte a gzip, adicionando o header Accept-Encoding com o valor gzip e decodifica os conteúdos codificados das respostas. Você não precisa passar nenhum parâmetro, você apenas chama esse método quando deseja habilitar o suporte a gzip. Por exemplo:

request.gzip();

.followAllRedirects()

Esse método habilita seguir os redirecionamentos. incluindo requisições HTTP 3XX que não são GET.

request.followAllRedirects();

.timeout()

Esse método define um tempo limite para esperar o server começar a responder com seus cabeçalhos. Você precisa passar o número do tempo limite em milissegundos. Por exemplo:

request.timeout(3000);

.acceptUnauthorized()

Esse método aceita resultado de requisições onde o SSL não é autorizado. Não é necessaŕio passar nenhum parâmetro. Por exemplo:

request.acceptUnauthorized();

.cookieJar()

Esse método define um cookie jar que será utilizado para enviar os cookies presentes no jar e definir os cookies da resposta no jar. É necessário passar a cookie jar no parâmetro. Por exemplo:

import { Cookie, CookieJar } from '@crawly/request-client';

const cookieOne = new Cookie({key: 'foo', value: 'bar'});
const cookieJar = new CookieJar();

cookieJar.setCookieSync(cookieOne, url);

request.cookieJar(cookieJar);

Os cookies da resposta serão adicionados ao jar, mas somente a resposta final (independente se é sucesso ou erro), no caso de retentativas somente será adicionado os cookies da última retentativa (que é a resposta final).

.retry()

Esse método define regras para retentativas de requisição. Pode ser passado 2 parâmetros para o método, sendo 1 obrigatório e outro opcional.

O primeiro parâmetro é o número máximo de retentativas que poderá ser feito.

Já o segundo parâmetro, que é opcional, é o validador que valida se a requisição deve ser retentada ou não. Se o validador retornar true, a requisição será retentada, se false não será retentada. O validador que vem por padrão, apenas verifica o status code da resposta, se a resposta for menor que 399, ele entende que precisa retentar, caso contrario, não. Independente se utilizar o validador padrão ou um customizado, sempre será respeitado o limite máximo de tentativas definido no primeiro parâmetro.

Por exemplo:

type ErrorOrSuccessResponseContract = ResponseContract | ResponseErrorContract;

reques.retry(3, (res: ErrorOrSuccessResponseContract) => res instanceof ErrorResponse);

Em caso de erro estrutura da mensagem pode ser encontrado no tópico de erro e em caso de sucesso pode ser encontrado no tópico de sucesso.

.saveStateGlobally()

Esse método salva globalmente o estado atual da instancia, após salvar todas as novas instâncias vão utilizar esse estado. Por exemplo:

const request1 = (new Request())
  .gzip()
  .followAllRedirects()
  .headers({ 'X-API-KEY': X_API_KEY })
  .saveStateGlobally();

const request2 = new Request();

Nesse exemplo, request2 tem tudo que foi definido em request1.

.resetStateGlobally()

Esse método reseta todo o estado que estava globalmente salvo e volta para o ponto inicial. Se você salva o estado utilizando o método .saveStateGlobally() mas em algum momento quer retirar esse estado salvo, basta utilizar esse método. Por exemplo:

const request1 = (new Request())
  .gzip()
  .followAllRedirects()
  .headers({ 'X-API-KEY': X_API_KEY })
  .saveStateGlobally();

const request2 = new Request();

request2.resetStateGlobally();

const request3 = new Request();

Nesse exemplo, o request2 tem o estado que foi definido no request1, mas ao chamar o método resetStateGlobally, as novas instâncias não terão o estado, ou seja, o request3 não tem o que foi definido no request1.

É importante ressaltar que apenas as novas instâncias não terão o estado definido na criação, as instâncias já criadas não vão ser alteradas (no exemplo request2 ainda terá o estado de request1).

.changeRequestProvider()

Esse método serve para alterar o provedor de request.

.get()

Esse é um dos métodos final da requisição, no qual faz uma requisição GET. É necessário passar a url onde será feito a requisição. Por exemplo:

request.get('http://google.com')

O retorno será um promise.

.use()

Método responsável para fazer integração com plugins. É necessário passar uma função que irá receber um argumento client que é a instância da classe de requisição.

Ao se criar plugins, você pode aplicar mudanças nas requisições, todos os métodos desta documentação estão disponíveis na classe.

const callback = (client: Client) => {
  client.addEventListener('success', () => console.log('Success'));
}

request.use(callback);

.post()

Esse é um dos métodos final da requisição, no qual faz uma requisição POST. É necessário passar a url onde será feito a requisição. Por exemplo:

request.post('http://google.com')

O retorno será um promise.

.put()

Esse é um dos métodos final da requisição, no qual faz uma requisição PUT. É necessário passar a url onde será feito a requisição. Por exemplo:

request.put('http://google.com')

O retorno será um promise.

.delete()

Esse é um dos métodos final da requisição, no qual faz uma requisição DELETE. É necessário passar a url onde será feito a requisição. Por exemplo:

request.delete('http://google.com')

O retorno será um promise.

Readme

Keywords

Package Sidebar

Install

npm i @crawly/request-client

Weekly Downloads

0

Version

1.4.11

License

MIT

Unpacked Size

202 kB

Total Files

185

Last publish

Collaborators

  • tony.crawly
  • jovanepires
  • tagliatti
  • naroga
  • omarkdev
  • naroga.crawly