diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a708080..53790b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ All types of contributions are welcome: ## Repository Layout -SmoothAPI is a dual-language API resilience and fault-tolerance library. The workspace is organized as follows: +SmoothAPI is a dual-language API robustness and fault-tolerance library. The workspace is organized as follows: ```text smooth-api/ diff --git a/examples/express/README.md b/examples/express/README.md index 470bdf1..b63f178 100644 --- a/examples/express/README.md +++ b/examples/express/README.md @@ -1,6 +1,6 @@ # SmoothAPI Express Example -This example demonstrates how to integrate SmoothAPI into an Express.js application to make outbound API requests more resilient using retries, circuit breakers, and fallback responses. +This example demonstrates how to integrate SmoothAPI into an Express.js application to make outbound API requests more robust using retries, circuit breakers, and fallback responses. ## Features diff --git a/examples/fastapi/README.md b/examples/fastapi/README.md index bdf87df..0ff705b 100644 --- a/examples/fastapi/README.md +++ b/examples/fastapi/README.md @@ -1,6 +1,6 @@ # SmoothAPI FastAPI Example -This example demonstrates how to integrate **SmoothAPI** into a FastAPI application to make outbound API requests more resilient using retries, circuit breakers, and fallback responses. +This example demonstrates how to integrate **SmoothAPI** into a FastAPI application to make outbound API requests more robust using retries, circuit breakers, and fallback responses. ## Features diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index 11cd92d..43a659f 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -2,36 +2,39 @@ A minimal [Next.js](https://nextjs.org/) (App Router, TypeScript) example showing how to use `@codingaryan/smoothapi` to make calls to an unreliable third-party -API resilient. Real upstream services fail intermittently, rate-limit, and go -down; this example wraps `fetch` with retries, a fallback value, and a circuit -breaker so your route handlers degrade gracefully instead of erroring out. +API robust. Real upstream services fail intermittently, rate-limit, and go +down completely. This example shows how to use SmoothAPI with Next.js App Router API Routes to protect your app. -It demonstrates two route handlers against the project's chaos **sandbox** -server: +## Endpoints in this example -- `/api/resilient` — retry + fallback against `/unstable-data`. -- `/api/circuit-demo` — circuit breaker against `/always-fail`. +- `/api/unstable-data` — simulates an upstream service that fails randomly. +- `/api/always-fail` — simulates an upstream service that is completely down. +- `/api/robust` — retry + fallback against `/unstable-data`. +- `/api/circuit-demo` — trips the circuit breaker against `/always-fail`. ---- +## How it works -## Prerequisites & Setup +### `/api/robust` — retry + fallback -### 1. Start the sandbox server +This route points `createSmoothFetch` at `/unstable-data`, which returns a +503 randomly. -The example calls the chaos sandbox on `http://localhost:3001`. From the repo -root: +1. It catches the 503 error. +2. It backs off exponentially and retries. +3. If it succeeds, you get the data. +4. If it fails 3 times, you get the fallback data: `{ status: "degraded", ... }` -```bash -cd sandbox -npm install -node server.js -``` +### `/api/circuit-demo` — circuit breaker -Leave this terminal running. +This route points `createSmoothFetch` at `/always-fail`, which always returns +a 503 error. -### 2. Run the example +1. The first 3 requests will be retried (and fail). +2. The circuit breaker trips `OPEN`. +3. The 4th and subsequent requests immediately return the fallback without even trying the network! +4. After the cooldown period (10s), it enters `HALF_OPEN` and tests the endpoint again. -In a second terminal: +## Running the example ```bash cd examples/nextjs @@ -39,15 +42,17 @@ npm install npm run dev ``` -Then open [http://localhost:3000](http://localhost:3000) and use the two buttons, -or call the routes directly with the curl commands below. +Then visit `http://localhost:3000` to interact with the API routes. +Alternatively, you can test it directly via curl: + +```bash +curl http://localhost:3000/api/robust +``` --- ## Walkthrough -### `/api/resilient` — retry + fallback - This route points `createResilientFetch` at `/unstable-data`, which returns a mix of `200`, `429`, and `500` responses. With the default retry settings, a retryable status (`429`/`500`/...) causes the client to back off and try again diff --git a/examples/nextjs/app/layout.tsx b/examples/nextjs/app/layout.tsx index 9cabec2..a8ffc0e 100644 --- a/examples/nextjs/app/layout.tsx +++ b/examples/nextjs/app/layout.tsx @@ -1,8 +1,8 @@ import type { ReactNode } from 'react'; export const metadata = { - title: 'smoothapi Next.js example', - description: 'Resilient fetch demo against the chaos sandbox', + title: 'Next.js App Router API Robustness Demo', + description: 'Robust fetch demo against the chaos sandbox', }; export default function RootLayout({ children }: { children: ReactNode }) { diff --git a/examples/nextjs/app/page.tsx b/examples/nextjs/app/page.tsx index f61fabf..625305c 100644 --- a/examples/nextjs/app/page.tsx +++ b/examples/nextjs/app/page.tsx @@ -18,10 +18,12 @@ export default function Home() { return (
-

smoothapi Next.js example

-

Make sure the sandbox server is running on http://localhost:3001.

+

Next.js Robust Fetch Demo

+

+ These buttons call our internal API routes. Those routes then use createSmoothFetch to call the chaos sandbox. Open your browser console and the server terminal to see the logs! +

- +
{output}
diff --git a/website/package.json b/website/package.json index f7e5e47..abbae36 100644 --- a/website/package.json +++ b/website/package.json @@ -9,9 +9,11 @@ "lint": "eslint" }, "dependencies": { + "@types/react-syntax-highlighter": "^15.5.13", "next": "16.2.9", "react": "19.2.4", - "react-dom": "19.2.4" + "react-dom": "19.2.4", + "react-syntax-highlighter": "^16.1.1" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/website/src/app/docs/api/python/page.tsx b/website/src/app/docs/api/python/page.tsx new file mode 100644 index 0000000..79cd3be --- /dev/null +++ b/website/src/app/docs/api/python/page.tsx @@ -0,0 +1,64 @@ +export default function PyApiPage() { + return ( +
+

API Reference: Python

+ +

@smooth_api(config)

+

+ The primary entry point for the Python SDK. A decorator that wraps standard functions or async functions (async def) with self-healing patterns. +

+ +

Arguments

+ + +

Returns

+

+ The wrapped function. It catches exceptions (like requests.exceptions.RequestException or httpx.HTTPError) and manages backoff automatically. +

+ +
+ +

SmoothConfig (Class)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeDefaultDescription
base_delayfloat0.1Initial delay in seconds.
max_retriesint3Maximum number of retry attempts.
failure_thresholdint3Consecutive failures before tripping the circuit.
timeout_msintNoneAutomatically abort requests that take longer than this duration.
+
+
+ ); +} diff --git a/website/src/app/docs/api/typescript/page.tsx b/website/src/app/docs/api/typescript/page.tsx new file mode 100644 index 0000000..d70d9a3 --- /dev/null +++ b/website/src/app/docs/api/typescript/page.tsx @@ -0,0 +1,64 @@ +export default function TSApiPage() { + return ( +
+

API Reference: TypeScript

+ +

createSmoothFetch(config)

+

+ The primary entry point for the TypeScript SDK. Returns a decorated fetch function that implements the self-healing patterns configured. +

+ +

Arguments

+ + +

Returns

+

+ (input: RequestInfo | URL, init?: RequestInit) => Promise<Response | any>: A drop-in replacement for the native fetch API. If a fallback is triggered, it may return the fallback object directly instead of a Response object. +

+ +
+ +

SmoothFetchConfig (Interface)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeRequiredDescription
backoffBackoffConfigYesConfiguration for retries and delays.
circuitBreakerCircuitBreakerConfigYesConfiguration for failure thresholds.
fallbackanyNoData to return when the circuit trips or errors are exhausted.
timeoutMsnumberNoAutomatically abort requests that take longer than this duration (in milliseconds).
+
+
+ ); +} diff --git a/website/src/app/docs/configuration/page.tsx b/website/src/app/docs/configuration/page.tsx new file mode 100644 index 0000000..4e08894 --- /dev/null +++ b/website/src/app/docs/configuration/page.tsx @@ -0,0 +1,78 @@ +export default function ConfigurationPage() { + return ( +
+

Configuration Options

+

+ Customize the behavior of `SmoothAPI` using the following properties when initializing: +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDefaultDescription
backoff.baseDelaynumber100 / 0.1Initial wait time before the first retry (ms in TS, seconds in Python).
backoff.maxRetriesnumber3Maximum number of attempts to resolve the request.
circuitBreaker.failureThresholdnumber3Consecutive failures needed to trip the circuit to OPEN.
circuitBreaker.cooldownMsnumber10000Time to wait (in ms) before entering HALF_OPEN probe state.
fallbackanyundefinedObject returned immediately on an OPEN circuit or client error fallback.
fallbackOnNonRetryablebooleanfalseIf true, returns fallbacks or mock responses on non-retryable client codes (e.g. 404, 401).
onNonRetryableErrorfunctionundefinedCustom callback fired when a client-error occurs. Disables default browser alerts.
timeoutMs / timeout_msnumberundefinedMaximum time (in ms) to wait for a request before aborting and retrying.
deduplicationobjectundefinedConfiguration object to enable coalescing concurrent identical requests.
+
+
+ ); +} diff --git a/website/src/app/docs/features/page.tsx b/website/src/app/docs/features/page.tsx new file mode 100644 index 0000000..a326d50 --- /dev/null +++ b/website/src/app/docs/features/page.tsx @@ -0,0 +1,45 @@ +export default function FeaturesPage() { + return ( +
+

Core Features

+
+ +
+

Exponential Backoff & Full Jitter

+

+ Retries transient failures with exponentially increasing delays. Generates randomized "jitter" boundaries to prevent client requests from hammering recovering endpoints in sync (the "thundering herd" problem). +

+
+ +
+

Per-Domain Circuit Breaker

+

+ Tracks API health in isolated state machines. If a specific domain reaches your failure threshold, the circuit trips to `OPEN`, immediately blocking further connections and returning fallback data. This prevents resource starvation and cascading failures. +

+
+ +
+

Graceful Client-Error Fallbacks

+

+ Support for `fallbackOnNonRetryable` / `fallback_on_non_retryable`. Safely intercept non-retryable client-side HTTP codes (like 404, 403, 400) and return custom data, show browser alerts, or fire custom notification callbacks instead of throwing app-breaking crashes. +

+
+ +
+

Request Deduplication

+

+ Automatically detects multiple concurrent identical requests and coalesces them into a single network call. Once the shared call completes, the response is delivered to all waiting callers, saving bandwidth and compute. +

+
+ +
+

Request Timeouts

+

+ Configurable timeouts to automatically abort requests that hang indefinitely, triggering a retry or failing fast instead of holding connections open endlessly. +

+
+ +
+
+ ); +} diff --git a/website/src/app/docs/guides/express/page.tsx b/website/src/app/docs/guides/express/page.tsx new file mode 100644 index 0000000..5566a74 --- /dev/null +++ b/website/src/app/docs/guides/express/page.tsx @@ -0,0 +1,54 @@ +import CodeBlock from "@/components/CodeBlock"; + +export default function ExpressGuidePage() { + const code = `import express from 'express'; +import { createSmoothFetch } from '@codingaryan/smoothapi'; + +const app = express(); + +// 1. Initialize SmoothAPI outside the route handler. +// This maintains the circuit state across all incoming requests. +const smoothFetch = createSmoothFetch({ + backoff: { + baseDelay: 200, + maxRetries: 4 + }, + circuitBreaker: { + failureThreshold: 5, + cooldownMs: 30000 // 30 second cooldown + }, + fallback: { message: "The database is currently overloaded, please try again later." }, + deduplication: { enabled: true } +}); + +// 2. Use it inside your route handlers +app.get('/api/users', async (req, res) => { + try { + const upstreamRes = await smoothFetch('https://internal-microservice/users'); + const data = await upstreamRes.json(); + + res.json(data); + } catch (error) { + res.status(500).json({ error: "Internal Server Error" }); + } +}); + +app.listen(3000, () => { + console.log('Server is running on port 3000'); +});`; + + return ( +
+

Express.js Integration Guide

+

+ Integrating SmoothAPI into an Express application is straightforward. The most important rule is to instantiate your self-healing fetch function outside of your request handlers. +

+

+ If you instantiate it inside the route, a new Circuit Breaker is created for every incoming request, completely defeating the purpose of the state machine! +

+ +

Example Integration

+ +
+ ); +} diff --git a/website/src/app/docs/guides/fastapi/page.tsx b/website/src/app/docs/guides/fastapi/page.tsx new file mode 100644 index 0000000..ea23940 --- /dev/null +++ b/website/src/app/docs/guides/fastapi/page.tsx @@ -0,0 +1,54 @@ +import CodeBlock from "@/components/CodeBlock"; + +export default function FastAPIGuidePage() { + const code = `import httpx +from fastapi import FastAPI, HTTPException +from smooth_api import smooth_api, SmoothConfig + +app = FastAPI() + +# 1. Initialize configuration at the module level +config = SmoothConfig( + fallback={"status": "degraded", "data": "Upstream is down. Using safe fallback data."}, + fallback_on_non_retryable=True, + timeout_ms=3000 +) + +# 2. Wrap your async service function +@smooth_api(config) +async def fetch_upstream_data(): + async with httpx.AsyncClient() as client: + # We simulate a call that might timeout or return 503 + response = await client.get("https://api.example.com/unstable") + + # httpx requires manually calling raise_for_status() + # so SmoothAPI can catch the Exception! + response.raise_for_status() + + return response.json() + +# 3. Use the self-healing function in your route +@app.get("/data") +async def get_data(): + try: + data = await fetch_upstream_data() + return data + except Exception as e: + raise HTTPException(status_code=500, detail="Service Unavailable")`; + + return ( +
+

FastAPI Integration Guide

+

+ Python's asyncio and web frameworks like FastAPI work beautifully with SmoothAPI. The @smooth_api decorator automatically detects if the function it is wrapping is async or sync and handles it accordingly. +

+ +

Async Integration Example

+

+ Here is a simple example of using httpx and SmoothAPI inside a FastAPI application. +

+ + +
+ ); +} diff --git a/website/src/app/docs/guides/nextjs/page.tsx b/website/src/app/docs/guides/nextjs/page.tsx new file mode 100644 index 0000000..d118b49 --- /dev/null +++ b/website/src/app/docs/guides/nextjs/page.tsx @@ -0,0 +1,53 @@ +import CodeBlock from "@/components/CodeBlock"; + +export default function NextjsGuidePage() { + const code = `import { NextResponse } from 'next/server'; +import { createSmoothFetch } from '@codingaryan/smoothapi'; + +// Create the fetch instance outside the route handler +// so the Circuit Breaker state is preserved across requests! +const smoothFetch = createSmoothFetch({ + backoff: { maxRetries: 3, baseDelay: 100 }, + circuitBreaker: { failureThreshold: 3, cooldownMs: 10000 }, + fallback: { status: "degraded", error: "Upstream service is down." }, + timeoutMs: 2000, +}); + +export async function GET() { + // Use cache: 'no-store' to ensure we hit the actual network + // and bypass Next.js static caching. + const res = await smoothFetch('https://api.example.com/unstable', { + cache: 'no-store' + }); + + const data = await res.json(); + return NextResponse.json(data); +}`; + + return ( +
+

Next.js Integration Guide

+

+ When using SmoothAPI with Next.js, especially the App Router (\`app/\`), there are a few important considerations regarding the built-in \`fetch\` cache. +

+ +

Bypassing the Next.js Cache

+

+ By default, Next.js aggressively caches \`fetch\` requests. When using SmoothAPI's circuit breaker and retry mechanisms, you usually want the requests to be executed dynamically to accurately track the upstream health. +

+ +

Example: Route Handler

+ + +

Client Components vs Server Components

+
    +
  • + Server Components: Initialize \`createSmoothFetch\` at the module level (outside the component) to maintain the state machine. +
  • +
  • + Client Components: You can use \`createSmoothFetch\` securely in the browser, and the circuit breaker state will be maintained per-user for the duration of their session. +
  • +
+
+ ); +} diff --git a/website/src/app/docs/installation/page.tsx b/website/src/app/docs/installation/page.tsx new file mode 100644 index 0000000..d95a97b --- /dev/null +++ b/website/src/app/docs/installation/page.tsx @@ -0,0 +1,23 @@ +import CodeBlock from "@/components/CodeBlock"; + +export default function InstallationPage() { + return ( +
+

Installation

+

+ Choose the appropriate installation method for your language environment. + Ensure you have the latest version of Node.js or Python installed before proceeding. +

+
+
+

TypeScript/JavaScript (NPM):

+ +
+
+

Python (PyPI):

+ +
+
+
+ ); +} diff --git a/website/src/app/docs/introduction/page.tsx b/website/src/app/docs/introduction/page.tsx new file mode 100644 index 0000000..96e6293 --- /dev/null +++ b/website/src/app/docs/introduction/page.tsx @@ -0,0 +1,27 @@ +export default function IntroductionPage() { + return ( +
+

Introduction

+

+ A failing third-party API can bring down your entire application, leading to cascading service failures, degraded user experience, and lost revenue. How do you protect your systems and keep them self-healing, even when downstream dependencies are completely unresponsive or failing? +

+

+ Enter SmoothAPI. SmoothAPI stops third-party API crashes from breaking your app. It wraps your HTTP calls with industry-standard self-healing patterns, catches network errors instantly, spaces out retries so recovering servers can breathe, and serves safe backup data the millisecond a service goes completely dead. +

+
+
+

TypeScript Native

+

+ Dual-environment fetch wrapper supporting Edge, Serverless, and Node.js with built-in type inference. +

+
+
+

Python Native

+

+ Elegant function decorator supporting both sync and async functions, integrating smoothly with requests and httpx. +

+
+
+
+ ); +} diff --git a/website/src/app/docs/layout.tsx b/website/src/app/docs/layout.tsx new file mode 100644 index 0000000..2a559fa --- /dev/null +++ b/website/src/app/docs/layout.tsx @@ -0,0 +1,19 @@ +import Header from "@/components/Header"; +import Sidebar from "@/components/Sidebar"; +import Footer from "@/components/Footer"; +import React from "react"; + +export default function DocsLayout({ children }: { children: React.ReactNode }) { + return ( +
+
+
+ +
+ {children} +
+
+
+
+ ); +} diff --git a/website/src/app/docs/quickstart/page.tsx b/website/src/app/docs/quickstart/page.tsx new file mode 100644 index 0000000..8520e13 --- /dev/null +++ b/website/src/app/docs/quickstart/page.tsx @@ -0,0 +1,83 @@ +"use client"; +import React, { useState } from "react"; +import CodeBlock from "@/components/CodeBlock"; + +export default function QuickstartPage() { + const [codeLang, setCodeLang] = useState<"ts" | "py">("ts"); + + const tsCode = `import { createSmoothFetch } from '@codingaryan/smoothapi'; + +const fetchWithRetry = createSmoothFetch({ + backoff: { + baseDelay: 100, // ms + maxRetries: 3 // retry 3 times + }, + circuitBreaker: { + failureThreshold: 3, // trip OPEN after 3 consecutive errors + cooldownMs: 10000 // stay OPEN for 10 seconds + }, + fallback: { status: "degraded", data: [] } +}); + +// Use it just like normal fetch! +const response = await fetchWithRetry('https://api.example.com/unstable');`; + + const pyCode = `import requests +from smooth_api import smooth_api, SmoothConfig + +config = SmoothConfig( + fallback={"status": "degraded", "data": []}, + fallback_on_non_retryable=True +) + +@smooth_api(config) +def get_data(): + res = requests.get('https://api.example.com/unstable') + res.raise_for_status() # Raise exception so decorator can intercept! + return res.json() + +# Execute safely +data = get_data()`; + + return ( +
+
+

Quickstart Guide

+
+ + +
+
+ + {codeLang === "ts" ? ( +
+

+ Create a custom self-healing fetch instance and use it as a drop-in replacement for native \`fetch\`: +

+ +
+ ) : ( +
+

+ Wrap any request functions using the \`smooth_api\` decorator to catch exceptions and manage backoff: +

+ +
+ )} +
+ ); +} diff --git a/website/src/app/layout.tsx b/website/src/app/layout.tsx index 516b194..9c8f5ea 100644 --- a/website/src/app/layout.tsx +++ b/website/src/app/layout.tsx @@ -8,8 +8,8 @@ const sansFont = Plus_Jakarta_Sans({ }); export const metadata: Metadata = { - title: "SmoothAPI", - description: "Zero-dependency, dual-language API resilience and fault-tolerance library. Implemented natively in TypeScript and Python with exponential backoff and circuit breaking.", + title: "SmoothAPI - Resilient Fetch & Backoff", + description: "Zero-dependency, dual-language API self-healing and fault-tolerance library. Implemented natively in TypeScript and Python with exponential backoff and circuit breaking.", icons: { icon: "/icon.svg", } diff --git a/website/src/app/page.tsx b/website/src/app/page.tsx index 26c9c37..d123ff1 100644 --- a/website/src/app/page.tsx +++ b/website/src/app/page.tsx @@ -1,35 +1,10 @@ "use client"; - -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect } from "react"; import Image from "next/image"; +import Link from "next/link"; +import Footer from "@/components/Footer"; -// Documentation sections type definition -type Section = { - id: string; - title: string; -}; - -const SECTIONS: Section[] = [ - { id: "introduction", title: "Introduction" }, - { id: "installation", title: "Installation" }, - { id: "quickstart", title: "Quickstart" }, - { id: "features", title: "Core Features" }, - { id: "simulator", title: "Interactive Simulator" }, - { id: "configuration", title: "Configuration Options" }, -]; - -/* -// Simulator Logs Type -type SimLog = { - time: string; - type: "info" | "success" | "warning" | "error"; - text: string; -}; -*/ - -export default function Home() { - const [activeSection, setActiveSection] = useState("introduction"); - const [codeLang, setCodeLang] = useState<"ts" | "py">("ts"); +export default function LandingPage() { const [githubStars, setGithubStars] = useState(null); useEffect(() => { @@ -43,156 +18,27 @@ export default function Home() { .catch((err) => console.error("Failed to fetch github stars:", err)); }, []); - /* - // Simulator States - const [circuitState, setCircuitState] = useState<"CLOSED" | "OPEN" | "HALF_OPEN">("CLOSED"); - const [failRate, setFailRate] = useState(0.8); // 80% failure when chaos injected - const [injectChaos, setInjectChaos] = useState(false); - const [failureCount, setFailureCount] = useState(0); - const [simLogs, setSimLogs] = useState([]); - const [isRequesting, setIsRequesting] = useState(false); - const [successCount, setSuccessCount] = useState(0); - */ - - // Auto-scroll-spy ref array - const sectionRefs = useRef<{ [key: string]: HTMLElement | null }>({}); - - useEffect(() => { - const handleScroll = () => { - const scrollPosition = window.scrollY + 160; - - for (const section of SECTIONS) { - const el = sectionRefs.current[section.id]; - if (el) { - const top = el.offsetTop; - const height = el.offsetHeight; - if (scrollPosition >= top && scrollPosition < top + height) { - setActiveSection(section.id); - break; - } - } - } - }; - - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - }, []); - - const scrollTo = (id: string) => { - const el = sectionRefs.current[id]; - if (el) { - const top = el.offsetTop - 80; - window.scrollTo({ top, behavior: "smooth" }); - setActiveSection(id); - } - }; - - /* - // Add Log Helper - const addLog = (text: string, type: "info" | "success" | "warning" | "error" = "info") => { - const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); - setSimLogs((prev) => [{ time: now, type, text }, ...prev.slice(0, 19)]); - }; - - // Simulator Request Run - const handleSimRequest = async () => { - if (isRequesting) return; - setIsRequesting(true); - - addLog(`Initiating API Request to /unstable-data...`, "info"); - - // 1. Check Circuit Breaker State - if (circuitState === "OPEN") { - addLog(`[CircuitBreaker] BLOCKED: Circuit is OPEN. Serving Fallback immediately (No Network IO).`, "error"); - addLog(`Response: { status: "degraded", data: [] } (Fallback Served) ✅`, "success"); - setIsRequesting(false); - return; - } - - // Simulate Network request delay - await new Promise((resolve) => setTimeout(resolve, 600)); - - // Determine Success/Failure - const isFailed = injectChaos ? Math.random() < failRate : false; - - if (isFailed) { - // Failure Flow - const nextFailCount = failureCount + 1; - setFailureCount(nextFailCount); - addLog(`API Call Failed: HTTP status 503 Service Unavailable`, "warning"); - - if (circuitState === "HALF_OPEN") { - setCircuitState("OPEN"); - setFailureCount(3); - addLog(`[CircuitBreaker] Probe request failed. Tripping back to OPEN! Cooldown starts.`, "error"); - } else if (nextFailCount >= 3) { - setCircuitState("OPEN"); - addLog(`[CircuitBreaker] 3 consecutive failures reached. Tripping to OPEN!`, "error"); - } else { - addLog(`[CircuitBreaker] Failure recorded (${nextFailCount}/3). Preparing backoff retry...`, "info"); - } - } else { - // Success Flow - setFailureCount(0); - addLog(`API Call Succeeded: HTTP status 200 OK`, "success"); - - if (circuitState === "HALF_OPEN") { - setCircuitState("CLOSED"); - setSuccessCount(0); - addLog(`[CircuitBreaker] Probe succeeded. Circuit is now CLOSED (Normal Operation).`, "success"); - } else { - breakerSuccess(); - } - } - - setIsRequesting(false); - }; - - const breakerSuccess = () => { - if (circuitState === "CLOSED") return; - }; - - // Trigger Cooldown Recovery Simulation - useEffect(() => { - if (circuitState === "OPEN") { - const timer = setTimeout(() => { - setCircuitState("HALF_OPEN"); - addLog(`[CircuitBreaker] Cooldown expired. Transitioning to HALF_OPEN. Probing next request...`, "warning"); - }, 6000); // 6s simulator cooldown - return () => clearTimeout(timer); - } - }, [circuitState]); - - const resetSimulator = () => { - setCircuitState("CLOSED"); - setFailureCount(0); - setInjectChaos(false); - setSimLogs([]); - addLog("Simulator reset. Circuit is CLOSED. Network operational.", "info"); - }; - */ - return (
- - {/* STICKY HEADER */} -
-
- -
- - {/* LEFT NAVIGATION SIDEBAR */} - - - {/* MAIN DOCUMENTATION CONTENT */} -
- - {/* INTRODUCTION */} -
{ sectionRefs.current["introduction"] = el; }} - className="mb-16 scroll-mt-24" - > -

Introduction

-

- A failing third-party API can bring down your entire application, leading to cascading service failures, degraded user experience, and lost revenue. How do you protect your systems and keep them resilient, even when downstream dependencies are completely unresponsive or failing? -

-

- Enter SmoothAPI. SmoothAPI stops third-party API crashes from breaking your app. It wraps your HTTP calls with industry-standard resilience patterns, catches network errors instantly, spaces out retries so recovering servers can breathe, and serves safe backup data the millisecond a service goes completely dead. -

-
-
-

TypeScript Native

-

- Dual-environment fetch wrapper supporting Edge, Serverless, and Node.js with built-in type inference. -

-
-
-

Python Native

-

- Elegant function decorator supporting both sync and async functions, integrating smoothly with requests and httpx. -

-
-
-
- - {/* INSTALLATION */} -
{ sectionRefs.current["installation"] = el; }} - className="mb-16 scroll-mt-24" - > -

Installation

-
-
-

TypeScript/JavaScript (NPM):

-
-                  npm install @codingaryan/smoothapi
-                
-
-
-

Python (PyPI):

-
-                  pip install smoothapi-py
-                
-
-
-
- - {/* QUICKSTART */} -
{ sectionRefs.current["quickstart"] = el; }} - className="mb-16 scroll-mt-24" - > -
-

Quickstart Guide

- {/* LANGUAGE TOGGLE */} -
- - -
-
- - {codeLang === "ts" ? ( -
-

- Create a custom resilient fetch instance and use it as a drop-in replacement for native `fetch`: -

-
-{`import { createResilientFetch } from '@codingaryan/smoothapi';
-
-const fetchWithRetry = createResilientFetch({
-  backoff: {
-    baseDelay: 100,      // ms
-    maxRetries: 3        // retry 3 times
-  },
-  circuitBreaker: {
-    failureThreshold: 3, // trip OPEN after 3 consecutive errors
-    cooldownMs: 10000    // stay OPEN for 10 seconds
-  },
-  fallback: { status: "degraded", data: [] }
-});
-
-// Use it just like normal fetch!
-const response = await fetchWithRetry('https://api.example.com/unstable');`}
-                
-
- ) : ( -
-

- Wrap any request functions using the `resilient_api` decorator to catch exceptions and manage backoff: -

-
-{`import requests
-from smooth_api import resilient_api, ResilientConfig
-
-config = ResilientConfig(
-    fallback={"status": "degraded", "data": []},
-    fallback_on_non_retryable=True
-)
-
-@resilient_api(config)
-def get_data():
-    res = requests.get('https://api.example.com/unstable')
-    res.raise_for_status() # Raise exception so decorator can intercept!
-    return res.json()
-
-# Execute safely
-data = get_data()`}
-                
-
- )} -
- - {/* CORE FEATURES */} -
{ sectionRefs.current["features"] = el; }} - className="mb-16 scroll-mt-24" - > -

Core Features

-
- -
-

Exponential Backoff & Full Jitter

-

- Retries transient failures with exponentially increasing delays. Generates randomized "jitter" boundaries to prevent client requests from hammering recovering endpoints in sync (the "thundering herd" problem). -

-
- -
-

Per-Domain Circuit Breaker

-

- Tracks API health in isolated state machines. If a specific domain reaches your failure threshold, the circuit trips to `OPEN`, immediately blocking further connections and returning fallback data. This prevents resource starvation and cascading failures. -

-
- -
-

Graceful Client-Error Fallbacks

-

- Support for `fallbackOnNonRetryable` / `fallback_on_non_retryable`. Safely intercept non-retryable client-side HTTP codes (like 404, 403, 400) and return custom data, show browser alerts, or fire custom notification callbacks instead of throwing app-breaking crashes. -

-
- -
-
- - {/* INTERACTIVE SIMULATOR */} -
{ sectionRefs.current["simulator"] = el; }} - className="mb-16 scroll-mt-24" - > -

Interactive Simulator

-

- Test how the circuit breaker and fallback mechanisms work in real-time. Toggle network failures, dispatch requests, and monitor the circuit status well logs: -

- -
- {/* Background gradient glow */} -
-
- - {/* Animated Icon */} -
-
-
- - - - -
-
- - {/* Status Badge */} -
- COMING SOON -
- -

Interactive API Sandbox

-

- We are building a comprehensive real-time dashboard simulator. You will be able to inject customizable latency, simulate network timeouts, trigger random HTTP exceptions, and watch the circuit breaker dynamically transition states live. -

- - {/* Simulated UI telemetry preview */} -
-
- Simulated Telemetry - -
-
-
[10:42:01] INFO Initializing state machine...
-
[10:42:02] INFO Target endpoint: api.smooth.dev/v1/chaos
-
[10:42:03] WARN Failure rate set to 80%. Ready to probe.
-
-
-
-
- - {/* CONFIGURATION OPTIONS */} -
{ sectionRefs.current["configuration"] = el; }} - className="mb-16 scroll-mt-24" - > -

Configuration Options

-

- Customize the behavior of `SmoothAPI` using the following properties when initializing: -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyTypeDefaultDescription
backoff.baseDelaynumber100 / 0.1Initial wait time before the first retry (ms in TS, seconds in Python).
backoff.maxRetriesnumber3Maximum number of attempts to resolve the request.
circuitBreaker.failureThresholdnumber3Consecutive failures needed to trip the circuit to OPEN.
circuitBreaker.cooldownMsnumber10000Time to wait (in ms) before entering HALF_OPEN probe state.
fallbackanyundefinedObject returned immediately on an OPEN circuit or client error fallback.
fallbackOnNonRetryablebooleanfalseIf true, returns fallbacks or mock responses on non-retryable client codes (e.g. 404, 401).
onNonRetryableErrorfunctionundefinedCustom callback fired when a client-error occurs. Disables default browser alerts.
-
-
- -
- - {/* RIGHT SIDEBAR (TABLE OF CONTENTS) */} - - -
- - {/* FOOTER */} - - +
+