The HTTP Client Module
This module exports a generic http client environmental effect with syntactic sugar to deal with http requests
Feature set
yarn add @matechs/http-clientModule
import { effect as T } from "@matechs/effect";
import { Predicate } from "fp-ts/lib/function";
import { Option } from "fp-ts/lib/Option";
// various environment entries
export const middlewareStackEnv: unique symbol = Symbol();
export const httpEnv: unique symbol = Symbol();
export const httpHeadersEnv: unique symbol = Symbol();
export const httpDeserializerEnv: unique symbol = Symbol();
// http methods
export enum Method {
GET,
POST,
PUT,
DELETE,
PATCH
}
// request content type use for posting data
export type RequestType = "JSON" | "DATA" | "FORM";
// represents an input compatible with type DATA
export interface DataInput {
}
// represents headers
export type Headers = Record<string, string>;
// represents an http response
export interface Response<Body> {
body: Option<Body>;
headers: Headers;
status: number;
}
// represent the error when we have a response
export interface HttpResponseError<ErrorBody> {
_tag: HttpErrorReason.Response;
response: Response<ErrorBody>;
}
// represent cases where the request failed,
// for example malformed or network down
export interface HttpRequestError {
_tag: HttpErrorReason.Request;
error: Error;
}
// index error cases
export enum HttpErrorReason {
Request,
Response
}
// describe http error
export type HttpError<ErrorBody> =
| HttpRequestError
| HttpResponseError<ErrorBody>;
// describe an effect used to deserialize http responses
export interface HttpDeserializer {
response: <A>(a: string) => A | undefined;
errorResponse: <E>(error: string) => E | undefined;
};
}
// fold on an http error
export function foldHttpError<A, B, ErrorBody>(
onError: (e: Error) => A,
onResponseError: (e: Response<ErrorBody>) => B
): (err: HttpError<ErrorBody>) => A | B
// describe an environment used to provide headers
export interface HttpHeaders {
[httpHeadersEnv]: Record<string, string>;
}
// main http effect
export interface Http {
request: <E, O>(
method: Method,
url: string,
headers: Record<string, string>,
requestType: RequestType,
body?: unknown
) => T.Effect<HttpDeserializer, HttpError<E>, Response<O>>;
};
}
// request function type exposed to allow middleware creation
export type RequestF = <R, E, O>(
method: Method,
url: string,
requestType: RequestType,
body?: unknown
) => T.Effect<RequestEnv & R, HttpError<E>, Response<O>>;
// describe a middleware that will be executed on every request
export type RequestMiddleware = (request: RequestF) => RequestF;
// describe an environment entry to hold the middlewares configured
export interface MiddlewareStack {
[middlewareStackEnv]?: {
stack: RequestMiddleware[];
};
}
// construct an environment with provided middlewares
export const middlewareStack: (
stack?: RequestMiddleware[]
) => MiddlewareStack
// represent environment to be used by consumer
export type RequestEnv = Http & HttpDeserializer & MiddlewareStack;
// JSON GET
export function get<E, O>(
url: string
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// JSON POST
export function post<I, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// DATA GET
export function postData<I extends DataInput, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// FORM POST
export function postForm<E, O>(
url: string,
body: FormData
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// JSON PATCH
export function patch<I, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// DATA PATCH
export function patchData<I extends DataInput, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// FORM PATCH
export function patchForm<E, O>(
url: string,
body: FormData
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// JSON PUT
export function put<I, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// DATA PUT
export function putData<I extends DataInput, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// FORM PUT
export function putForm<E, O>(
url: string,
body: FormData
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// JSON DELETE
export function del<I, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function delData<I extends DataInput, E, O>(
url: string,
body?: I
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// FORM DELETE
export function delForm<E, O>(
url: string,
body: FormData
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
// Provide headers in child environment
// replace = true will discard any header already in environment
// replace = false (default) will merge the two
export function withHeaders(
headers: Record<string, string>,
replace = false
): <R, E, A>(eff: T.Effect<R, E, A>) => T.Effect<R, E, A>
// Provide headers through a middleware
// Request path used to restrict to specific domains/urls
// Replace as per withHeaders
export function withPathHeaders(
headers: Record<string, string>,
path: Predicate<string>,
replace = false
): RequestMiddleware
// Default json deserializer implementation
export const jsonDeserializer: HttpDeserializer
// Fold over the request type (useful in middleware dev)
export function foldRequestType<A, B, C>(
requestType: RequestType,
onJson: () => A,
onData: () => B,
onForm: () => C
): A | B | C
// Get method as string (useful in middleware dev)
export function getMethodAsString(method: Method): stringUsage
Notes
Last updated