From 9b38a947f65a1420bc15ce6cbd549e73ac6d38b2 Mon Sep 17 00:00:00 2001 From: Francisco Veracoechea Date: Mon, 3 Jul 2023 18:37:53 -0400 Subject: [PATCH] Updated docs and tests --- .editorconfig | 2 +- docs/components/features.mdx | 12 +- docs/pages/error-handling.mdx | 76 +++++ docs/pages/index.mdx | 8 +- docs/pages/reference/_meta.json | 7 +- docs/pages/reference/core.mdx | 241 -------------- docs/pages/reference/error.mdx | 31 -- docs/pages/reference/fetchtastic.mdx | 302 ++++++++++++++++++ docs/pages/reference/http-error.mdx | 34 ++ docs/pages/reference/resolver.mdx | 94 ------ docs/pages/reference/types.mdx | 162 ++++------ .../reference/{utility.mdx => utilities.mdx} | 42 +-- docs/pages/usage.mdx | 87 ++--- lib/src/core/Fetchtastic.ts | 11 +- lib/src/core/HttpError.ts | 9 +- lib/src/core/types.ts | 23 +- lib/src/internals/constants.ts | 3 + lib/src/utils/helpers.ts | 6 +- lib/test/requests.test.ts | 2 +- 19 files changed, 561 insertions(+), 591 deletions(-) delete mode 100644 docs/pages/reference/core.mdx delete mode 100644 docs/pages/reference/error.mdx create mode 100644 docs/pages/reference/fetchtastic.mdx create mode 100644 docs/pages/reference/http-error.mdx delete mode 100644 docs/pages/reference/resolver.mdx rename docs/pages/reference/{utility.mdx => utilities.mdx} (91%) create mode 100644 lib/src/internals/constants.ts diff --git a/.editorconfig b/.editorconfig index b0a528e..b9acd98 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,5 +11,5 @@ indent_style = space indent_size = 2 max_line_length = 100 -[*.{css,scss,sass,graphql,gql,md,mdx}] +[*.{css,scss,sass,graphql,gql}] indent_size = 4 diff --git a/docs/components/features.mdx b/docs/components/features.mdx index f0f00b9..50ddabb 100644 --- a/docs/components/features.mdx +++ b/docs/components/features.mdx @@ -1,8 +1,8 @@ ## Features -- **Small** - Less than 3KB -- **Composable** - Safely reuse previous configurations -- **Intuitive** - Clean and easy to use API -- **Type safe** - Strongly typed, written in TypeScript -- **Isomorphic** - Compatible with modern browsers, Node.js and Deno -- **Well Tested** - Fully covered by unit tests +- **Small** - Less than 3KB +- **Composable** - Safely reuse previous configurations +- **Intuitive** - Clean and easy to use API +- **Type safe** - Strongly typed, written in TypeScript +- **Isomorphic** - Compatible with modern browsers, Node.js and Deno +- **Well Tested** - Fully covered by unit tests diff --git a/docs/pages/error-handling.mdx b/docs/pages/error-handling.mdx index 4f6c27f..eb6b844 100644 --- a/docs/pages/error-handling.mdx +++ b/docs/pages/error-handling.mdx @@ -1 +1,77 @@ # Error Handling + +Sicen [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) doest not reject on http error status, manually checking and handling every request error code can be very tedious: + +```typescript + +fetch("anything") + .then(response => { + if(!response.ok) { + if(response.status === 404) throw new Error("Not found"); + else if(response.status === 401) throw new Error("Unauthorized"); + else if(response.status === 418) throw new Error("I'm a teapot !"); + /* ... */ + else throw new Error("Other error") + } else { + /* ... */ + } + }) + .then(data => /* ... */) + .catch(error => { /* ... */ }) +``` + +Fetchtastic throws an [HttpError](/reference/http-error) when the response is not successful and contains helper methods to handle common codes. + +We call those helper methods [Error Catchers](/reference/fetchtastic#error-catchers): + +```typescript +const api = new Fetchtastic('https://jsonplaceholder.typicode.com/posts'); + +await api + .badRequest(error => /* 400 bad-request */) + .unauthorized(error => /* 401 unauthorized */) + .forbidden(error => /* 403 forbidden */) + .notFound(error => /* 404 not-found */) + .timeout(error => /* 408 request-timeout */) + .serverError(error => /* 500 internal-server-error */) + .onError(418, error => /* I'm a Teapot */) + .resolve() + .then(doSomethingWithResponse) + .catch((error: Error) => { + /* Uncaught errors */ + + if (error instanceof HttpError) { + console.log(error.url) // https://jsonplaceholder.typicode.com/posts + console.log(error.status) // 429 + console.log(error.message) // Too Many Requests + } + + }); + +``` + +The original config is passed along the error and can be used in order to perform an additional request, and modify the initial response: + +```typescript +let authToken = ''; + +const api = new Fetchtastic('https://my.backend-api.com'); + +function getResource(callback) { + api + .get('/resource') + .headers({ Authorization: authToken }) + .unauthorized(async (error, config) => { + // Renew credentials + const credentials = await api.get('/renew-token').json(); + authToken = credentials.token; + // Replay the original request with new credentials + return config.headers({ Authorization: authToken }).resolve(); + }) + .json() + .then(callback); +} +``` + +In the above example, you can notice that the execution flow is preserved as expected, +`.then` will be called with the result of the original request or the replayed one. diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index e2ca614..f815d72 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -56,8 +56,8 @@ Simplifies code structure, enhances readability, and improves the overall develo const config = new Fetchtastic('https://jsonplaceholder.typicode.com'); const posts = await config - .header({ Accept: 'application/json', 'Content-Type': 'application/json' }) - .searchParams({ page: 1, first: 12 }) - .get('/posts') - .json(); + .header({ Accept: 'application/json', 'Content-Type': 'application/json' }) + .searchParams({ page: 1, first: 12 }) + .get('/posts') + .json(); ``` diff --git a/docs/pages/reference/_meta.json b/docs/pages/reference/_meta.json index d2fb6a0..e7a372a 100644 --- a/docs/pages/reference/_meta.json +++ b/docs/pages/reference/_meta.json @@ -1,7 +1,6 @@ { - "core": "Core", - "resolver": "Resolver", - "error": "Error", + "fetchtastic": "Fetchtastic", + "http-error": "HttpError", "types": "Types", - "utility": "Utility" + "utilities": "Utilities" } diff --git a/docs/pages/reference/core.mdx b/docs/pages/reference/core.mdx deleted file mode 100644 index 73562ee..0000000 --- a/docs/pages/reference/core.mdx +++ /dev/null @@ -1,241 +0,0 @@ -# Fetchtastic - -The `Fetchtastic` class represents an HTTP request configuration. It provides methods for setting headers, URL parameters, request body, and other options. It also provides convenience methods for performing common HTTP methods such as GET, POST, PUT, DELETE, OPTIONS, PATCH, and HEAD. - -## Properties - -- `URL` - The URL of the request, including any URL parameters. -- `searchParamsJSON` - An object representing the URL parameters of the request. -- `headersJSON` - An object representing the headers of the request. -- `method`: The HTTP method associated with the request (`GET`, `POST`, `PUT`, etc.). - -## Constructor - -```typescript -constructor(baseUrl?: string | URL, controller?: AbortController); -``` - -Creates a new instance of the `Fetchtastic` class. - -- `baseUrl`: The base URL of the request. Optional. -- `controller`: An `AbortController` instance for aborting the request. Optional. - -## Methods - -### HTTP methods: - -These functions when called, set a specific HTTP method for the request. -Likewise, you can pass optional `url` and `body` arguments to these methods if needed. - -`get` - -```typescript -get(url?: string): Fetchtastic -``` - -`post` - -```typescript -post(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`put` - -```typescript -put(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`delete` - -```typescript -delete(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`options` - -```typescript -options(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`options` - -```typescript -options(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`patch` - -```typescript -patch(url?: string, body?: BodyInit | null | unknown): Fetchtastic -``` - -`head` - -```typescript -head(url?: string): Fetchtastic -``` - -### Response methods - -These functions when called, execute the HTTP request and return a promise with its response. - -The primary objectives of these functions involve initiating the HTTP request with the existing configuration, with a focus on managing responses, and parsing the resulting data. - -`resolve` - -```typescript -resolve(): Promise -``` - -Sends the HTTP request and resolves with the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object representing the response. - -`json` - -```typescript -json(assertData?: DataAssertionFn): Promise -``` - -Returns a promise that resolves with the result of parsing the response body text as [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON). - -- `assertData`: An (optional) assertion function to validate the parsed JSON data. If provided, the parsed data is passed to this function for validation. - -`arrayBuffer` - -```typescript -arrayBuffer(): Promise -``` - -Returns a promise that resolves with an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) representation of the response body. - -`blob` - -```typescript -blob(): Promise -``` - -Returns a promise that resolves with an [Blob](/) - -`formData` - -```typescript -formData(): Promise -``` - -Returns a promise that resolves with the parsed [FormData](/) object. - -`text` - -```typescript -text(): Promise -``` - -Returns a promise that resolves with the response data as plain text. - -### `headers` - -```typescript -headers(data?: HeadersInit, replace = false): Fetchtastic -``` - -Sets the headers of the request. - -- `data`: The headers to set. -- `replace`: Indicates whether to replace the existing headers or append to them. Default is `false`. - -### `appendHeader` - -```typescript -appendHeader(name: string, value: string): Fetchtastic -``` - -Appends a header to the request. - -- `name`: The name of the header. -- `value`: The value of the header. - -### `deleteHeader` - -```typescript -deleteHeader(name: string): Fetchtastic -``` - -Deletes a header from the request. - -- `name`: The name of the header to delete. - -### `url` - -```typescript -url(url: string | URL, replace = false): Fetchtastic -``` - -Sets the URL of the request. - -- `url`: The URL to set. -- `replace`: Indicates whether to replace the existing URL or append to it. Default is `false`. - -### `searchParams` - -```typescript -searchParams(data?: SearchParamInput, replace = false): Fetchtastic -``` - -Sets the URL parameters of the request. - -- `data`: The URL parameters to set. -- `replace`: Indicates whether to replace the existing URL parameters or append to them. Default is `false`. - -### `appendSearchParam` - -```typescript -appendSearchParam(name: string, value: string | number | boolean): Fetchtastic -``` - -Appends a URL parameter to the request. - -- `name`: The name of the URL search parameter. - -- `value`: The value of the URL search parameter. - -### `deleteSearchParam` - -```typescript -deleteSearchParam(name: string): Fetchtastic -``` - -Deletes a URL parameter from the request. - -- `name`: The name of the URL parameter to delete. - -### `body` - -```typescript -body(body: BodyInit | null | unknown): Fetchtastic -``` - -Sets the body of the request. - -- `body`: The body of the request. - -### `setOptions` - -```typescript -setOptions(options: FetchtasticOptions, replace = false): Fetchtastic -``` - -Sets the options of the request. - -- `options`: The options to set. -- `replace`: Indicates whether to replace the existing options or merge them with the new options. Default is `false`. - -### `getOptions` - -```typescript -getOptions(method: HttpMethod): FetchOptions -``` - -Gets the options object for the request. - -- `method`: The HTTP method of the request. - -Returns the options object for the request. diff --git a/docs/pages/reference/error.mdx b/docs/pages/reference/error.mdx deleted file mode 100644 index 741e14c..0000000 --- a/docs/pages/reference/error.mdx +++ /dev/null @@ -1,31 +0,0 @@ -# FetchError - -The `FetchError` class extends the built-in `Error` class and represents an error that occurs during an HTTP request made with `Fetchtastic`. It encapsulates information about the error, including the request URL, HTTP method, response details, and error message. - -## Properties - -- `status`: The HTTP status code of the error response. -- `method`: The HTTP method used in the request. -- `response`: The `Response` object representing the error response. -- `url`: The URL of the request that resulted in the error. -- `text`: The response body as plain text **(if available)**. -- `json`: The response body parsed as JSON **(if available)**. - -## Constructor - -### `constructor` - -```typescript -constructor(url: string, method: HttpMethod, response?: Response, message?: string) -``` - -Creates a new instance of the `FetchError` class. - -- `url: string`: The URL of the request that resulted in the error. -- `method: HttpMethod`: The HTTP method used in the request. -- `response?: Response`: The `Response` object representing the error response **(optional)**. -- `message?: string`: A custom error message **(optional)**. - -## Methods - -The `FetchError` class does not expose any public methods. diff --git a/docs/pages/reference/fetchtastic.mdx b/docs/pages/reference/fetchtastic.mdx new file mode 100644 index 0000000..0db9619 --- /dev/null +++ b/docs/pages/reference/fetchtastic.mdx @@ -0,0 +1,302 @@ +# Fetchtastic + +Represents an HTTP request configuration. It provides methods for setting headers, URL parameters, request body, and other options. It also provides convenience methods for performing common HTTP methods such as GET, POST, PUT, DELETE, OPTIONS, PATCH, and HEAD. + +## Properties + +- `URL` - The URL of the request, including any URL parameters. +- `searchParamsJSON` - An object representing the URL parameters of the request. +- `headersJSON` - An object representing the headers of the request. +- `method`: The HTTP method associated with the request (`GET`, `POST`, `PUT`, etc.). + +## Constructor + +Creates a new instance of the `Fetchtastic` class. + +- `baseUrl`: The base URL of the request. Optional. +- `controller`: An `AbortController` instance for aborting the request. Optional. + +```typescript +constructor(baseUrl?: string | URL, controller?: AbortController); +``` + +## HTTP methods: + +These functions when called, set a specific HTTP method to the request configuration. +Likewise, you can pass optional `url` and `body` arguments to these methods if needed. + +### get + +```typescript +get(url?: string): Fetchtastic +``` + +### post + +```typescript +post(url?: string, body?: BodyInit | null | unknown): Fetchtastic +``` + +### put + +```typescript +put(url?: string, body?: BodyInit | null | unknown): Fetchtastic +``` + +### delete + +```typescript +delete(url?: string, body?: BodyInit | null | unknown): Fetchtastic +``` + +### options + +```typescript +options(url?: string, body?: BodyInit | null | unknown): Fetchtastic +``` + +### patch + +```typescript +patch(url?: string, body?: BodyInit | null | unknown): Fetchtastic +``` + +### head + +```typescript +head(url?: string): Fetchtastic +``` + +## Response methods + +These functions when called, execute the HTTP request and return a promise with its response. + +The primary objectives of these functions involve initiating the HTTP request with the existing configuration, with a focus on managing responses, and parsing the resulting data. + +### resolve + +Sends the HTTP request and resolves with the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object representing the response. + +```typescript +resolve(): Promise +``` + +### json + +Returns a promise that resolves with the result of parsing the response body text as `JSON`. + +- `assertData`: An (optional) assertion function to validate the parsed `JSON` data. If provided, the parsed data is passed to this function for validation. + +```typescript +json(assertData?: DataAssertionFn): Promise +``` + +### arrayBuffer + +Returns a promise that resolves with an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) representation of the response body. + +```typescript +arrayBuffer(): Promise +``` + +### blob + +Returns a promise that resolves with a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) representation of the response body. + +```typescript +blob(): Promise +``` + +### formData + +Returns a promise that resolves with a [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) representation of the response body. + +```typescript +formData(): Promise +``` + +### text + +Returns a promise that resolves with the response data as plain text. + +```typescript +text(): Promise +``` + +## Error Catchers + +Error catchers are in charge of handling [Not-OK](https://developer.mozilla.org/en-US/docs/Web/API/Response/ok) responses, +by default `Fetchtastic` will throw an error (for HTTP error codes) unless a catcher has been provided to handle it. + +```typescript +type ErrorCatcher = (error: HttpError, config: Fetchtastic) => void | Promise; +``` + +You can add as many **catchers** as you need for a given error code, they all will be executed if an error is caught. + +### onError + +Sets an error-catcher function to be executed in the case of a HTTP error code response. + +- `status`: HTTP status code of the error to be handled. +- `catcher`: on-error callback function executed in case of error. + +```typescript +onError(status: number, catcher: ErrorCatcher): Fetchtastic +``` + +### badRequest - 400 + +```typescript +badRequest(catcher: ErrorCatcher): Fetchtastic +``` + +### unauthorized - 401 + +```typescript +unauthorized(catcher: ErrorCatcher): Fetchtastic +``` + +### forbidden - 403 + +```typescript +forbidden(catcher: ErrorCatcher): Fetchtastic +``` + +### notFound - 404 + +```typescript +notFound(catcher: ErrorCatcher): Fetchtastic +``` + +### timeout - 408 + +```typescript +timeout(catcher: ErrorCatcher): Fetchtastic +``` + +### serverError - 500 + +```typescript +serverError(catcher: ErrorCatcher): Fetchtastic +``` + +## Request configuration: + +### controller + +Registers an abort controller, in order to cancel the request if needed. + +- `abortController`: A instance of [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) + +```typescript +controller(abortController: AbortController): Fetchtastic +``` + +### headers + +Sets the headers of the request, it uses [`Headers.set`](https://developer.mozilla.org/en-US/docs/Web/API/Headers/set) behind the scenes. + +- `data`: The headers to set. +- `replace`: Indicates whether to replace the existing headers or append to them. Default is `false`. + +```typescript +headers(data?: HeadersInit, replace = false): Fetchtastic +``` + +### appendHeader + +Appends a header to the request, it uses [`Headers.append`](https://developer.mozilla.org/en-US/docs/Web/API/Headers/append) behind the scenes. + +- `name`: The name of the header. +- `value`: The value of the header. + +```typescript +appendHeader(name: string, value: string): Fetchtastic +``` + +### deleteHeader + +Deletes a header from the request. + +- `name`: The name of the header to delete. + +```typescript +deleteHeader(name: string): Fetchtastic +``` + +### url + +Sets the URL of the request. + +- `url`: The URL to set. +- `replace`: Indicates whether to replace the existing URL or append to it. Default is `false`. + +```typescript +url(url: string | URL, replace = false): Fetchtastic +``` + +### searchParams + +Sets the URL parameters of the request. + +- `data`: The URL parameters to set. +- `replace`: Indicates whether to replace the existing URL parameters or append to them. Default is `false`. + +```typescript +searchParams(data?: SearchParamInput, replace = false): Fetchtastic +``` + +### appendSearchParam + +Appends a URL parameter to the request. + +- `name`: The name of the URL search parameter. +- `value`: The value of the URL search parameter. + +```typescript +appendSearchParam(name: string, value: string | number | boolean): Fetchtastic +``` + +### deleteSearchParam + +Deletes a URL parameter from the request. + +- `name`: The name of the URL parameter to delete. + +```typescript +deleteSearchParam(name: string): Fetchtastic +``` + +### `body` + +Sets the body of the request. + +- `body`: The body of the request. + +```typescript +body(body: BodyInit | null | unknown): Fetchtastic +``` + +### setOptions + +Sets the options of the request. + +- `options`: The options to set. +- `replace`: Indicates whether to replace the existing options or merge them with the new options. Default is `false`. + +```typescript +setOptions(options: FetchtasticOptions, replace = false): Fetchtastic +``` + +### getOptions + +Gets the options object for the request. + +- `method`: The HTTP method of the request. + +Returns the options object for the request. + +```typescript +getOptions(method: HttpMethod): FetchOptions +``` diff --git a/docs/pages/reference/http-error.mdx b/docs/pages/reference/http-error.mdx new file mode 100644 index 0000000..bb8841e --- /dev/null +++ b/docs/pages/reference/http-error.mdx @@ -0,0 +1,34 @@ +# HttpError + +The `HttpError` class extends the built-in [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) class and represents an error that occurs during an HTTP request made with `Fetchtastic`. +It encapsulates information about the error, including the request URL, HTTP method, response details, and error message. + +```typescript +import { HttpError } from 'fetchtastic/core'; +``` + +## Properties + +- `status`: HTTP status code associated with the error. +- `method`: Indicates the HTTP method used in the failed request. +- `response`: Refers to the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object received from the failed request. +- `url`: The URL of the request that resulted in the error. + +## Constructor + +### `constructor` + +```typescript +constructor(url: string, method: HttpMethod, response: Response, message?: string) +``` + +Creates a new instance of the `HttpError` class. + +- `url`: The URL of the request that resulted in the error. +- `method`: The HTTP method used in the request. +- `response`: The `Response` object representing the error response. +- `message`: A custom error message **(optional)**. + +## Methods + +The `HttpError` does not expose any public methods. diff --git a/docs/pages/reference/resolver.mdx b/docs/pages/reference/resolver.mdx deleted file mode 100644 index 3877f8b..0000000 --- a/docs/pages/reference/resolver.mdx +++ /dev/null @@ -1,94 +0,0 @@ -# FetchResolver - -The `FetchResolver` class is a generic class that represents a resolver for making HTTP requests with a specific HTTP method. It is used in conjunction with the `Fetchtastic` class to perform various HTTP operations such as sending requests, handling responses, and parsing response data. - -**Properties:** - -- `method`: The HTTP method associated with the resolver request to be resolved (`GET`, `POST`, `PUT`, etc.). - -## Constructor - -```typescript -constructor(config: Fetchtastic, method: Method) { /** */ } -``` - -Creates a new instance of the `FetchResolver` class. - -- `config`: The `Fetchtastic` instance representing the request configuration. -- `method`: The HTTP method associated with the resolver. - -## Methods - -### `config` - -```typescript -config(updateFn: (config: Fetchtastic) => void): FetchResolver -``` - -Updates the configuration of the associated `Fetchtastic` instance. - -- `updateFn`: A function that takes the current `Fetchtastic` instance as a parameter and modifies it. - -Returns the `FetchResolver` instance. - -### `resolve` - -```typescript -resolve(): Promise -``` - -Sends the HTTP request and resolves with the `Response` object representing the response. - -Returns a promise that resolves with the `Response` object if the request is successful. - -### `json` - -```typescript -json(assertData?: DataAssertionFn): Promise -``` - -Parses the response data as JSON and resolves with the parsed data. - -- `assertData` (optional): An assertion function to validate the parsed JSON data. If provided, the parsed data is passed to this function for validation. - -Returns a promise that resolves with the parsed JSON data. - -### `arrayBuffer` - -```typescript -arrayBuffer(): Promise -``` - -Parses the response data as an `ArrayBuffer` and resolves with the parsed data. - -Returns a promise that resolves with the parsed `ArrayBuffer` data. - -### `blob` - -```typescript -blob(): Promise -``` - -Parses the response data as a `Blob` and resolves with the parsed data. - -Returns a promise that resolves with the parsed `Blob` data. - -### `formData` - -```typescript -formData(): Promise -``` - -Parses the response data as a `FormData` object and resolves with the parsed data. - -Returns a promise that resolves with the parsed `FormData` object. - -### `text` - -```typescript -text(): Promise -``` - -Parses the response data as plain text and resolves with the parsed data. - -Returns a promise that resolves with the parsed text data. diff --git a/docs/pages/reference/types.mdx b/docs/pages/reference/types.mdx index b217ff7..67a17d4 100644 --- a/docs/pages/reference/types.mdx +++ b/docs/pages/reference/types.mdx @@ -1,164 +1,124 @@ -# Exposed Types +# Exposed types -## HttpMethods +The following types can be imported from `fetchtastic/core` -`HttpMethods` is an array of strings that represents the HTTP methods available for making requests. It includes the following methods: +### HttpMethod -- `OPTIONS` -- `GET` -- `HEAD` -- `PUT` -- `POST` -- `DELETE` -- `PATCH` +`HttpMethod` is a type alias that represents a single HTTP method, It includes the following methods: -```typescript -import { HttpMethods } from 'module'; - -// Access individual HTTP methods -const optionsMethod = HttpMethods[0]; // 'OPTIONS' -const getMethod = HttpMethods[1]; // 'GET' -// ... - -// Iterate over all HTTP methods -for (const method of HttpMethods) { - console.log(method); -} -``` - -## HttpMethod - -`HttpMethod` is a type alias that represents a single HTTP method. It is defined as `(typeof HttpMethods)[number]`, which means that it can only be one of the values in the `HttpMethods` array. +- `OPTIONS` +- `GET` +- `HEAD` +- `PUT` +- `POST` +- `DELETE` +- `PATCH` ```typescript -import { HttpMethod } from 'module'; +import { HttpMethod } from 'fetchtastic/core'; const method: HttpMethod = 'GET'; ``` -## ResponseParsers +### ResponseParser -`ResponseParsers` is an array of strings that represents the possible response types that can be parsed. It includes the following parsers: +`ResponseParser` is a type alias that represents a single response type, It includes the following parsers: -- `ArrayBuffer` -- `Blob` -- `FormData` -- `JSON` -- `Text` +- `ArrayBuffer` +- `Blob` +- `FormData` +- `JSON` +- `Text` ```typescript -import { ResponseParsers } from 'module'; - -// Access individual response parsers -const arrayBufferParser = ResponseParsers[0]; // 'ArrayBuffer' -const blobParser = ResponseParsers[1]; // 'Blob' -// ... - -// Iterate over all response parsers -for (const parser of ResponseParsers) { - console.log(parser); -} -``` - -## ResponseParser - -`ResponseParser` is a type alias that represents a single response type that can be parsed. It is defined as `(typeof ResponseParsers)[number]`, which means that it can only be one of the values in the `ResponseParsers` array. - -```typescript -import { ResponseParser } from 'module'; +import { ResponseParser } from 'fetchtastic/core'; const parser: ResponseParser = 'JSON'; ``` -## SearchParamInput +### SearchParamInput `SearchParamInput` is a type alias that represents the different types of inputs that can be used for a search parameter. It can be one of the following types: -- `string` -- `URLSearchParams` -- `[string, string | boolean | number][]` -- `Record` +- `string` +- `URLSearchParams` +- `[string, string | boolean | number][]` +- `Record` ```typescript -import { SearchParamInput } from 'module'; +import { SearchParamInput } from 'fetchtastic/core'; const input1: SearchParamInput = 'key=value'; const input2: SearchParamInput = new URLSearchParams('key1=value1&key2=value2'); const input3: SearchParamInput = [ - ['key', 'value'], - ['key2', true], + ['key', 'value'], + ['key2', true], ]; const input4: SearchParamInput = { key: 'value', key2: 123 }; ``` -## FetchtasticOptions +### FetchtasticOptions -`FetchtasticOptions` is a type alias that represents the options that can be passed to the `fetch` function. It is defined as `Omit`, which means that it includes all of the options from `RequestInit` except for `signal` and `method`. +`FetchtasticOptions` is a type alias that represents the options that can be passed to the `fetch` function. It is defined as: ```typescript -import { FetchtasticOptions } from 'module'; - -const options: FetchtasticOptions = { - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', - // ... -}; +type FetchtasticOptions = Omit; ``` -## FetchOptions +That means it includes all of the options from `RequestInit` except for `signal` and `method` + +### FetchOptions `FetchOptions` is a type alias that extends `RequestInit` and includes the `method` property, which specifies the HTTP method to use for the request. ```typescript -import { FetchOptions } from 'module'; +import { FetchOptions } from 'fetchtastic/core'; const options: FetchOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ key: 'value' }), - // ... + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ key: 'value' }), + // ... }; ``` -## FetchRequestHeader +### FetchRequestHeader `FetchRequestHeader` is a type alias that represents the different types of headers that are most commonly included in a `fetch` request. It includes the following headers: -- `Accept` -- `Content-Type` -- `Authorization` -- `User-Agent` -- `Referer` -- `Cache-Control` -- `Accept-Encoding` -- `Origin` -- `Connection` -- `Cookie` -- `Pragma` -- `If-Modified-Since` +- `Accept` +- `Content-Type` +- `Authorization` +- `User-Agent` +- `Referer` +- `Cache-Control` +- `Accept-Encoding` +- `Origin` +- `Connection` +- `Cookie` +- `Pragma` +- `If-Modified-Since` ```typescript -import { FetchRequestHeader } from 'module'; +import { FetchRequestHeader } from 'fetchtastic/core'; const header: FetchRequestHeader = 'Accept'; ``` -## DataAssertionFn +### DataAssertionFn A function type that asserts and transforms data. It takes an `unknown` data parameter and returns a transformed value of type `T`. ```typescript -import { DataAssertionFn } from 'module'; +import { DataAssertionFn } from 'fetchtastic/core'; const assertData: DataAssertionFn = data => { - if (typeof data === 'number') { - return data * 2; - } - return 0; + if (typeof data === 'number') { + return data * 2; + } + return 0; }; ``` diff --git a/docs/pages/reference/utility.mdx b/docs/pages/reference/utilities.mdx similarity index 91% rename from docs/pages/reference/utility.mdx rename to docs/pages/reference/utilities.mdx index 0aee229..27ac39d 100644 --- a/docs/pages/reference/utility.mdx +++ b/docs/pages/reference/utilities.mdx @@ -1,6 +1,6 @@ # Utility functions -This functions can be imported from `'fetchtastic/utils'`. +The following functions can be imported from `fetchtastic/utils` ## pipe @@ -8,8 +8,6 @@ Pipes the value of an expression into a pipeline of functions. Performs left-to-right function composition, where the first argument is a value, and the remaining arguments must be unary functions. -### Signature - ```typescript export function pipe(a: A): A; export function pipe(a: A, ab: (a: A) => B): B; @@ -47,85 +45,61 @@ Note: The TypeScript type parameter letters (`A`, `B`, `C`, etc.) are used to re A function that returns the same argument passed to it. -### Signature - ```typescript export const identity: (data: T) => T = x => x; ``` -### Usage +The `identity` function is a simple utility that takes an argument and returns it unchanged. It is useful in scenarios where you need to pass a function as an argument but want a no-op function that doesn't modify the input. ```typescript const result = identity('Hello, World!'); console.log(result); // Output: 'Hello, World!' ``` -### Description - -The `identity` function is a simple utility that takes an argument and returns it unchanged. It is useful in scenarios where you need to pass a function as an argument but want a no-op function that doesn't modify the input. - ## noop A no-operation function. -### Signature - ```typescript export const noop = (..._any: unknown[]) => void null; ``` -### Usage +The `noop` function is a utility that does nothing. It is commonly used as a placeholder or a default function when no action is required. ```typescript noop(); // Does nothing ``` -### Description - -The `noop` function is a utility that does nothing. It is commonly used as a placeholder or a default function when no action is required. - ## isHttpMethod Returns `true` if the given value is a valid `HttpMethod`. -### Signature - ```typescript export function isHttpMethod(value: unknown): value is HttpMethod; ``` -### Usage +The `isHttpMethod` function checks whether the provided value is a valid `HttpMethod`. It validates that the value is a string and matches one of the predefined `HttpMethods`. ```typescript const result1 = isHttpMethod('GET'); // true const result2 = isHttpMethod('INVALID'); // false ``` -### Description - -The `isHttpMethod` function checks whether the provided value is a valid `HttpMethod`. It validates that the value is a string and matches one of the predefined `HttpMethods`. - ## StatusCodes A map of HTTP status codes and their corresponding descriptions. -### Signature - ```typescript export const StatusCodes = new Map([ - [100, 'Continue'], - [101, 'Switching Protocols'], - // ... + [100, 'Continue'], + [101, 'Switching Protocols'], + // ... ]); ``` -### Usage +The `StatusCodes` map provides a mapping of HTTP status codes to their descriptive phrases. It can be used to retrieve the description for a given status code. ```typescript const description = StatusCodes.get(200); // 'OK' console.log(description); // Output: 'OK' ``` - -### Description - -The `StatusCodes` map provides a mapping of HTTP status codes to their descriptive phrases. It can be used to retrieve the description for a given status code. diff --git a/docs/pages/usage.mdx b/docs/pages/usage.mdx index 67f257b..f71c106 100644 --- a/docs/pages/usage.mdx +++ b/docs/pages/usage.mdx @@ -1,3 +1,5 @@ +import { Steps } from 'nextra-theme-docs'; + ## Minimal example ### GET @@ -14,9 +16,9 @@ const posts = await api.get('/posts').json(); const api = new Fetchtastic('https://jsonplaceholder.typicode.com'); const body = { - title: 'foo', - body: 'bar', - userId: 1, + title: 'foo', + body: 'bar', + userId: 1, }; await api.post('/posts', body).appendHeader('Content-Type', 'application/json').resolve(); @@ -26,15 +28,17 @@ await api.post('/posts', body).appendHeader('Content-Type', 'application/json'). You safely can reuse previous instances, since every method returns a new instance and does not modify the previous one. -1. Global config + + +### Global config ```typescript const config = new Fetchtastic('https://jsonplaceholder.typicode.com') - .appendHeader('Accept', 'application/json') - .setOptions({ credentials: 'include', mode: 'cors' }); + .appendHeader('Accept', 'application/json') + .setOptions({ credentials: 'include', mode: 'cors' }); ``` -2. Endpoint level config +### Endpoint level config ```typescript const postsAPI = config.searchParams({ page: 1, first: 12, sortBy: 'id' }).url('/api/v1'); @@ -42,20 +46,22 @@ const postsAPI = config.searchParams({ page: 1, first: 12, sortBy: 'id' }).url(' const commentsAPI = config.appendHeader('Content-Type', 'application/json').url('/api/v2'); ``` -3. Send Requests +### Send Requests ```typescript const blogPost = await postsAPI.get('/posts/1').json(); await commentsAPI - .post('/posts/1/comments', { - text: 'Lorem ipsum dolor sit amet', - userId: 1, - postId: 2, - }) - .json(); + .post('/posts/1/comments', { + text: 'Lorem ipsum dolor sit amet', + userId: 1, + postId: 2, + }) + .json(); ``` + + ## Aborting a request Fetch has the ability to abort requests using AbortController and signals under the hood. @@ -65,14 +71,15 @@ Compatible with browsers that support [AbortController](https://developer.mozill const controller = new AbortController(); const api = new Fetchtastic('https://jsonplaceholder.typicode.com'); -api.controller(controller) - .get('/posts/1/comments') - .json() - .then(data => storeData(data)) // should never be called - .catch((e: Error) => { - console.log(e.name); // AbortError - console.log(e.message); // The user aborted a request. - }); +api + .controller(controller) + .get('/posts/1/comments') + .json() + .then(data => storeData(data)) // should never be called + .catch((e: Error) => { + console.log(e.name); // AbortError + console.log(e.message); // The user aborted a request. + }); // Abort it! controller.abort(); ``` @@ -98,27 +105,27 @@ For example, you can do that by using: ```typescript type Post = { - id: number; - title: string; - body: string; + id: number; + title: string; + body: string; }; function isPost(data: unknown): data is Post { - return ( - data != null && - typeof data === 'object' && - 'title' in data && - typeof data.title === 'string' && - 'body' in data && - typeof data.body === 'string' - ); + return ( + data != null && + typeof data === 'object' && + 'title' in data && + typeof data.title === 'string' && + 'body' in data && + typeof data.body === 'string' + ); } export function assertPosts(data: unknown) { - if (data && Array.isArray(data) && data.every(isPost)) { - return data; - } - throw new Error('Invalid data format'); + if (data && Array.isArray(data) && data.every(isPost)) { + return data; + } + throw new Error('Invalid data format'); } const api = new Fetchtastic('https://jsonplaceholder.typicode.com'); @@ -132,9 +139,9 @@ const posts = await api.get('/posts').json(assertPosts); import { z } from 'zod'; const PostSchema = z.object({ - id: z.number(), - title: z.string(), - body: z.string(), + id: z.number(), + title: z.string(), + body: z.string(), }); const PostListSchema = z.array(PostSchema); diff --git a/lib/src/core/Fetchtastic.ts b/lib/src/core/Fetchtastic.ts index 86e1eaa..39fd616 100644 --- a/lib/src/core/Fetchtastic.ts +++ b/lib/src/core/Fetchtastic.ts @@ -2,7 +2,7 @@ import { getNewSearchParms } from '../internals/getNewSearchParms'; import { getResponseParser } from '../internals/getResponseParser'; import { isJsonBody, shouldStringify } from '../internals/shouldStringify'; import { identity } from '../utils'; -import { ErrorCatcher, HttpError } from './HttpError'; +import { HttpError } from './HttpError'; import { DataAssertionFn, FetchOptions, @@ -12,6 +12,11 @@ import { SearchParamInput, } from './types'; +export type ErrorCatcher = ( + error: HttpError, + config: Fetchtastic, +) => void | Promise; + /** * Represents a configurable Fetchtastic instance that can be used to make HTTP requests. * Implements the `ConfigurableFetch` interface. @@ -128,11 +133,11 @@ export class Fetchtastic { } const results = await Promise.all(promises); - return results.find(res => res instanceof Response); + return results.findLast(res => res instanceof Response); } /** - * Adds an abort controller, in order to cancel the request if needed. + * Registers an abort controller, in order to cancel the request if needed. * @param abortController - an `AbortController` instance */ controller(abortController: AbortController) { diff --git a/lib/src/core/HttpError.ts b/lib/src/core/HttpError.ts index 74cc357..a42c870 100644 --- a/lib/src/core/HttpError.ts +++ b/lib/src/core/HttpError.ts @@ -1,15 +1,10 @@ import { StatusCodes } from '../utils/statusCodes'; -import { Fetchtastic } from './Fetchtastic'; import { HttpMethod } from './types'; -export interface ErrorCatcher { - (error: HttpError, config: Fetchtastic): void | Promise | Promise; -} - /** * Represents an error that occurs during an HTTP request made with Fetchtastic. * It encapsulates information about the error, - * including the request URL, HTTP method, response details, and error message. + * including the request URL, status code, response details, and error message. * * @extends Error */ @@ -33,7 +28,7 @@ export class HttpError extends Error { constructor(url: string, method: HttpMethod, response: Response, message?: string) { super(); - this.name = 'FetchError'; + this.name = 'HttpError'; this.url = url; this.method = method; this.status = response.status || 0; diff --git a/lib/src/core/types.ts b/lib/src/core/types.ts index 3dc8acf..2650ee3 100644 --- a/lib/src/core/types.ts +++ b/lib/src/core/types.ts @@ -1,9 +1,7 @@ -export const HttpMethods = ['OPTIONS', 'GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'] as const; +import { HttpMethods, ResponseParsers } from '../internals/constants'; export type HttpMethod = (typeof HttpMethods)[number]; -export const ResponseParsers = ['ArrayBuffer', 'Blob', 'FormData', 'JSON', 'Text'] as const; - export type ResponseParser = (typeof ResponseParsers)[number]; export type SearchParamInput = @@ -33,22 +31,3 @@ export type FetchRequestHeader = | 'If-Modified-Since'; export type DataAssertionFn = (data: unknown) => T; - -export interface ConfigurableFetch { - headers(data?: HeadersInit, replace?: boolean): T; - appendHeader(name: FetchRequestHeader, value: string): T; - appendHeader(name: string, value: string): T; - deleteHeader(name: string): T; - - url(url: URL): T; - url(url: string, replace?: boolean): T; - url(url: string, replace?: boolean): T; - - searchParams(data?: SearchParamInput, replace?: boolean): T; - appendSearchParam(name: string, value: string | number | boolean): T; - deleteSearchParam(name: string): T; - - body(body: BodyInit | null | unknown): T; - - setOptions(options: FetchtasticOptions, replace?: boolean): T; -} diff --git a/lib/src/internals/constants.ts b/lib/src/internals/constants.ts new file mode 100644 index 0000000..2d571af --- /dev/null +++ b/lib/src/internals/constants.ts @@ -0,0 +1,3 @@ +export const HttpMethods = ['OPTIONS', 'GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'] as const; + +export const ResponseParsers = ['ArrayBuffer', 'Blob', 'FormData', 'JSON', 'Text'] as const; diff --git a/lib/src/utils/helpers.ts b/lib/src/utils/helpers.ts index 56f298e..c3e39cd 100644 --- a/lib/src/utils/helpers.ts +++ b/lib/src/utils/helpers.ts @@ -1,4 +1,7 @@ -import { HttpMethod, HttpMethods } from '../core/types'; +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { HttpMethod } from '../core/types'; +import { HttpMethods } from '../internals/constants'; /** * Identity, function that returns the same argument passed to it @@ -8,7 +11,6 @@ export const identity: (data: T) => T = x => x; /** * No operation */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars export const noop = (..._any: unknown[]) => void null; /** diff --git a/lib/test/requests.test.ts b/lib/test/requests.test.ts index a2967b1..1c34447 100644 --- a/lib/test/requests.test.ts +++ b/lib/test/requests.test.ts @@ -1,4 +1,4 @@ -import { HttpError, Fetchtastic } from '../src/core'; +import { Fetchtastic, HttpError } from '../src/core'; const fetchMock = jest.fn(); global.fetch = fetchMock;