Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions examples/browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Browser Example — @codingaryan/smoothapi

A minimal browser-based example demonstrating how to use `@codingaryan/smoothapi` with retries, fallbacks, and circuit breaker protection.

The example interacts with the local sandbox server and provides two demos:

* **Retry Demo** — Uses `/unstable-data` to demonstrate automatic retries and fallback handling.
* **Circuit Breaker Demo** — Uses `/always-fail` to demonstrate circuit breaker behavior and fallback responses.

## Prerequisites

* Node.js 18+
* npm

## Start the Sandbox

From the repository root:

```bash
cd sandbox
npm install
node server.js
```

The sandbox will run on:

```text
http://localhost:3001
```

## Run the Browser Example

Open a second terminal:

```bash
cd examples/browser
npm install
npm run dev
```

Open the URL shown by Vite (typically `http://localhost:5173`).

## Retry Demo

The Retry Demo calls:

```text
http://localhost:3001/unstable-data
```

This endpoint intentionally returns a mix of:

* 200 responses
* 429 responses
* 500 responses

SmoothAPI automatically retries retryable failures and returns the final successful response when available.

## Circuit Breaker Demo

The Circuit Breaker Demo calls:

```text
http://localhost:3001/always-fail
```

This endpoint always returns a failure response.

After enough consecutive failures, the circuit breaker opens and returns the configured fallback immediately without making additional network requests.

## Features Demonstrated

* Automatic retries
* Exponential backoff
* Fallback responses
* Circuit breaker protection
* Browser integration with SmoothAPI
21 changes: 21 additions & 0 deletions examples/browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SmoothAPI Browser Example</title>
</head>
<body>
<h1>SmoothAPI Browser Example</h1>

<p>Demonstrates retries, circuit breaker behavior, and fallback handling.</p>

<button id="retry-btn">Retry Demo</button>
<button id="circuit-btn">Circuit Breaker Demo</button>

<h2>Output</h2>
<pre id="output">Click a button to begin...</pre>

<script type="module" src="./main.ts"></script>
</body>
</html>
99 changes: 99 additions & 0 deletions examples/browser/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { createResilientFetch } from "@codingaryan/smoothapi";

const retryButton = document.getElementById("retry-btn");
const circuitButton = document.getElementById("circuit-btn");
const output = document.getElementById("output");

const retryFallback = {
data: "cached fallback (stale)"
};

const circuitFallback = {
data: "circuit-open fallback"
};

const retryFetch = createResilientFetch({
retryOn: [429, 500, 502, 503, 504],
fallback: retryFallback,
fallbackOnNonRetryable: true
});

const circuitFetch = createResilientFetch({
retryOn: [429, 500, 502, 503, 504],
circuitBreaker: {
failureThreshold: 3,
cooldownMs: 5000
},
fallback: circuitFallback
});

retryButton?.addEventListener("click", async () => {
if (!output) return;

output.textContent = "Running retry demo...";

try {
const result = await retryFetch(
"http://localhost:3001/unstable-data"
);

if (result instanceof Response) {
const data = await result.json().catch(() => null);

output.textContent = JSON.stringify(
{
source: "live",
status: result.status,
data
},
null,
2
);
} else {
output.textContent = JSON.stringify(
{
source: "fallback",
data: result
},
null,
2
);
}
} catch (error) {
output.textContent = `Error: ${error}`;
}
});

circuitButton?.addEventListener("click", async () => {
if (!output) return;

output.textContent = "Running circuit breaker demo...";

try {
const result = await circuitFetch(
"http://localhost:3001/always-fail"
);

if (result instanceof Response) {
output.textContent = JSON.stringify(
{
hitNetwork: true,
status: result.status
},
null,
2
);
} else {
output.textContent = JSON.stringify(
{
hitNetwork: false,
data: result
},
null,
2
);
}
} catch (error) {
output.textContent = `Error: ${error}`;
}
});
18 changes: 18 additions & 0 deletions examples/browser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "smoothapi-browser-example",
"version": "1.0.0",
"private": true,
"description": "Browser example for @codingaryan/smoothapi",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@codingaryan/smoothapi": "1.0.0"
},
"devDependencies": {
"typescript": "^5.4.5",
"vite": "^5.4.0"
}
}
10 changes: 10 additions & 0 deletions examples/browser/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
5 changes: 5 additions & 0 deletions sandbox/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ let requestCount = 0;

app.use(express.json());

app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
next();
});

// Always 200. Used by test runners to poll for readiness before the suite
// starts. Does not touch requestCount.
app.get('/health', (_req, res) => {
Expand Down
Loading