create-request 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  - [Installation](#installation)
15
15
  - [Basic Usage](#basic-usage)
16
16
  - [Advanced Usage](#advanced-usage)
17
- - [GraphQL Support](#graphql-support)
17
+ - [GraphQL Support](#graphql-requests)
18
18
  - [Interceptors](#interceptors)
19
19
  - [TypeScript Support](#typescript-support)
20
20
  - [CSRF Protection](#csrf-protection)
@@ -247,13 +247,33 @@ const result = await create
247
247
  .getJson();
248
248
  ```
249
249
 
250
+ #### GraphQL Error Handling
251
+
252
+ GraphQL errors do not cause exceptions by default. Use the `throwOnError` option to make them throw exceptions:
253
+
254
+ ```typescript
255
+ // Throw an error if the GraphQL response contains errors
256
+ try {
257
+ const user = await create
258
+ .post("https://api.example.com/graphql")
259
+ .withGraphQL(
260
+ "query GetUser($id: ID!) { user(id: $id) { name email } }",
261
+ { id: "123" },
262
+ { throwOnError: true }
263
+ )
264
+ .getJson();
265
+ } catch (error) {
266
+ console.error(error.message);
267
+ }
268
+ ```
269
+
250
270
  The `withGraphQL` method automatically:
251
271
 
252
272
  - Formats the body as JSON with `query` and optional `variables` properties
253
273
  - Sets `Content-Type` to `application/json`
254
- - Trims whitespace from the query string
255
274
  - Validates the query is non-empty
256
275
  - Validates variables are a plain object (not arrays or null)
276
+ - Optionally throws errors when GraphQL response contains errors (with `throwOnError: true`)
257
277
 
258
278
  ### Query Parameters Advanced Features
259
279
 
@@ -358,52 +378,6 @@ try {
358
378
 
359
379
  ## Advanced Usage
360
380
 
361
- ### GraphQL Support
362
-
363
- `create-request` provides first-class support for GraphQL with the `withGraphQL` method:
364
-
365
- ```typescript
366
- // Simple query
367
- const query = "query { user { name email } }";
368
- const data = await create
369
- .post("https://api.example.com/graphql")
370
- .withGraphQL(query)
371
- .getJson();
372
-
373
- // Query with variables
374
- const queryWithVars = "query GetUser($id: ID!) { user(id: $id) { name email } }";
375
- const user = await create
376
- .post("https://api.example.com/graphql")
377
- .withGraphQL(queryWithVars, { id: "123" })
378
- .getJson();
379
-
380
- // Mutation
381
- const mutation =
382
- "mutation CreateUser($name: String!) { createUser(name: $name) { id name } }";
383
- const result = await create
384
- .post("https://api.example.com/graphql")
385
- .withGraphQL(mutation, { name: "John Doe" })
386
- .getJson();
387
-
388
- // Complex variables with nested objects
389
- const complexQuery =
390
- "query SearchUsers($filters: UserFilters!) { users(filters: $filters) { id name } }";
391
- const results = await create
392
- .post("https://api.example.com/graphql")
393
- .withGraphQL(complexQuery, {
394
- filters: {
395
- name: "John",
396
- age: 30,
397
- tags: ["active", "verified"],
398
- metadata: {
399
- source: "web",
400
- verified: true,
401
- },
402
- },
403
- })
404
- .getJson();
405
- ```
406
-
407
381
  ### Interceptors
408
382
 
409
383
  Interceptors allow you to modify requests, transform responses, or handle errors globally or per-request. This is perfect for adding authentication tokens, logging, error recovery, and more.
@@ -745,7 +719,7 @@ This library works with all browsers that support the Fetch API:
745
719
 
746
720
  | Feature | create-request | Fetch | Axios | SuperAgent | Got | Ky | node-fetch | Redaxios |
747
721
  | ------------------- | -------------- | ------ | ------- | ---------- | ------- | ------ | ---------- | -------- |
748
- | **Size (min+gzip)** | ~5.5KB | Native | ~13.6KB | ~17.8KB | ~17.8KB | ~3.4KB | ~7.7KB | ~1KB |
722
+ | **Size (min+gzip)** | ~5.8KB | Native | ~13.6KB | ~17.8KB | ~17.8KB | ~3.4KB | ~7.7KB | ~1KB |
749
723
  | **Browser** | Modern | Modern | IE11+ | IE9+ | ❌ No | Modern | ❌ No | Modern |
750
724
  | **Node.js** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
751
725
  | **HTTP/2** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
@@ -1,5 +1,5 @@
1
1
  import { type HttpMethod, RedirectMode, RequestMode, RequestPriority, ReferrerPolicy, CredentialsPolicy } from "./enums.js";
2
- import type { RequestOptions, RetryCallback, CookiesRecord, CookieOptions, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from "./types.js";
2
+ import type { RetryCallback, CookiesRecord, CookieOptions, RequestOptions, GraphQLOptions, ErrorInterceptor, RequestInterceptor, ResponseInterceptor } from "./types.js";
3
3
  import { ResponseWrapper } from "./ResponseWrapper.js";
4
4
  /**
5
5
  * Base class with common functionality for all request types
@@ -16,6 +16,11 @@ export declare abstract class BaseRequest {
16
16
  private responseInterceptors;
17
17
  private errorInterceptors;
18
18
  constructor(url: string);
19
+ /**
20
+ * Get GraphQL options if set (only for BodyRequest subclasses)
21
+ * @returns GraphQL options or undefined
22
+ */
23
+ protected getGraphQLOptions(): GraphQLOptions | undefined;
19
24
  /**
20
25
  * Creates a fluent API for setting enum-based options
21
26
  * Combines direct setter with convenience methods
@@ -52,7 +57,7 @@ export declare abstract class BaseRequest {
52
57
  *
53
58
  * @param timeout - The timeout in milliseconds
54
59
  * @returns The request instance for chaining
55
- * @throws Error if timeout is not a positive number
60
+ * @throws RequestError if timeout is not a positive number
56
61
  *
57
62
  * @example
58
63
  * request.withTimeout(5000); // 5 seconds timeout
@@ -63,7 +68,7 @@ export declare abstract class BaseRequest {
63
68
  *
64
69
  * @param retries - Number of retry attempts before failing
65
70
  * @returns The request instance for chaining
66
- * @throws Error if retries is not a non-negative integer
71
+ * @throws RequestError if retries is not a non-negative integer
67
72
  *
68
73
  * @example
69
74
  * request.withRetries(3); // Retry up to 3 times
@@ -154,6 +159,29 @@ export declare abstract class BaseRequest {
154
159
  LOW: () => BaseRequest;
155
160
  AUTO: () => BaseRequest;
156
161
  };
162
+ /**
163
+ * Sets the integrity hash for subresource integrity verification
164
+ * @param integrity - The integrity hash string (e.g., "sha256-...")
165
+ * @returns The instance for chaining
166
+ * @example
167
+ * request.withIntegrity("sha256-abcdef1234567890...")
168
+ */
169
+ withIntegrity(integrity: string): this;
170
+ /**
171
+ * Sets cache mode - supports both direct call and fluent API
172
+ * @example
173
+ * request.withCache("no-cache")
174
+ * request.withCache(CacheMode.NO_CACHE)
175
+ * request.withCache.NO_CACHE()
176
+ */
177
+ get withCache(): ((cache: string) => BaseRequest) & {
178
+ DEFAULT: () => BaseRequest;
179
+ NO_STORE: () => BaseRequest;
180
+ RELOAD: () => BaseRequest;
181
+ NO_CACHE: () => BaseRequest;
182
+ FORCE_CACHE: () => BaseRequest;
183
+ ONLY_IF_CACHED: () => BaseRequest;
184
+ };
157
185
  /**
158
186
  * Adds query parameters to the request URL
159
187
  * @param params - An object containing the query parameters
@@ -309,9 +337,20 @@ export declare abstract class BaseRequest {
309
337
  * Execute the request and parse the response as JSON
310
338
  *
311
339
  * @returns A promise that resolves to the parsed JSON data
340
+ * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
312
341
  *
313
342
  * @example
314
343
  * const users = await request.getJson<User[]>();
344
+ *
345
+ * @example
346
+ * // Error handling - errors are always RequestError
347
+ * try {
348
+ * const data = await request.getJson();
349
+ * } catch (error) {
350
+ * if (error instanceof RequestError) {
351
+ * console.log(error.status, error.url, error.method);
352
+ * }
353
+ * }
315
354
  */
316
355
  getJson<T = unknown>(): Promise<T>;
317
356
  /**
@@ -347,6 +386,7 @@ export declare abstract class BaseRequest {
347
386
  *
348
387
  * @param selector - Optional function to extract and transform data
349
388
  * @returns A promise that resolves to the selected data
389
+ * @throws {RequestError} When the request fails, JSON parsing fails, or the selector throws an error
350
390
  *
351
391
  * @example
352
392
  * // Get full response
@@ -354,6 +394,16 @@ export declare abstract class BaseRequest {
354
394
  *
355
395
  * // Extract specific data
356
396
  * const users = await request.getData(data => data.results.users);
397
+ *
398
+ * @example
399
+ * // Error handling - errors are always RequestError
400
+ * try {
401
+ * const data = await request.getData();
402
+ * } catch (error) {
403
+ * if (error instanceof RequestError) {
404
+ * console.log(error.status, error.url, error.method);
405
+ * }
406
+ * }
357
407
  */
358
408
  getData<T = unknown, R = T>(selector?: (data: T) => R): Promise<T | R>;
359
409
  /**
@@ -1,5 +1,5 @@
1
1
  import { BaseRequest } from "./BaseRequest.js";
2
- import type { Body } from "./types.js";
2
+ import type { Body, GraphQLOptions } from "./types.js";
3
3
  import type { ResponseWrapper } from "./ResponseWrapper.js";
4
4
  /**
5
5
  * Base class for requests that can have a body (POST, PUT, PATCH)
@@ -7,6 +7,7 @@ import type { ResponseWrapper } from "./ResponseWrapper.js";
7
7
  export declare abstract class BodyRequest extends BaseRequest {
8
8
  protected body?: Body;
9
9
  private bodyType?;
10
+ private graphQLOptions;
10
11
  constructor(url: string);
11
12
  /**
12
13
  * Sets the body of the request
@@ -19,6 +20,7 @@ export declare abstract class BodyRequest extends BaseRequest {
19
20
  *
20
21
  * @param query - The GraphQL query or mutation string
21
22
  * @param variables - Optional variables object to pass with the query
23
+ * @param options - Optional GraphQL-specific options
22
24
  * @returns The request instance for chaining
23
25
  *
24
26
  * @example
@@ -28,13 +30,23 @@ export declare abstract class BodyRequest extends BaseRequest {
28
30
  * @example
29
31
  * const request = new PostRequest('/graphql')
30
32
  * .withGraphQL('mutation { createUser(name: $name) { id } }', { name: 'John' });
33
+ *
34
+ * @example
35
+ * // Throw an error if the GraphQL response contains errors
36
+ * const request = new PostRequest('/graphql')
37
+ * .withGraphQL('query { user { id } }', undefined, { throwOnError: true });
31
38
  */
32
- withGraphQL(query: string, variables?: Record<string, unknown>): this;
39
+ withGraphQL(query: string, variables?: Record<string, unknown>, options?: GraphQLOptions): this;
33
40
  /**
34
41
  * Check if Content-Type header is already set (case-insensitive)
35
42
  */
36
43
  private hasContentType;
37
44
  private setContentTypeIfNeeded;
45
+ /**
46
+ * Get the GraphQL options if set
47
+ * @returns The GraphQL options or undefined
48
+ */
49
+ protected getGraphQLOptions(): GraphQLOptions | undefined;
38
50
  /**
39
51
  * Execute the request and return the ResponseWrapper
40
52
  * Overrides the base implementation to add body handling
@@ -1,3 +1,4 @@
1
+ import type { GraphQLOptions } from "./types.js";
1
2
  /**
2
3
  * Wrapper for HTTP responses with methods to transform the response data
3
4
  * Provides caching and conversion between different response formats.
@@ -10,22 +11,40 @@ export declare class ResponseWrapper {
10
11
  private jsonCache;
11
12
  private blobCache;
12
13
  private bodyUsed;
13
- constructor(response: Response, url?: string, method?: string);
14
+ private graphQLOptions?;
15
+ constructor(response: Response, url?: string, method?: string, graphQLOptions?: GraphQLOptions);
14
16
  get status(): number;
15
17
  get statusText(): string;
16
18
  get headers(): Headers;
17
19
  get ok(): boolean;
18
20
  get raw(): Response;
21
+ /**
22
+ * Check for GraphQL errors and throw if throwOnError is enabled
23
+ * @param data - The parsed JSON data
24
+ * @throws RequestError if GraphQL response contains errors and throwOnError is enabled
25
+ */
26
+ private checkGraphQLErrors;
19
27
  /**
20
28
  * Parse the response body as JSON
21
29
  * Caches the result for subsequent calls.
30
+ * If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
22
31
  *
23
32
  * @returns The parsed JSON data
24
- * @throws Error if the response has already been consumed in a different format
33
+ * @throws {RequestError} When the request fails, JSON parsing fails, GraphQL errors occur (if throwOnError enabled), or body is already consumed
25
34
  *
26
35
  * @example
27
36
  * const data = await response.getJson();
28
37
  * console.log(data.items);
38
+ *
39
+ * @example
40
+ * // Error handling - errors are always RequestError
41
+ * try {
42
+ * const data = await response.getJson();
43
+ * } catch (error) {
44
+ * if (error instanceof RequestError) {
45
+ * console.log(error.status, error.url, error.method);
46
+ * }
47
+ * }
29
48
  */
30
49
  getJson<T = unknown>(): Promise<T>;
31
50
  /**
@@ -34,7 +53,7 @@ export declare class ResponseWrapper {
34
53
  * if the body has already been consumed in those formats.
35
54
  *
36
55
  * @returns The response text
37
- * @throws Error if the response has already been consumed in an incompatible format
56
+ * @throws {RequestError} When reading fails or the response has already been consumed in an incompatible format
38
57
  *
39
58
  * @example
40
59
  * const text = await response.getText();
@@ -46,7 +65,7 @@ export declare class ResponseWrapper {
46
65
  * if the body has already been consumed as text.
47
66
  *
48
67
  * @returns The response as a Blob
49
- * @throws Error if the response has already been consumed in an incompatible format
68
+ * @throws {RequestError} When reading fails or the response has already been consumed in an incompatible format
50
69
  *
51
70
  * @example
52
71
  * const blob = await response.getBlob();
@@ -58,7 +77,7 @@ export declare class ResponseWrapper {
58
77
  * This method consumes the body and should only be called once.
59
78
  *
60
79
  * @returns The response body as a ReadableStream or null
61
- * @throws Error if the response body has already been consumed
80
+ * @throws {RequestError} When the response body has already been consumed
62
81
  *
63
82
  * @example
64
83
  * const stream = response.getBody();
@@ -74,6 +93,7 @@ export declare class ResponseWrapper {
74
93
  *
75
94
  * @param selector - Optional function to extract and transform data
76
95
  * @returns A promise that resolves to the selected data
96
+ * @throws {RequestError} When the request fails, JSON parsing fails, or the selector throws an error
77
97
  *
78
98
  * @example
79
99
  * // Get full response
@@ -81,6 +101,16 @@ export declare class ResponseWrapper {
81
101
  *
82
102
  * // Extract specific data
83
103
  * const users = await response.getData(data => data.results.users);
104
+ *
105
+ * @example
106
+ * // Error handling - errors are always RequestError
107
+ * try {
108
+ * const data = await response.getData();
109
+ * } catch (error) {
110
+ * if (error instanceof RequestError) {
111
+ * console.log(error.status, error.url, error.method);
112
+ * }
113
+ * }
84
114
  */
85
115
  getData<T = unknown, R = T>(selector?: (data: T) => R): Promise<T | R>;
86
116
  }
@@ -72,3 +72,14 @@ export declare enum ReferrerPolicy {
72
72
  NO_REFERRER_WHEN_DOWNGRADE = "no-referrer-when-downgrade",
73
73
  STRICT_ORIGIN_WHEN_CROSS_ORIGIN = "strict-origin-when-cross-origin"
74
74
  }
75
+ /**
76
+ * Cache modes for fetch requests
77
+ */
78
+ export declare enum CacheMode {
79
+ RELOAD = "reload",
80
+ DEFAULT = "default",
81
+ NO_CACHE = "no-cache",
82
+ NO_STORE = "no-store",
83
+ FORCE_CACHE = "force-cache",
84
+ ONLY_IF_CACHED = "only-if-cached"
85
+ }