| Metric | Coverage | Status |
|---|---|---|
| Statements | 86.61% | β |
| Branches | 76.35% | |
| Functions | 90.24% | β |
| Lines | 86.61% | β |
Last Updated: 2025-11-29
Convert OpenAPI/Swagger YAML specifications into typed React hooks with caching, polling, and cancellation support.
- π OpenAPI/Swagger Support: Convert YAML specs into TypeScript/JavaScript code
- π£ React Hooks: Method-specific hooks (
useGet,usePost,usePut,usePatch,useDelete) - πΎ Smart Caching: Built-in response caching with TTL support
- π Polling: Auto-refresh data at regular intervals
- π Request Cancellation: Abort in-flight requests
- π Full TypeScript Support: Auto-generated types from OpenAPI schemas
- π Zero Configuration: Works out of the box
- π― Type-Safe: End-to-end type safety from API to UI
- β‘ Lightweight: Minimal dependencies, tree-shakeable
- β¨ React 19 Ready: Full support for React 17, 18, and 19 with new hooks
- Optimistic Updates with
useOptimistic(React 19+) - Form Actions with
useActionState(React 19+) - Backward Compatible: Graceful fallback for React 17/18
- Optimistic Updates with
npm install react-api-weaverCreate a api.yaml file with your API specification:
openapi: 3.0.0
info:
title: My API
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/users:
get:
operationId: getUsers
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
name:
type: stringnpx react-api-weaver generate -i api.yaml -o src/generatedThis generates TypeScript functions and types:
src/generated/api.ts- API functionssrc/generated/types.ts- TypeScript types/interfaces for requests and responsessrc/generated/index.ts- Exports for easy importing
import React from 'react';
import { useGet } from 'react-api-weaver';
import { getUsers } from './generated/api';
function UserList() {
const { data, loading, error, refetch, abort } = useGet(
() => getUsers(),
{
cache: true,
polling: 30000, // Refresh every 30 seconds
}
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<button onClick={refetch}>Refresh</button>
<button onClick={abort}>Cancel</button>
{data?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}Generate API client code from OpenAPI YAML:
react-api-weaver generate -i <input.yaml> -o <output-dir> [options]Options:
-i, --input <path>: Path to OpenAPI YAML file (required)-o, --output <path>: Output directory for generated code (required)-f, --format <format>: Output format:ts,js, orboth(default:ts)-b, --base-url <url>: Base URL for API requests
Example:
react-api-weaver generate -i api.yaml -o src/generated -f ts -b https://api.example.comWatch for changes and regenerate automatically:
react-api-weaver watch -i <input.yaml> -o <output-dir> [options]Example:
react-api-weaver watch -i api.yaml -o src/generatedHook for GET requests with caching support.
const { data, loading, error, refetch, abort } = useGet(
apiFunction,
options
);Hook for POST requests (cache disabled by default).
const { data, loading, error, refetch, abort } = usePost(
apiFunction,
options
);Hook for PUT requests (cache disabled by default).
const { data, loading, error, refetch, abort } = usePut(
apiFunction,
options
);Hook for PATCH requests (cache disabled by default).
const { data, loading, error, refetch, abort } = usePatch(
apiFunction,
options
);Hook for DELETE requests (cache disabled by default).
const { data, loading, error, refetch, abort } = useDelete(
apiFunction,
options
);Hook for mutations with optimistic updates using React 19's useOptimistic.
const { data, optimisticData, loading, error, mutate, abort } = useApiOptimistic(
apiFunction,
{
optimisticUpdate: (currentData, input) => {
// Return the optimistic state
return { ...currentData, ...input };
},
onSuccess: (data) => console.log('Success!', data),
}
);Specialized Optimistic Hooks:
usePostOptimistic- POST with optimistic updatesusePutOptimistic- PUT with optimistic updatesusePatchOptimistic- PATCH with optimistic updatesuseDeleteOptimistic- DELETE with optimistic updates
Hook for form-based API interactions using React 19's useActionState.
const { data, error, isPending, action, formAction } = useApiAction(
apiFunction,
{
onSuccess: (data) => console.log('Success!', data),
}
);
// Use with forms
<form action={formAction}>
<input name="title" />
<button type="submit">Submit</button>
</form>
// Or call directly
await action({ title: 'New Todo' });All hooks accept an options object:
interface UseApiOptions<TData> {
// Enable/disable caching (default: true for GET, false for others)
cache?: boolean | {
ttl?: number; // Time to live in milliseconds
key?: string; // Custom cache key
};
// Polling interval in milliseconds
polling?: number;
// Whether the request should be executed (default: true)
enabled?: boolean;
// Success callback
onSuccess?: (data: TData) => void;
// Error callback
onError?: (error: Error) => void;
// Number of retries or boolean (default: 0)
retry?: number | boolean;
// Delay between retries in milliseconds (default: 1000)
retryDelay?: number;
}All hooks return an object with:
interface UseApiResult<TData> {
// Response data
data: TData | null;
// Loading state
loading: boolean;
// Error object
error: Error | null;
// Manual refetch function
refetch: () => Promise<void>;
// Abort current request
abort: () => void;
}import { useGet } from 'react-api-weaver';
import { getTodos } from './generated/api';
function TodoList() {
const { data, loading } = useGet(
() => getTodos({ _limit: 10 }),
{ cache: { ttl: 300000 } } // Cache for 5 minutes
);
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(todo => <li key={todo.id}>{todo.title}</li>)}
</ul>
);
}import { usePost } from 'react-api-weaver';
import { createTodo } from './generated/api';
function CreateTodo() {
const [title, setTitle] = useState('');
const { loading, refetch } = usePost(
() => createTodo({}, { title, userId: 1, completed: false }),
{
enabled: false,
onSuccess: (data) => {
console.log('Todo created:', data);
setTitle('');
},
}
);
const handleSubmit = (e) => {
e.preventDefault();
refetch();
};
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button disabled={loading}>Create</button>
</form>
);
}import { useGet } from 'react-api-weaver';
import { getTodoById } from './generated/api';
function LiveTodo({ id }) {
const { data } = useGet(
() => getTodoById({ id }),
{ polling: 5000 } // Poll every 5 seconds
);
return <div>{data?.title}</div>;
}import { useGet } from 'react-api-weaver';
import { getUsers } from './generated/api';
function UserList() {
const { data, loading, abort } = useGet(() => getUsers());
return (
<div>
{loading && <button onClick={abort}>Cancel</button>}
{data && <div>{data.length} users loaded</div>}
</div>
);
}import { useGet } from 'react-api-weaver';
import { getUserById } from './generated/api';
function UserProfile({ userId }) {
const { data } = useGet(
() => getUserById({ id: userId }),
{ enabled: !!userId } // Only fetch when userId is available
);
return <div>{data?.name}</div>;
}import { usePostOptimistic } from 'react-api-weaver';
import { createTodo } from './generated/api';
function OptimisticTodo() {
const [todos, setTodos] = useState([]);
const { optimisticData, loading, mutate } = usePostOptimistic(
(input) => createTodo(input),
{
optimisticUpdate: (current, input) => ({
id: Date.now(), // Temporary ID
...input,
}),
onSuccess: (data) => {
setTodos(prev => [...prev, data]);
},
}
);
const handleCreate = () => {
mutate({ title: 'New Todo', userId: 1, completed: false });
};
return (
<div>
<button onClick={handleCreate} disabled={loading}>
Add Todo
</button>
{optimisticData && (
<div style={{ opacity: loading ? 0.5 : 1 }}>
β‘ {optimisticData.title} (optimistic)
</div>
)}
{todos.map(todo => (
<div key={todo.id}>{todo.title}</div>
))}
</div>
);
}import { useApiAction } from 'react-api-weaver';
import { createTodo } from './generated/api';
function TodoForm() {
const { data, error, isPending, formAction } = useApiAction(
(input) => createTodo({
userId: 1,
title: input.title,
completed: input.completed === 'true',
})
);
return (
<form action={formAction}>
<input name="title" placeholder="Todo title" required />
<select name="completed">
<option value="false">Not Done</option>
<option value="true">Done</option>
</select>
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create Todo'}
</button>
{error && <div>Error: {error.message}</div>}
{data && <div>Created: {data.title}</div>}
</form>
);
}The generated API functions accept a RequestConfig parameter:
interface RequestConfig {
headers?: Record<string, string>;
baseURL?: string;
timeout?: number;
signal?: AbortSignal;
}Example:
const { data } = useGet(
() => getUsers({}, {
headers: { 'Authorization': 'Bearer token' },
timeout: 5000,
})
);You can set a base URL in three ways:
- In the OpenAPI YAML (servers section)
- Via CLI:
react-api-weaver generate -i api.yaml -o src/generated -b https://api.example.com - At runtime: Pass
baseURLin the request config
your-project/
βββ src/
β βββ generated/ # Generated API code
β β βββ api.ts # Generated API functions
β β βββ types.ts # TypeScript types/interfaces
β β βββ index.ts # Exports (functions + types)
β βββ components/
β βββ UserList.tsx # Your components using hooks
βββ api.yaml # OpenAPI specification
βββ package.json
All TypeScript types are exported from the generated types.ts file. You can import types separately from functions:
// Import functions
import { getUsers, createUser } from './generated/api';
// Import types separately
import type { GetUsersResponse, CreateUserBody, CreateUserResponse } from './generated/types';
// Or import everything from index
import { getUsers, type GetUsersResponse } from './generated';Available Types:
{OperationName}Params- Request parameters (for GET, DELETE, etc.){OperationName}Body- Request body (for POST, PUT, PATCH){OperationName}Response- Response data type
Example:
import { getTodoById } from './generated/api';
import type { GetTodoByIdParams, GetTodoByIdResponse } from './generated/types';
function TodoComponent({ todoId }: { todoId: number }) {
const params: GetTodoByIdParams = { id: todoId };
const { data } = useGet<GetTodoByIdResponse>(
() => getTodoById(params)
);
return <div>{data?.title}</div>;
}{
"scripts": {
"generate": "react-api-weaver generate -i api.yaml -o src/generated"
}
}Run npm run generate when you update your API spec.
{
"scripts": {
"predev": "react-api-weaver generate -i api.yaml -o src/generated",
"dev": "vite"
}
}Automatically generates code before starting the dev server.
react-api-weaver watch -i api.yaml -o src/generatedAutomatically regenerates code when the YAML file changes.
For local development and testing:
# In react-api-weaver directory
npm run build
npm link
# In your project directory
npm link react-api-weaverContributions are welcome! Please feel free to submit a Pull Request.
MIT
- Built with TypeScript
- Powered by React
- OpenAPI parsing by Swagger Parser
- Bundled with tsdown
If you find this project helpful, consider supporting me by buying me a coffee!
React API Weaver now fully supports React 19 while maintaining backward compatibility with React 17 and 18.
Use useOptimistic (React 19) for instant UI feedback before server responses:
import { usePostOptimistic } from 'react-api-weaver';
const { optimisticData, mutate } = usePostOptimistic(
createTodo,
{
optimisticUpdate: (current, input) => ({
id: Date.now(),
...input,
}),
}
);Benefits:
- β‘ Instant UI updates
- π Automatic rollback on errors
- π― Type-safe optimistic state
Use useActionState (React 19) for progressive enhancement:
import { useApiAction } from 'react-api-weaver';
const { formAction, isPending } = useApiAction(createTodo);
<form action={formAction}>
<input name="title" />
<button type="submit" disabled={isPending}>Submit</button>
</form>Benefits:
- π Works with native form elements
- π Progressive enhancement
- π― Built-in pending states
Check React version at runtime:
import { isReact19OrLater, getReactMajorVersion } from 'react-api-weaver';
if (isReact19OrLater()) {
console.log('React 19 features available!');
}All React 19 features gracefully degrade on React 17/18:
| Feature | React 19 | React 17/18 Fallback |
|---|---|---|
useApiOptimistic |
Uses native useOptimistic |
Manual state management |
useApiAction |
Uses native useActionState |
useTransition + state |
| Optimistic hooks | Native rollback | Manual error handling |
Step 1: Update Dependencies
npm install react@19 react-dom@19
npm install react-api-weaver@latestStep 2: Use New Hooks (Optional)
Replace standard mutation hooks with optimistic variants:
// Before (React 18)
const { data, loading, refetch } = usePost(createTodo, {
enabled: false,
onSuccess: (data) => {
setItems(prev => [...prev, data]);
}
});
// After (React 19)
const { optimisticData, loading, mutate } = usePostOptimistic(createTodo, {
optimisticUpdate: (current, input) => ({
id: Date.now(),
...input,
}),
onSuccess: (data) => {
setItems(prev => [...prev, data]);
}
});Step 3: Adopt Form Actions (Optional)
// Before (React 18)
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
await createTodo(Object.fromEntries(formData));
};
// After (React 19)
const { formAction } = useApiAction(createTodo);
<form action={formAction}>
{/* form fields */}
</form>β Good use cases:
- Creating new items in a list
- Toggling boolean states (like/unlike)
- Updating text fields
- Deleting items with visual feedback
β Avoid for:
- Complex server-side validation
- Operations with side effects
- Critical financial transactions
- When server response differs significantly from input
β Good use cases:
- Traditional form submissions
- Server-side validation
- Progressive enhancement
- Multi-step forms
β Avoid for:
- Real-time validation
- Complex client-side logic
- Non-form interactions
- Optimistic updates (React 19)
- Form actions (React 19)
- Automated testing (Jest + React Testing Library)
- React Query integration
- Middleware support (interceptors)
- WebSocket support
- GraphQL support
- Zod schema validation
- Devtools integration
- Video tutorial
- Interactive playground
- More examples (auth, pagination, etc.)
- API reference site
Made with β€οΈ by the React API Weaver team