Skip to content
Merged

V4 #93

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
145 commits
Select commit Hold shift + click to select a range
7e383ac
perf: Bail early for get requests
MattCCC Jun 7, 2025
2e1da1a
fix: Cache key check
MattCCC Jun 7, 2025
a9b9f33
fix: Return in flight request promise only if not aborted
MattCCC Jun 7, 2025
bf4f3a6
chore: Kick off react dep for development
MattCCC Jun 7, 2025
0fda976
feat: Add Pub/sub to notify subscribers about flight status
MattCCC Jun 8, 2025
b7f0266
fix: Prefer cache reload bailing earlier
MattCCC Jun 8, 2025
011916b
feat: Split revalidation from cache + add revalidation on window focu…
MattCCC Jun 8, 2025
260b9a6
feat: Kick off useFetcher hook
MattCCC Jun 8, 2025
d05ec6e
feat: Handle Dependent Queries
MattCCC Jun 8, 2025
d32807a
feat: Allow to pass revalidate as mutation setting
MattCCC Jun 8, 2025
5ea908f
feat: Merge types of hook with global ones
MattCCC Jun 8, 2025
fba00e4
chore: Explicitly define platform
MattCCC Jun 8, 2025
da44190
feat: Remove trigger + improve response typings
MattCCC Jun 8, 2025
ae2958c
perf: Reuse dedupeTime
MattCCC Jun 8, 2025
d6b2b9f
refactor: Simplify in-flight typings
MattCCC Jun 8, 2025
4ba0b60
feat: Allow cacheKey to be a string
MattCCC Jun 8, 2025
2f236f4
docs: Add cacheKey as string information + "Auto-Generated Cache Key …
MattCCC Jun 8, 2025
c34c732
feat: Introduce revalidateOnFocus setting
MattCCC Jun 8, 2025
3f1363f
fix: Don't pass undefined in baseURL if it doesn't exist
MattCCC Jun 9, 2025
bb3676a
fix: Properly pass generics to CacheSkipFunction type
MattCCC Jun 12, 2025
57e6ad1
chore: Add Testing Library for integration tests
MattCCC Jun 12, 2025
945698b
fix: Properly pass generics to CacheSkipFunction type
MattCCC Jun 12, 2025
6ad0feb
fix: Retry was not triggered when shouldRetry was not defined
MattCCC Jun 12, 2025
f122158
chore: Update deps
MattCCC Jun 25, 2025
3301032
feat: Pass "fetcher" as a simple function similar to native fetch() i…
MattCCC Jun 25, 2025
5375c20
feat: Use standardized response object even if custom fetcher is used
MattCCC Jun 25, 2025
02db5cf
feat: Replace FetcherInstance with CustomFetcher type
MattCCC Jun 25, 2025
3725c16
refactor: Update addListener to return void and improve listener mana…
MattCCC Jun 25, 2025
f48e234
feat: Add size limits for new React distribution files
MattCCC Jun 25, 2025
68d931e
feat: Enhance cache management and React integration
MattCCC Jun 26, 2025
e0c2675
feat: Add cache error handling and update fetcher configuration
MattCCC Jun 26, 2025
a3b6d9e
docs: Enhance caching options and add error response caching in READM…
MattCCC Jun 26, 2025
91e6366
docs: Simplify custom fetcher implementation in README example
MattCCC Jun 26, 2025
1a789d5
feat: Refactor cache reference management and enhance decrement logic…
MattCCC Jun 26, 2025
c00a046
docs: Add revalidation on focus and cache error responses options in …
MattCCC Jun 26, 2025
92b4e2d
docs: Add React integration section with useFetcher hook examples and…
MattCCC Jun 26, 2025
386c22e
refactor: Improve cache handling and error response management; strea…
MattCCC Jun 26, 2025
a5c818b
feat: Enhance README and Jest configuration; add response handling an…
MattCCC Jun 27, 2025
792212b
perf: Remove unnecessary config dependency from useFetcher hook to op…
MattCCC Jun 27, 2025
3d15dbd
refactor: Enhance type definitions and improve documentation for useF…
MattCCC Jun 27, 2025
d4ac84e
refactor: Rename removeRequestFromQueue to abortRequest and update re…
MattCCC Jun 27, 2025
61dd4ff
refactor: Update parameter naming in applyInterceptor function for cl…
MattCCC Jun 27, 2025
5b58ee5
test: Add more tests and abortable fetch mock utility for handling re…
MattCCC Jun 27, 2025
cb2c4fd
fix: Default response shouldn't be set unless it is explicitly defined
MattCCC Jun 27, 2025
a2c571d
test: Add test case for handling null response data type in integrati…
MattCCC Jun 27, 2025
6ded07e
test: Add advanced caching tests for cache corruption recovery and si…
MattCCC Jun 27, 2025
2b1366f
docs: Update README to reflect improved code coverage and add new fea…
MattCCC Jun 27, 2025
308e285
fix: Correct condition in isJSONSerializable to check for undefined v…
MattCCC Jun 27, 2025
94f62c4
refactor: Replace queue manager with inflight manager for request han…
MattCCC Jun 27, 2025
00edc1f
test: Adjust performance test threshold
MattCCC Jun 27, 2025
28e2c5b
refactor: Rename timestamp to time in CacheEntry and related function…
MattCCC Jun 27, 2025
4a5a63a
test: Increase performance test threshold to 150ms for better flexibi…
MattCCC Jun 27, 2025
034a9c8
refactor: Simplify URL path construction in buildConfig and improve r…
MattCCC Jun 28, 2025
3d5951a
refactor: Improve comments for clarity and maintain idempotency in re…
MattCCC Jun 28, 2025
9c1897f
fix: Update cacheTime assignment to use logical OR for better clarity…
MattCCC Jun 28, 2025
702b64f
refactor: Move integration tests to a separate dir
MattCCC Jun 28, 2025
e0b5c4b
test: Add more tests to cover error handling when network is down + u…
MattCCC Jun 28, 2025
f1a1c15
feat: Enhance retry logic in request handler to support null fallback…
MattCCC Jun 28, 2025
46859a2
feat: Add comprehensive authentication integration tests covering log…
MattCCC Jun 28, 2025
abfa0af
test: Add real-time and WebSocket integration tests for polling, manu…
MattCCC Jun 28, 2025
0ee2f46
test: Add integration tests for form submission, file upload, CRUD op…
MattCCC Jun 29, 2025
19c54c3
refactor: Shorten realtime tests
MattCCC Jun 29, 2025
0d00c51
fix: Update useFetcher to trigger on valid URL and improve cleanup logic
MattCCC Jun 29, 2025
1f6f22d
feat: add staleTime option for background revalidation and enhance ca…
MattCCC Jun 29, 2025
0fa63c9
fix: Update feature comparison table in README for clarity and consis…
MattCCC Jun 29, 2025
efb52ea
refactor: move fetchf into request handler
MattCCC Jun 29, 2025
7edaa5c
refactor: update return type of mutate function to promise for better…
MattCCC Jun 29, 2025
362bf87
test: add integration test for immediate switching behavior in useFet…
MattCCC Jun 29, 2025
8b2eac4
feat: enhance revalidation options by adding revalidateOnReconnect an…
MattCCC Jun 29, 2025
2778b70
refactor: rename Logger interface to FetcherLogger for clarity
MattCCC Jun 29, 2025
f4e47b2
feat: implement setDefaultConfig function to allow custom configurati…
MattCCC Jun 29, 2025
05fef10
feat: add isSlowConnection utility to detect slow network conditions
MattCCC Jun 29, 2025
3da05c9
feat: implement adaptive timeouts based on connection speed for impro…
MattCCC Jun 29, 2025
20d88b8
docs: add global configuration support with setDefaultConfig function…
MattCCC Jun 29, 2025
763862f
docs: update README to clarify default values for API configuration o…
MattCCC Jun 29, 2025
030076b
docs: enhance README with detailed error handling examples and timeou…
MattCCC Jun 29, 2025
8b3cb82
docs: expand README with detailed performance implications and error …
MattCCC Jun 29, 2025
97c63b9
docs: add setCache function documentation and clarify API fetcher con…
MattCCC Jun 29, 2025
d471c7e
fix: improve request body handling and prevent unnecessary rerenders …
MattCCC Jun 29, 2025
bbf68e0
feat: implement timeout management with a new timing wheel for improv…
MattCCC Jun 30, 2025
c2f9c5b
fix: Deduplication logic should not block cancellable requests
MattCCC Jun 30, 2025
ac40788
refactor: remove getInstance method and related custom fetcher logic …
MattCCC Jul 1, 2025
da736bb
fix: enhance type definitions in createApiFetcher for improved type s…
MattCCC Jul 1, 2025
d9a1ec9
fix: apply endpoint config to absolute URLs defined if defined
MattCCC Jul 1, 2025
2ee4c02
refactor: request handler tests to use fetchf directly and improve er…
MattCCC Jul 1, 2025
fdcca16
perf: Remove reliance on useEffect in React
MattCCC Jul 2, 2025
249dff9
docs: rename isValidating to isFetching in React hook for clarity in …
MattCCC Jul 2, 2025
0672c4e
perf: Improve performance of react hook
MattCCC Jul 2, 2025
7edb6cf
docs: add getCache function to README for retrieving cached data
MattCCC Jul 3, 2025
2566b26
sec: enhance API fetcher to handle absolute URLs and improve config m…
MattCCC Jul 3, 2025
e4f60b1
feat: add getCache and ttl to setCache
MattCCC Jul 6, 2025
a3201f7
refactor: update type definitions in withPolling and api-handler for …
MattCCC Jul 6, 2025
a7729a3
fix: update fetchBook usage to correctly pass query and path parameters
MattCCC Jul 6, 2025
0f2d9b6
refactor: update config merging logic and improve interceptor handlin…
MattCCC Jul 7, 2025
1429007
feat: adjust interceptor merging logic to apply LIFO order for onResp…
MattCCC Jul 7, 2025
33d00a1
feat: implement error handling utility and enhance FetchError class
MattCCC Jul 7, 2025
8b5e3bd
feat: Extract retry logic into a new handler with onRetry interceptor…
MattCCC Jul 7, 2025
07e11e0
feat: Enhance cache management with improved expiry and stale handlin…
MattCCC Jul 7, 2025
684b1fc
feat: Extract Retries from Request Handler + ensure that onResponse i…
MattCCC Jul 7, 2025
9b476f6
feat: Add performance benchmarks for fetchff library and enhance cach…
MattCCC Jul 7, 2025
3395b3b
feat: Refactor cache key generation by removing unused properties and…
MattCCC Jul 7, 2025
f9585e7
feat: Optimize default result handling and enhance immediate fetch tr…
MattCCC Jul 8, 2025
0cbb26a
docs: Add Interceptor Execution Order section + onRetry interceptor i…
MattCCC Jul 8, 2025
520707a
perf: Improve cache key generation by centralizing sanitization pattern
MattCCC Jul 8, 2025
b4df3b2
chore: Increase size limits for browser and node builds in size-limit…
MattCCC Jul 8, 2025
9ff0e2c
feat: Update cache handling functions to include optional ttl and sta…
MattCCC Jul 8, 2025
3f78756
feat: Enhance interceptor functionality with automatic cache key rege…
MattCCC Jul 10, 2025
a1fe81e
feat: Update cache key generation to include a whitelist of headers a…
MattCCC Jul 10, 2025
60c854b
perf: Simplify cache key generation by directly hashing headers and u…
MattCCC Jul 10, 2025
d210a26
feat: Update Content-Type handling in fetchff to automatically set ba…
MattCCC Jul 10, 2025
25f4f54
test: Refactor Content-Type handling in request tests to include null…
MattCCC Jul 10, 2025
5eb5386
fix: Improve Content-Type handling by checking for Blob and File type…
MattCCC Jul 10, 2025
90ab193
fix: some fixes
devdgna Jul 10, 2025
8f58936
feat: Test add Node.js version 24.x to CI workflow matrix
MattCCC Jul 10, 2025
21cc4cd
fix: Add missing eslint disable comment for File class constructor
MattCCC Jul 10, 2025
f0e8b16
fix: Ensure bundling is enabled for the browser configuration in tsup
MattCCC Jul 10, 2025
bd79557
perf: Better rerenders management + added tests for cache efficiency
MattCCC Jul 10, 2025
4ba9a12
feat: Add keepPreviousData option for improved UI experience
MattCCC Jul 11, 2025
6936960
docs: Add isCancelled property to error object for request cancellati…
MattCCC Jul 11, 2025
a376075
refactor: Rename revalidate options to refetch for clarity and consis…
MattCCC Jul 11, 2025
debf811
refactor: Remove unnecessary request context hint header from cache k…
MattCCC Jul 11, 2025
f136c98
perf: Remove double emit of response from cache
MattCCC Jul 11, 2025
ae27be0
test: fix timer handling
devdgna Jul 11, 2025
9e339fc
fix: some fixes
devdgna Jul 11, 2025
58e7c2b
fix: remove redundant empty test assertion
devdgna Jul 11, 2025
43359db
feat: add benchmarks for useFetcher against SWR and React Query
MattCCC Jul 11, 2025
4a4522c
fix: improve request abortion logic and enhance test cleanup
MattCCC Jul 11, 2025
5df02a6
fix: flattenData setting can flatten when there are more keys beyond …
MattCCC Jul 11, 2025
853890d
feat: add select option to transform response data and update documen…
MattCCC Jul 11, 2025
b78985b
fix: correct type definitions for request body in endpoint functions
MattCCC Jul 12, 2025
a3b5151
fix: reorder type parameters for consistency in error handling classe…
MattCCC Jul 12, 2025
699193d
fix: update interceptor types for consistency across request and resp…
MattCCC Jul 12, 2025
f394c55
fix: update FetchResponse type definition to include RequestBody, Que…
MattCCC Jul 12, 2025
6187075
Merge pull request #91 from MattCCC/react-hook
MattCCC Jul 12, 2025
30a73f8
fix: update type definitions for endpoints and request handling for c…
MattCCC Jul 13, 2025
7df18cc
fix: update ApiHandlerConfig type to require either apiUrl or baseURL
MattCCC Jul 13, 2025
072bd21
docs: update README and add example files for API usage
MattCCC Jul 13, 2025
85d614f
fix: correct type references in API handler and request handler for c…
MattCCC Jul 13, 2025
3c78d53
fix: export fetchf as fetchff for consistency and clarity
MattCCC Jul 13, 2025
bb58591
docs: add getDefaultConfig function to README and export it
MattCCC Jul 13, 2025
d357edc
docs: add note about official support for useFetcher in React integra…
MattCCC Jul 13, 2025
f68b499
fix: unify request type naming from DefaultRequestTypes to DefaultReq…
MattCCC Jul 14, 2025
b4d22a5
feat: add benchmark images for React concurrency and performance
MattCCC Jul 14, 2025
39789c8
feat: add examples and tests for typings declarations in examples.ts
MattCCC Jul 14, 2025
b4f39ab
Merge pull request #92 from MattCCC/typings
MattCCC Jul 14, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ["18.x", "20.x", "22.x"]
node: ['18.x', '20.x', '22.x', '24.x']
os: [ubuntu-latest, macOS-latest]

steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,6 @@ package/

dist/*.ts
dist/**/*.ts
dist/**/*.mts
!dist/*.map
!dist/**/*.map
2,430 changes: 2,066 additions & 364 deletions README.md

Large diffs are not rendered by default.

Binary file added docs/benchmarks/concurrency-react.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/benchmarks/react-benchmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions docs/examples/example-basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
apiUrl: 'https://example.com/api',
endpoints: {
getUser: {
url: '/user-details/:id',
method: 'GET',
},
getBooks: {
url: '/books/all',
method: 'GET',
},
},
});

async function main() {
// Basic GET request with path param
const { data: user } = await api.getUser({ urlPathParams: { id: 2 } });
console.log('User:', user);

// Basic GET request to fetch all books
const { data: books } = await api.getBooks();
console.log('Books:', books);
}

main();
24 changes: 24 additions & 0 deletions docs/examples/example-custom-headers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
apiUrl: 'https://api.example.com/',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer YOUR_TOKEN',
},
endpoints: {
getProfile: {
url: '/profile/:id',
},
},
});

async function main() {
// GET request with custom headers and path param
const { data: profile } = await api.getProfile({
urlPathParams: { id: 123 },
});
console.log('Profile:', profile);
}

main();
34 changes: 34 additions & 0 deletions docs/examples/example-error-strategy-defaultResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
apiUrl: 'https://example.com/api',
// strategy: 'defaultResponse',
endpoints: {
sendMessage: {
method: 'post',
url: '/send-message/:postId',
// strategy: 'defaultResponse',
},
},
});

async function sendMessage() {
const { data, error } = await api.sendMessage({
body: { message: 'Text' },
urlPathParams: { postId: 1 },
strategy: 'defaultResponse',
defaultResponse: { status: 'failed', message: 'Default response' },
onError(error) {
console.error('API error:', error.message);
},
});

if (error) {
console.warn('Message failed to send, using default response:', data);
return;
}

console.log('Message sent successfully:', data);
}

sendMessage();
27 changes: 27 additions & 0 deletions docs/examples/example-error-strategy-reject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createApiFetcher } from 'fetchff';
import type { ResponseError } from 'fetchff';

const api = createApiFetcher({
apiUrl: 'https://example.com/api',
endpoints: {
sendMessage: {
method: 'post',
url: '/send-message/:postId',
strategy: 'reject',
},
},
});

async function sendMessage() {
try {
await api.sendMessage({
body: { message: 'Text' },
urlPathParams: { postId: 1 },
});
console.log('Message sent successfully');
} catch (error) {
console.error('Message failed to send:', (error as ResponseError).message);
}
}

sendMessage();
28 changes: 28 additions & 0 deletions docs/examples/example-error-strategy-silent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
baseURL: 'https://example.com/api',
endpoints: {
sendMessage: {
method: 'post',
url: '/send-message/:postId',
// strategy: 'silent',
},
},
});

async function sendMessage() {
await api.sendMessage({
body: { message: 'Text' },
urlPathParams: { postId: 1 },
strategy: 'silent',
onError(error) {
console.error('Silent error logged:', error.message);
},
});

// Because of the strategy, if API call fails, it will never reach this point. Otherwise try/catch would need to be required.
console.log('Message sent successfully');
}

sendMessage();
30 changes: 30 additions & 0 deletions docs/examples/example-error-strategy-softFail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
apiUrl: 'https://example.com/api',
// You can set default strategy for all endpoints
strategy: 'softFail',
endpoints: {
sendMessage: {
method: 'post',
url: '/send-message/:postId',
// You can override strategy for particular endpoint (we set the same here for demonstration)
strategy: 'softFail',
},
},
});

async function sendMessage() {
const { data, error } = await api.sendMessage({
body: { message: 'Text' },
urlPathParams: { postId: 1 },
});

if (error) {
console.error('Request Error', error.message);
} else {
console.log('Message sent successfully:', data);
}
}

sendMessage();
32 changes: 32 additions & 0 deletions docs/examples/example-request-chaining.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createApiFetcher } from 'fetchff';

const api = createApiFetcher({
baseURL: 'https://example.com/api',
endpoints: {
getUser: { url: '/user' },
createPost: { url: '/post', method: 'POST' },
},
});

interface PostData {
title: string;
content: string;
}

async function fetchUserAndCreatePost(userId: number, postData: PostData) {
// Fetch user data
const { data: userData } = await api.getUser({ params: { userId } });

// Create a new post with the fetched user data
return await api.createPost({
body: {
...postData,
userId: userData.id,
},
});
}

// Example usage
fetchUserAndCreatePost(1, { title: 'New Post', content: 'This is a new post.' })
.then((response) => console.log('Post created:', response))
.catch((error) => console.error('Error:', error));
51 changes: 51 additions & 0 deletions docs/examples/example-typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createApiFetcher } from 'fetchff';
import type { Endpoint } from 'fetchff';

// Example endpoint interfaces
type Book = { id: number; title: string; rating: number };
type Books = { books: Book[]; totalResults: number };
type BookQueryParams = { newBook?: boolean; category?: string };
type BookPathParams = { bookId: number };

const endpoints = {
fetchBooks: {
url: '/books',
method: 'GET' as const,
},
fetchBook: {
url: '/books/:bookId',
method: 'GET' as const,
},
} as const;

interface EndpointsList {
fetchBook: Endpoint<{
response: Book;
params: BookQueryParams;
urlPathParams: BookPathParams;
}>;
fetchBooks: Endpoint<{ response: Books; params: BookQueryParams }>;
}

const api = createApiFetcher<EndpointsList>({
baseURL: 'https://example.com/api',
endpoints,
strategy: 'softFail',
});

async function main() {
// Properly typed request with URL params
const { data: book } = await api.fetchBook({
params: { newBook: true },
urlPathParams: { bookId: 1 },
});
console.log('Book:', book);

// Generic type can be passed directly for additional type safety
const { data: books } = await api.fetchBooks<{ response: Books }>({
params: { category: 'fiction' },
});
console.log('Books:', books);
}

main();
10 changes: 10 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,14 @@ module.exports = {
testEnvironment: 'node',
workerThreads: true,
coverageReporters: ['lcov', 'text', 'html'],
coveragePathIgnorePatterns: [
'/node_modules/',
'/test/utils/',
'/test/mocks/',
'/dist/',
],
moduleNameMapper: {
'^fetchff$': '<rootDir>/src/index.ts',
'^fetchff/(.*)$': '<rootDir>/src/$1.ts',
},
};
Loading
Loading