Skip to content

Instantly share code, notes, and snippets.

@panzerdp
Last active September 18, 2022 23:51
Show Gist options
  • Save panzerdp/1312bcd5f455fa409d2aced844cad96f to your computer and use it in GitHub Desktop.
Save panzerdp/1312bcd5f455fa409d2aced844cad96f to your computer and use it in GitHub Desktop.
An extensible fetch() implementation that uses the decorator pattern
type ResponseWithData = Response & { data?: any };
interface Fetcher {
run(input: RequestInfo, init?: RequestInit): Promise<ResponseWithData>;
}
class BasicFetcher implements Fetcher {
async run(input: RequestInfo, init?: RequestInit): Promise<ResponseWithData> {
return await fetch(input, init);
}
}
class JsonFetcherDecorator implements Fetcher {
private decoratee: Fetcher;
constructor(decoratee: Fetcher) {
this.decoratee = decoratee;
}
async run(input: RequestInfo, init?: RequestInit): Promise<ResponseWithData> {
const response = await this.decoratee.run(input, init);
const json = await response.json();
response.data = json;
return response;
}
}
const TIMEOUT = 8000; // 8 seconds
class TimeoutFetcherDecorator implements Fetcher {
private decoratee: Fetcher;
constructor(decoratee: Fetcher) {
this.decoratee = decoratee;
}
async run(input: RequestInfo, init?: RequestInit): Promise<ResponseWithData> {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), TIMEOUT);
const response = await this.decoratee.run(input, {
...init,
signal: controller.signal
});
clearTimeout(id);
return response;
}
}
const fetcher = new TimeoutFetcherDecorator(
new JsonFetcherDecorator(
new BasicFetcher()
)
);
export const decoratedFetch = fetcher.run.bind(fetcher);
@panzerdp
Copy link
Author

Usage:

import { decoratedFetch } from './decoratedFetch';

async function executeRequest() {
  const { data } = await decoratedFetch('/movies.json');
  console.log(data);
}

executeRequest(); 
// logs [{ name: 'Heat' }, { name: 'Alien' }]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment