Skip to content

Instantly share code, notes, and snippets.

@WomB0ComB0
Created October 13, 2025 23:00
Show Gist options
  • Select an option

  • Save WomB0ComB0/e6555ab3fd2755f4b59008ee779442dc to your computer and use it in GitHub Desktop.

Select an option

Save WomB0ComB0/e6555ab3fd2755f4b59008ee779442dc to your computer and use it in GitHub Desktop.
effect-schema-fetcher - Enhanced with AI-generated documentation
"use strict";
import { HttpClient, HttpClientRequest } from "@effect/platform";
import { Schema, ParseResult } from "effect";
import { Duration, Effect, pipe, Schedule } from "effect";
declare const EMPTY = "";
/**
* @module effect-fetcher
*
* Type-safe, Effect-based HTTP data fetching utilities with Effect Schema runtime validation.
*
* This module provides a generic, type-safe fetcher utility and convenience functions for all HTTP verbs.
* It supports retries, timeouts, custom headers, error handling, runtime validation with Effect Schema,
* and integrates with Effect for composable async flows.
*
* ## Features
* - Type-safe HTTP requests for all verbs (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD)
* - Runtime type validation with effect/Schema
* - Effect-based error handling and retry logic
* - Customizable headers, timeouts, and retry strategies
* - Rich error context via FetcherError and ValidationError
* - Query parameter serialization
* - Designed for use with Effect and React Query
*
* @see FetcherError
* @see ValidationError
* @see fetcher
* @see get
* @see post
* @see put
* @see patch
* @see del
* @see options
* @see head
*
* @example
* ```ts
* import { get } from './effect-fetcher';
* import { Effect, Schema } from 'effect';
*
* const UserSchema = Schema.Struct({
* id: Schema.Number,
* name: Schema.String,
* email: Schema.String
* });
*
* const UsersSchema = Schema.Struct({
* users: Schema.Array(UserSchema)
* });
*
* const effect = get('/api/users', {
* retries: 2,
* schema: UsersSchema
* });
* const result = await Effect.runPromise(effect);
* console.log(result.users); // Fully typed and validated!
* ```
*/
// HTTP Method type definition
const HttpMethod = Schema.Literal(
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS",
"HEAD",
);
/**
* Represents all supported HTTP methods for the fetcher utility.
*/
type HttpMethod = Schema.Schema.Type<typeof HttpMethod>;
// Query parameters type definition
const QueryParams = Schema.Record({
key: Schema.String,
value: Schema.Union(
Schema.String,
Schema.Number,
Schema.Boolean,
Schema.Undefined,
Schema.Null,
Schema.Array(Schema.Union(Schema.String, Schema.Number, Schema.Boolean)),
),
});
/**
* Represents a type-safe map of query parameters.
* Each value can be a string, number, boolean, null, undefined, or an array of those types.
*/
export type QueryParams = Schema.Schema.Type<typeof QueryParams>;
// Request body type definition
const RequestBody = Schema.Union(
Schema.Record({ key: Schema.String, value: Schema.Unknown }),
Schema.Array(Schema.Unknown),
Schema.String,
Schema.Number,
Schema.Boolean,
Schema.Null,
);
/**
* Represents a type-safe request body for HTTP methods that support a body.
* Can be an object, array, string, number, boolean, or null.
*/
type RequestBody = Schema.Schema.Type<typeof RequestBody>;
// Headers type definition
const Headers = Schema.Record({ key: Schema.String, value: Schema.String });
/**
* Represents HTTP headers as key-value string pairs.
*/
type Headers = Schema.Schema.Type<typeof Headers>;
/**
* Configuration options for the fetcher utility.
*/
export interface FetcherOptions<T = unknown> {
/** Number of times to retry the request on failure */
retries?: number;
/** Delay in milliseconds between retries */
retryDelay?: number;
/** Optional callback invoked on error */
onError?: (error: unknown) => void;
/** Timeout in milliseconds for the request */
timeout?: number;
/** Additional headers to include in the request */
headers?: Record<string, string>;
/** Effect/Schema for runtime validation of the response */
schema?: Schema.Schema<T, any, never>;
/** Abortsignal */
signal?: AbortSignal;
}
/**
* Custom error class for validation-specific errors.
* Includes detailed validation problems from effect/Schema.
*/
export class ValidationError extends Error {
constructor(
message: string,
public readonly url: string,
public readonly problems: string,
public readonly responseData: unknown,
public readonly attempt?: number,
) {
super(message);
this.name = "ValidationError";
Object.setPrototypeOf(this, ValidationError.prototype);
}
[Symbol.toStringTag] = "ValidationError";
[Symbol.for("nodejs.util.inspect.custom")]() {
return this.toString();
}
[Symbol.for("Deno.customInspect")]() {
return this.toString();
}
[Symbol.for("react.element")]() {
return this.toString();
}
toString(): string {
return `ValidationError: ${this.message} (URL: ${this.url}${this.attempt ? `, Attempt: ${this.attempt}` : ""})`;
}
/**
* Get a formatted string of all validation problems
*/
getProblemsString(): string {
return this.problems;
}
}
/**
* Custom error class for fetcher-specific errors.
* Includes additional context such as the request URL, HTTP status, response data, and attempt number.
*/
export class FetcherError extends Error {
constructor(
message: string,
public readonly url: string,
public readonly status?: number,
public readonly responseData?: unknown,
public readonly attempt?: number,
) {
super(message);
this.name = "FetcherError";
Object.setPrototypeOf(this, FetcherError.prototype);
}
[Symbol.toStringTag] = "FetcherError";
[Symbol.for("nodejs.util.inspect.custom")]() {
return this.toString();
}
[Symbol.for("Deno.customInspect")]() {
return this.toString();
}
[Symbol.for("react.element")]() {
return this.toString();
}
toString(): string {
return `FetcherError: ${this.message} (URL: ${this.url}${this.status ? `, Status: ${this.status}` : ""}${this.attempt ? `, Attempt: ${this.attempt}` : ""})`;
}
}
// --- Overloaded function signatures for type safety with effect/Schema ---
/**
* Performs a GET request with optional schema validation.
*/
export function fetcher<T = unknown>(
input: string,
method?: "GET",
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
/**
* Performs a GET request with Effect schema validation and automatic type inference.
*/
export function fetcher<S extends Schema.Schema<any, any, never>>(
input: string,
method: "GET",
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
/**
* Performs a POST, PUT, or PATCH request with a request body and optional schema validation.
*/
export function fetcher<T = unknown>(
input: string,
method: "POST" | "PUT" | "PATCH",
options?: FetcherOptions<T>,
params?: QueryParams,
body?: RequestBody,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
/**
* Performs a POST, PUT, or PATCH request with Effect schema validation and automatic type inference.
*/
export function fetcher<S extends Schema.Schema<any, any, never>>(
input: string,
method: "POST" | "PUT" | "PATCH",
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
body?: RequestBody,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
/**
* Performs a DELETE, OPTIONS, or HEAD request with optional schema validation.
*/
export function fetcher<T = unknown>(
input: string,
method: "DELETE" | "OPTIONS" | "HEAD",
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
/**
* Enhanced data fetching utility with type safety, Effect Schema validation, and Effect-based error handling.
* Supports retries, timeouts, custom headers, runtime validation, and error handling.
*
* @template T
* @param input The URL to request
* @param method The HTTP method to use
* @param options Optional fetcher configuration including Effect Schema
* @param params Optional query parameters
* @param body Optional request body (for methods that support it)
* @returns An Effect that resolves to the validated response data of type T
*
* @example
* ```ts
* import { Schema } from 'effect';
*
* const UserSchema = Schema.Struct({
* id: Schema.Number,
* name: Schema.String,
* email: Schema.String
* });
*
* const effect = pipe(
* get("/api/user/123", {
* retries: 1,
* retryDelay: 1_000,
* timeout: 10_000,
* schema: UserSchema,
* onError: (error) => {
* if (error instanceof ValidationError) {
* console.error("Validation failed:", error.getProblemsString())
* }
* }
* }),
* Effect.provide(HttpClient.layer)
* )
* ```
*/
export function fetcher<T = unknown>(
input: string,
method: HttpMethod = "GET",
options: FetcherOptions<T> = {},
params?: QueryParams,
body?: RequestBody,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient> {
const {
retries = 0,
retryDelay = 1_000,
onError,
timeout = 10_000,
headers = {},
schema,
} = options;
/**
* Builds a query string from the provided query parameters.
*/
const buildQueryString = (params?: QueryParams): string => {
if (!params) return EMPTY;
const urlParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value == null) return;
if (Array.isArray(value)) {
value
.filter((item): item is string | number | boolean => item != null)
.forEach((item) => urlParams.append(key, String(item)));
} else {
urlParams.append(key, String(value));
}
});
return urlParams.toString();
};
const url = params ? `${input}?${buildQueryString(params)}` : input;
/**
* Builds a type-safe HttpClientRequest for the given method and URL.
*/
const buildRequest = (
method: HttpMethod,
url: string,
): HttpClientRequest.HttpClientRequest => {
switch (method) {
case "GET":
return HttpClientRequest.get(url);
case "POST":
return HttpClientRequest.post(url);
case "PUT":
return HttpClientRequest.put(url);
case "PATCH":
return HttpClientRequest.patch(url);
case "DELETE":
return HttpClientRequest.del(url);
case "OPTIONS":
return HttpClientRequest.options(url);
case "HEAD":
return HttpClientRequest.head(url);
}
};
/**
* Validates response data using the provided Effect schema.
*/
const validateResponse = (
data: unknown,
attempt: number,
): Effect.Effect<T, ValidationError, never> => {
if (!schema) {
return Effect.succeed(data as T);
}
const result = Schema.decodeUnknownEither(schema)(data);
if (result._tag === "Left") {
const problems = ParseResult.TreeFormatter.formatIssueSync(result.left.issue);
const validationError = new ValidationError(
"Response validation failed",
url,
problems,
data,
attempt,
);
if (onError) onError(validationError);
return Effect.fail(validationError);
}
return Effect.succeed(result.right as T);
};
return Effect.gen(function* () {
const client = yield* HttpClient.HttpClient;
let attempt = 0;
// Build the request object
let req = buildRequest(method, url);
req = HttpClientRequest.setHeaders(headers)(req);
// Add body for methods that support it with proper error handling
if (
body != null &&
(method === "POST" || method === "PUT" || method === "PATCH")
) {
req = yield* pipe(
HttpClientRequest.bodyJson(body)(req),
Effect.mapError(
(error) =>
new FetcherError(
`Failed to serialize request body: ${error instanceof Error ? error.message : String(error)}`,
url,
undefined,
undefined,
attempt,
),
),
);
}
/**
* Wraps an Effect with a timeout, converting timeout errors to FetcherError.
*/
const withTimeout = <A, E, R>(
eff: Effect.Effect<A, E, R>,
): Effect.Effect<A, FetcherError | E, R> =>
pipe(
eff,
Effect.timeoutFail({
duration: Duration.millis(timeout),
onTimeout: () =>
new FetcherError(
"Request timed out",
url,
undefined,
undefined,
attempt,
),
}),
);
// Retry schedule with exponential backoff and maximum number of retries
const retrySchedule = Schedule.intersect(
Schedule.exponential(Duration.millis(retryDelay)),
Schedule.recurs(retries),
);
/**
* Executes the HTTP request, handling errors, HTTP status, response parsing, and validation.
*/
const executeRequest = Effect.gen(function* () {
attempt++;
// Execute the HTTP request and handle network/transport errors
const response = yield* pipe(
client.execute(req),
withTimeout,
Effect.mapError((error) => {
if (error instanceof FetcherError) return error;
return new FetcherError(
error instanceof Error ? error.message : String(error),
url,
undefined,
undefined,
attempt,
);
}),
);
// Check for HTTP errors (non-2xx status codes)
if (response.status < 200 || response.status >= 300) {
const errorData = yield* pipe(
response.json,
Effect.catchAll(() => Effect.succeed(undefined)),
);
const error = new FetcherError(
`HTTP ${response.status}: ${response.text || "Request failed"}`,
url,
response.status,
errorData,
attempt,
);
if (onError) onError(error);
return yield* Effect.fail(error);
}
// Parse response data as JSON, with fallback to text and detailed error reporting
const rawData = yield* pipe(
response.json,
Effect.catchAll((error) => {
// Try to get response text for better debugging
return pipe(
response.text,
Effect.flatMap((text) => {
const errorMessage = `Failed to parse JSON response. Status: ${response.status}, Content-Type: ${response.headers["Content-Type"] || "unknown"}, Body: ${text.slice(0, 200)}${text.length > 200 ? "..." : ""}`;
return Effect.fail(
new FetcherError(
errorMessage,
url,
response.status,
{ originalError: error, responseText: text },
attempt,
),
);
}),
Effect.catchAll(() =>
Effect.fail(
new FetcherError(
`Failed to parse response: ${error instanceof Error ? error.message : String(error)}`,
url,
response.status,
undefined,
attempt,
),
),
),
);
}),
);
// Validate the response data using Effect Schema if provided
const validatedData = yield* validateResponse(rawData, attempt);
return validatedData;
});
// Run the request with retry and error handling
return yield* pipe(
executeRequest,
Effect.retry(retrySchedule),
Effect.catchAll((error) => {
if (error instanceof FetcherError || error instanceof ValidationError) {
if (onError) onError(error);
return Effect.fail(error);
}
const fetcherError = new FetcherError(
String(error),
url,
undefined,
undefined,
attempt,
);
if (onError) onError(fetcherError);
return Effect.fail(fetcherError);
}),
);
});
}
/**
* Convenience function for GET requests with optional schema validation.
*
* @example
* ```ts
* import { Schema } from 'effect';
*
* const UserSchema = Schema.Struct({
* id: Schema.Number,
* name: Schema.String,
* email: Schema.String
* });
*
* const effect = get("/api/user", { schema: UserSchema });
* const user = await Effect.runPromise(effect); // Fully typed and validated!
* ```
*/
export function get<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function get<S extends Schema.Schema<any, any, never>>(
url: string,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function get<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "GET", options, params);
}
/**
* Convenience function for POST requests with optional schema validation.
*/
export function post<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function post<S extends Schema.Schema<any, any, never>>(
url: string,
body: RequestBody,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function post<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "POST", options, params, body);
}
/**
* Convenience function for PUT requests with optional schema validation.
*/
export function put<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function put<S extends Schema.Schema<any, any, never>>(
url: string,
body: RequestBody,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function put<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "PUT", options, params, body);
}
/**
* Convenience function for PATCH requests with optional schema validation.
*/
export function patch<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function patch<S extends Schema.Schema<any, any, never>>(
url: string,
body: RequestBody,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function patch<T = unknown>(
url: string,
body?: RequestBody,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "PATCH", options, params, body);
}
/**
* Convenience function for DELETE requests with optional schema validation.
*/
export function del<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function del<S extends Schema.Schema<any, any, never>>(
url: string,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function del<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "DELETE", options, params);
}
/**
* Convenience function for OPTIONS requests with optional schema validation.
*/
export function options<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function options<S extends Schema.Schema<any, any, never>>(
url: string,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function options<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "OPTIONS", options, params);
}
/**
* Convenience function for HEAD requests with optional schema validation.
*/
export function head<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
): Effect.Effect<T, FetcherError | ValidationError, HttpClient.HttpClient>;
export function head<S extends Schema.Schema<any, any, never>>(
url: string,
options: FetcherOptions<Schema.Schema.Type<S>> & { schema: S },
params?: QueryParams,
): Effect.Effect<
Schema.Schema.Type<S>,
FetcherError | ValidationError,
HttpClient.HttpClient
>;
export function head<T = unknown>(
url: string,
options?: FetcherOptions<T>,
params?: QueryParams,
) {
return fetcher<T>(url, "HEAD", options, params);
}
// --- Utility functions for common schema patterns ---
/**
* Helper function to create a paginated response schema.
*
* @example
* ```ts
* const UserSchema = Schema.Struct({
* id: Schema.Number,
* name: Schema.String
* });
*
* const PaginatedUsersSchema = createPaginatedSchema(UserSchema);
*
* const effect = get("/api/users", {
* schema: PaginatedUsersSchema
* });
* ```
*/
export const createPaginatedSchema = <T, I>(
itemSchema: Schema.Schema<T, I, never>,
) => {
return Schema.Struct({
data: Schema.Array(itemSchema),
pagination: Schema.Struct({
page: Schema.Number,
pageSize: Schema.Number,
total: Schema.Number,
totalPages: Schema.Number,
}),
});
};
/**
* Helper function to create an API response wrapper schema.
*
* @example
* ```ts
* const UserSchema = Schema.Struct({
* id: Schema.Number,
* name: Schema.String
* });
*
* const WrappedUserSchema = createApiResponseSchema(UserSchema);
*
* const effect = get("/api/user/123", {
* schema: WrappedUserSchema
* });
* ```
*/
export const createApiResponseSchema = <T, I>(
dataSchema: Schema.Schema<T, I, never>,
) => {
return Schema.Struct({
success: Schema.Boolean,
data: dataSchema,
message: Schema.optional(Schema.String),
errors: Schema.optional(Schema.Array(Schema.String)),
});
};

effect-schema-fetcher.ts

File Type: TS
Lines: 848
Size: 21.3 KB
Generated: 10/13/2025, 7:00:22 PM


Code Analysis: effect-schema-fetcher.ts

This TypeScript file, effect-schema-fetcher.ts, provides a type-safe, Effect-based HTTP data fetching utility with runtime validation using Effect Schema. It offers a generic fetcher function and convenience functions for common HTTP verbs (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD). The module is designed to integrate seamlessly with Effect for composable asynchronous flows, providing features like retries, timeouts, custom headers, and detailed error handling.

Key Components and Functionality:

  1. Type Definitions:

    • HttpMethod: Defines a schema for valid HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) using Schema.Literal.
    • QueryParams: Defines a schema for query parameters as a record of string keys and values that can be strings, numbers, booleans, null, undefined, or arrays of these types.
    • RequestBody: Defines a schema for request bodies, allowing objects, arrays, strings, numbers, booleans, and null values.
    • Headers: Defines a schema for HTTP headers as a record of string key-value pairs.
    • FetcherOptions: An interface that configures the fetcher function, including options for retries, retry delay, error handling, timeout, custom headers, Effect Schema for runtime validation (schema), and an AbortSignal.
  2. Error Handling:

    • ValidationError: A custom error class extending Error that represents validation failures. It includes the URL, validation problems (as a string), the response data, and the attempt number. It provides a getProblemsString method to retrieve a formatted string of validation errors.
    • FetcherError: A custom error class extending Error that represents general fetcher errors. It includes the URL, HTTP status code, response data, and the attempt number.
  3. fetcher Function (Overloaded):

    • The core of the module, the fetcher function, is overloaded to provide type safety and flexibility. It supports different HTTP methods and optional schema validation.
    • The function signatures handle cases with and without a schema, allowing for automatic type inference when a schema is provided.
    • The function returns an Effect that encapsulates the asynchronous operation, making it composable with other Effect-based code.
  4. HTTP Verb Convenience Functions:

    • The file likely includes convenience functions like get, post, put, patch, del, options, and head that are wrappers around the fetcher function, pre-configured for specific HTTP methods. (The provided snippet is truncated, so these are not visible, but the documentation suggests their existence.)

Architecture and Design:

  • Effect Integration: The module is built around the Effect library, leveraging its features for asynchronous programming, error handling, and resource management.
  • Type Safety: The use of TypeScript and Effect Schema ensures type safety throughout the data fetching process, from request construction to response validation.
  • Schema-Driven Validation: Effect Schema is used for runtime validation of the response data, ensuring that the data conforms to the expected structure and types.
  • Composable: The fetcher function returns an Effect, allowing developers to compose data fetching operations with other Effect-based logic.
  • Error Handling: Custom error classes (ValidationError and FetcherError) provide detailed information about errors that occur during the data fetching process.

Practical Usage:

The effect-schema-fetcher.ts file provides a robust and type-safe way to perform HTTP requests in Effect-based applications. It simplifies data fetching by handling retries, timeouts, and validation, while providing detailed error information.

Example Usage (from the documentation):

import { get } from './effect-fetcher';
import { Effect, Schema } from 'effect';

const UserSchema = Schema.Struct({
  id: Schema.Number,
  name: Schema.String,
  email: Schema.String
});

const UsersSchema = Schema.Struct({
  users: Schema.Array(UserSchema)
});

const effect = get('/api/users', {
  retries: 2,
  schema: UsersSchema
});
const result = await Effect.runPromise(effect);
console.log(result.users); // Fully typed and validated!

This example demonstrates how to use the get function to fetch data from an API endpoint and validate the response against a schema. The Effect.runPromise function executes the Effect and returns a promise that resolves with the validated data.

Potential Improvements:

  • Complete HTTP Verb Functions: Ensure that all HTTP verb convenience functions (e.g., get, post, put, etc.) are implemented and well-documented.
  • Request Body Serialization: Add support for automatically serializing request bodies to JSON or other formats.
  • Customizable HTTP Client: Allow users to provide their own HttpClient instance for greater control over the underlying HTTP client.
  • Middleware Support: Consider adding support for middleware to intercept and modify requests and responses.
  • Documentation: Provide comprehensive documentation for all functions, types, and options.

In summary, effect-schema-fetcher.ts is a well-designed module that provides a type-safe and composable way to perform HTTP requests with runtime validation using Effect and Effect Schema. It simplifies data fetching and error handling in Effect-based applications.


Description generated using AI analysis

@WomB0ComB0
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment