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();
// request content type use for posting data
export type RequestType = "JSON" | "DATA" | "FORM";
// represents an input compatible with type DATA
export interface DataInput {
export type Headers = Record<string, string>;
// represents an http response
export interface Response<Body> {
// 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;
export enum HttpErrorReason {
export type HttpError<ErrorBody> =
| 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;
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>;
headers: Record<string, string>,
requestType: RequestType,
) => T.Effect<HttpDeserializer, HttpError<E>, Response<O>>;
// request function type exposed to allow middleware creation
export type RequestF = <R, E, O>(
requestType: RequestType,
) => 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 {
stack: RequestMiddleware[];
// construct an environment with provided middlewares
export const middlewareStack: (
stack?: RequestMiddleware[]
// represent environment to be used by consumer
export type RequestEnv = Http & HttpDeserializer & MiddlewareStack;
export function get<E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function post<I, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function postData<I extends DataInput, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function postForm<E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function patch<I, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function patchData<I extends DataInput, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function patchForm<E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function put<I, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function putData<I extends DataInput, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function putForm<E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function del<I, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function delData<I extends DataInput, E, O>(
): T.Effect<RequestEnv, HttpError<E>, Response<O>>
export function delForm<E, O>(
): 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>,
): <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>,
// 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,
// Get method as string (useful in middleware dev)
export function getMethodAsString(method: Method): string