@crawly/request-client
v1.4.11
Published
Request client
Downloads
31
Readme
@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:
- SuperAgent - Definida por padrão
- Request Promise Native
Como usar
- Como funciona?
- Usando
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.