guardz-axios
v1.11.3
Published
Type-safe HTTP client built on top of Axios with runtime validation using guardz. Part of the guardz ecosystem for comprehensive TypeScript type safety.
Maintainers
Readme
Guardz Axios
A type-safe HTTP client built on top of Axios with runtime validation using guardz 1.11.3+.
What This Library Does
Guardz Axios transforms your HTTP requests into type-safe operations that automatically validate response data at runtime. Instead of manually checking response types and handling errors, you get:
- Automatic runtime validation of all response data
- Type-safe results with discriminated unions
- Comprehensive error handling with detailed feedback
- Multiple API patterns for different use cases
- Retry logic with configurable backoff strategies
- Tolerance mode for graceful degradation
Installation
npm install guardz-axios guardz axiosGuardz Ecosystem
This library is part of the Guardz ecosystem - a comprehensive TypeScript type guard solution designed to bring runtime type safety to your applications:
🌟 Core Components
guardz - Core Type Guard Library
The foundation of the ecosystem, providing 50+ built-in type guards and utilities:
npm install guardzFeatures:
- 50+ built-in type guards for all JavaScript types
- Composite type guards for complex validation
- Custom type guard creation with
isType()andisOneOf() - Array and object validation with
isArrayOf()andisRecord() - Optional and nullable types with
isOptional()andisNullable() - Union types with
isUnion()andisOneOf() - Intersection types with
isIntersection()
Example:
import { isType, isString, isNumber, isOptional, isArrayOf } from "guardz";
interface User {
id: number;
name: string;
email?: string;
tags: string[];
}
const isUser = isType<User>({
id: isNumber,
name: isString,
email: isOptional(isString),
tags: isArrayOf(isString),
});
// Runtime validation
if (isUser(data)) {
// data is guaranteed to be User type
console.log(data.name); // Type-safe access
}guardz-generator - Automatic Type Guard Generation
Automatically generate type guards from your TypeScript interfaces and types:
npm install guardz-generatorFeatures:
- Automatic generation from TypeScript interfaces
- CLI tool for batch processing
- Watch mode for development
- Custom templates for specialized guards
- Integration with build tools and IDEs
Usage:
# Generate guards from all TypeScript files
npx guardz-generator generate "src/**/*.ts"
# Watch mode for development
npx guardz-generator watch "src/**/*.ts"
# Generate guards for specific files
npx guardz-generator generate src/types/user.ts src/types/post.tsGenerated Output:
// Auto-generated from interface User
export const isUser = isType<User>({
id: isNumber,
name: isString,
email: isOptional(isString),
tags: isArrayOf(isString),
});
// Auto-generated from interface Post
export const isPost = isType<Post>({
id: isNumber,
title: isString,
content: isString,
author: isUser,
publishedAt: isOptional(isString),
});guardz-axios - Type-Safe HTTP Client
This package - a type-safe HTTP client with runtime validation:
npm install guardz-axiosFeatures:
- Runtime validation of all HTTP responses
- Type-safe results with discriminated unions
- Multiple API patterns (curried, fluent, configuration-first)
- Retry logic with exponential backoff
- Tolerance mode for graceful degradation
- Comprehensive error handling
Example:
import { safeGet } from "guardz-axios";
import { isUser } from "./guards"; // Generated by guardz-generator
const result = await safeGet({ guard: isUser })("/users/1");
if (result.status === Status.SUCCESS) {
console.log(result.data.name); // Type-safe access
} else {
console.error(`Error: ${result.message}`);
}guardz-event - Type-Safe Event Handling
Type-safe event handling with runtime validation:
npm install guardz-eventFeatures:
- Type-safe event emitters with runtime validation
- Event type guards for payload validation
- Event listeners with automatic type inference
- Event middleware for transformation and filtering
- Event history and replay capabilities
- Integration with Node.js EventEmitter and browser events
Example:
import { createTypedEventEmitter } from "guardz-event";
import { isType, isString, isNumber } from "guardz";
interface UserCreatedEvent {
userId: number;
userName: string;
}
const isUserCreatedEvent = isType<UserCreatedEvent>({
userId: isNumber,
userName: isString,
});
const emitter = createTypedEventEmitter({
"user:created": isUserCreatedEvent,
});
// Type-safe event emission
emitter.emit("user:created", { userId: 1, userName: "John" });
// Type-safe event listening
emitter.on("user:created", (event) => {
console.log(`User created: ${event.userName} (ID: ${event.userId})`);
// event is fully typed as UserCreatedEvent
});🔄 Ecosystem Integration
The guardz ecosystem components work seamlessly together:
// 1. Define your types
interface User {
id: number;
name: string;
email: string;
}
// 2. Generate type guards automatically
// npx guardz-generator generate "src/**/*.ts"
// This creates: src/guards/user.guards.ts
// 3. Use in HTTP requests
import { safeGet } from "guardz-axios";
import { isUser } from "./guards/user.guards";
const result = await safeGet({ guard: isUser })("/users/1");
// 4. Use in event handling
import { createTypedEventEmitter } from "guardz-event";
const emitter = createTypedEventEmitter({
"user:updated": isUser,
});
emitter.on("user:updated", (user) => {
// user is fully typed as User
console.log(`User updated: ${user.name}`);
});🚀 Getting Started with the Ecosystem
Install the core packages:
npm install guardz guardz-generator guardz-axios guardz-eventSet up automatic type guard generation:
# Add to package.json scripts "scripts": { "generate:guards": "guardz-generator generate \"src/**/*.ts\"", "watch:guards": "guardz-generator watch \"src/**/*.ts\"" }Generate guards from your types:
npm run generate:guardsUse in your application:
import { safeGet } from "guardz-axios"; import { createTypedEventEmitter } from "guardz-event"; import { isUser, isPost } from "./guards"; // Auto-generated // Type-safe HTTP requests const userResult = await safeGet({ guard: isUser })("/users/1"); // Type-safe event handling const emitter = createTypedEventEmitter({ "user:created": isUser, "post:published": isPost, });
📚 Documentation Links
- guardz Documentation - Core type guard library
- guardz-generator Documentation - Automatic type guard generation
- guardz-axios Documentation - Type-safe HTTP client (this package)
- guardz-event Documentation - Type-safe event handling
🎯 Use Cases
The guardz ecosystem is perfect for:
- API Development - Validate all incoming/outgoing data
- Event-Driven Applications - Type-safe event handling
- Microservices - Ensure data consistency across services
- Frontend Applications - Validate API responses and user input
- Backend Services - Validate database queries and external API calls
- Testing - Generate type-safe test data and mocks
🔧 Development Workflow
- Define your types in TypeScript interfaces
- Generate type guards automatically with guardz-generator
- Use guards in your HTTP requests with guardz-axios
- Handle events type-safely with guardz-event
- Enjoy full runtime type safety across your application
Quick Start
import { safeGet } from "guardz-axios";
import { isType, isString, isNumber } from "guardz";
import { Status } from "guardz-axios";
interface User {
id: number;
name: string;
email: string;
}
const isUser = isType<User>({
id: isNumber,
name: isString,
email: isString,
});
const result = await safeGet({ guard: isUser })("/users/1");
if (result.status === Status.SUCCESS) {
console.log("User:", result.data); // Fully typed as User
} else {
console.log("Error:", result.code, result.message);
}How It Works
Result Type
The library uses a discriminated union for type-safe results:
type SafeRequestResult<T> =
| { status: Status.SUCCESS; data: T }
| { status: Status.ERROR; code: number; message: string };Success Response
When the request succeeds and validation passes:
{
status: Status.SUCCESS,
data: T // Your validated data
}Error Response
When the request fails or validation fails:
{
status: Status.ERROR,
code: number, // HTTP status code or 500 for validation errors
message: string // Human-readable error message
}Error Types and Messages
1. Validation Errors (Code: 500)
When response data doesn't match the expected type:
{
status: Status.ERROR,
code: 500,
message: "Response data validation failed: Expected userData.id (\"1\") to be \"number\""
}2. Network Errors (Code: 500)
When network requests fail:
{
status: Status.ERROR,
code: 500,
message: "Network Error"
}3. Timeout Errors (Code: 500)
When requests timeout:
{
status: Status.ERROR,
code: 500,
message: "timeout of 5000ms exceeded"
}4. HTTP Status Errors
When the server returns error status codes:
{
status: Status.ERROR,
code: 404,
message: "Not Found"
}Error Handling Examples
Basic Error Handling
const result = await safeGet({ guard: isUser })("/users/1");
if (result.status === Status.SUCCESS) {
console.log("Success:", result.data);
} else {
console.error(`Request failed: ${result.message}`);
console.error(`Status: ${result.code}`);
}Tolerance Mode Error Handling
const result = await safeGet({
guard: isUser,
tolerance: true,
onError: (error, context) => {
console.warn(`Validation warning: ${error}`);
console.warn(`Context: ${context.url} (${context.method})`);
},
})("/users/1");
if (result.status === Status.SUCCESS) {
console.log("Data is valid:", result.data);
} else {
console.log("Request failed:", result.message);
}Retry with Error Handling
const result = await safeRequest({
url: "/users/1",
method: "GET",
guard: isUser,
retry: {
attempts: 3,
delay: 1000,
backoff: "exponential",
},
});
if (result.status === Status.SUCCESS) {
console.log("Request succeeded:", result.data);
} else {
console.error(`Request failed: ${result.message}`);
}Error Handling
The library provides detailed error messages and multiple error handling strategies:
Best Practices for Error Handling
Always check the status first:
if (result.status === Status.SUCCESS) { // Handle success } else { // Handle error }Handle different error types appropriately:
if (result.status === Status.ERROR) { switch (result.code) { case 404: // Handle not found break; case 500: // Handle server errors break; } }Use tolerance mode for graceful degradation:
const result = await safeGet({ guard: isUser, tolerance: true, })("/users/1"); if (result.status === Status.SUCCESS) { // Use data confidently } else { // Handle error }Use detailed error messages:
const result = await safeGet({ guard: isUser, onValidationError: (errors) => { errors.forEach((error) => { console.error(`Validation failed: ${error}`); }); }, })("/users/1");
API Patterns
Pattern 1: Curried Functions
Simple, functional approach:
import { safeGet, safePost } from "guardz-axios";
const getUser = safeGet({ guard: isUser });
const createUser = safePost({ guard: isUser });
const result = await getUser("/users/1");
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Pattern 2: Configuration-first
Full control over request configuration:
import { safeRequest } from "guardz-axios";
const result = await safeRequest({
url: "/users/1",
method: "GET",
guard: isUser,
timeout: 5000,
retry: {
attempts: 3,
delay: 1000,
backoff: "exponential",
},
});
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Pattern 3: Fluent API Builder
Chainable, readable API:
import { safe } from "guardz-axios";
const result = await safe()
.get("/users/1")
.guard(isUser)
.timeout(5000)
.retry({ attempts: 3, delay: 1000 })
.execute();
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Pattern 4: Context/Provider
Shared configuration across requests:
import { createSafeApiContext } from "guardz-axios";
const api = createSafeApiContext({
baseURL: "https://api.example.com",
timeout: 5000,
defaultTolerance: true,
});
const result = await api.get("/users/1", { guard: isUser });
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Advanced Features
Tolerance Mode
Handle invalid responses gracefully:
const result = await safeGet({
guard: isUser,
tolerance: true,
onError: (error, context) => {
console.warn(`Validation warning: ${error}`);
},
})("/users/1");
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
} else {
console.log("Request failed:", result.message);
}Retry Logic
Automatic retry with exponential backoff:
const result = await safeGet({
guard: isUser,
retry: {
attempts: 3,
delay: 1000,
backoff: "exponential",
retryOn: (error) => {
// Custom retry logic
return error.message.includes("network");
},
},
})("/users/1");
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Custom Axios Instance
Use your own Axios configuration:
import axios from "axios";
const customAxios = axios.create({
baseURL: "https://api.example.com",
headers: { Authorization: "Bearer token" },
});
const result = await safeGet({
guard: isUser,
axiosInstance: customAxios,
})("/users/1");
if (result.status === Status.SUCCESS) {
console.log("User:", result.data);
}Type Safety
Automatic Type Inference
const result = await safeGet({ guard: isUser })("/users/1");
if (result.status === Status.SUCCESS) {
// TypeScript knows this is User
console.log(result.data.name); // ✅ Type-safe
console.log(result.data.email); // ✅ Type-safe
}Type Guards
import { isType, isString, isNumber } from "guardz";
const isUser = isType<User>({
id: isNumber,
name: isString,
email: isString,
});
const result = await safeGet({ guard: isUser })("/users/1");💡 Tip: Use guardz-generator to automatically generate type guards from your TypeScript interfaces instead of writing them manually!
Examples
See the examples directory for complete working examples:
Sponsors
Support this project by becoming a sponsor:
License
MIT
