Skip to content

Commit af797d1

Browse files
anil-uipathclaude
andcommitted
fix: address PR review comments [APPS-35694]
Concise comments throughout: http.types.ts, http-request.ts, retry-policy.ts and fetch-with-retry.ts all had JSDoc and inline comments that explained more than the reader needs. Trimmed to the non-obvious part in each case. Moves the httpRequest guide out of error-handling.md into its own docs/http-requests.md page with an mkdocs nav entry, leaving a short pointer behind. The helper is a feature, not an error-handling topic, and it had grown to dominate a page about error types. Adds `redirected` to HttpResponse. `Response.type` was raised as a possible addition but three of its five values are unreachable here — `opaque` needs `mode: 'no-cors'`, `opaqueredirect` needs `redirect: 'manual'`, and neither is exposed, while `error` only comes from a constructed `Response.error()`. That leaves basic/cors, which is constant in Node. `redirected` is the property that actually varies, and it is the only signal a caller gets when an API silently redirects to an HTML login page and returns 200. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent eaddabb commit af797d1

10 files changed

Lines changed: 213 additions & 260 deletions

File tree

docs/error-handling.md

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -251,114 +251,4 @@ try {
251251
console.log('Debug info:', JSON.stringify(debugInfo, null, 2));
252252
}
253253
}
254-
```
255-
## The `httpRequest` Helper
256-
257-
`httpRequest` is a general-purpose HTTP utility for calling any URL. It carries no UiPath
258-
authentication, so it follows a different error contract from the SDK's service methods.
259-
260-
**A status the server returned is never an exception.** A 404 or a 500 comes back as a resolved
261-
response with `ok: false` — branch on the status rather than catching:
262-
263-
```typescript
264-
import { httpRequest } from '@uipath/uipath-typescript';
265-
266-
const response = await httpRequest('https://api.example.com/v1/orders');
267-
268-
if (response.ok) {
269-
console.log(response.data);
270-
} else {
271-
console.log('Request failed with status', response.status);
272-
}
273-
```
274-
275-
**`data` is `unknown`.** The body is whatever the server sent — the shape you expect on a success,
276-
an error payload on a 4xx or 5xx, and nothing at all on a 204. Give it a type once you know which
277-
you have:
278-
279-
```typescript
280-
if (response.ok) {
281-
const order = response.data as { id: string };
282-
console.log(order.id);
283-
}
284-
```
285-
286-
**A request that never produced a response still throws.** DNS failures, refused connections,
287-
a timeout, and caller cancellation all surface as a `NetworkError`:
288-
289-
```typescript
290-
import { httpRequest, NetworkError } from '@uipath/uipath-typescript';
291-
292-
try {
293-
await httpRequest('https://api.example.com/v1/orders', { timeoutMs: 5000 });
294-
} catch (error) {
295-
if (error instanceof NetworkError) {
296-
console.log('The request never reached the server:', error.message);
297-
}
298-
}
299-
```
300-
301-
One other case throws: asking for `responseType: 'json'` explicitly when the body does not parse
302-
raises a `ServerError`. Without an explicit `responseType`, an unparseable body is returned as raw
303-
text instead — auto-detection is a guess, and a host can label an HTML error page as JSON.
304-
305-
### Retries and backoff
306-
307-
The idempotent methods — `GET`, `HEAD`, `PUT`, `DELETE`, `OPTIONS`, per RFC 9110 — are retried up
308-
to twice by default on a transient failure: a transport error, or a `408`, `429`, `500`, `502`,
309-
`503`, or `504`. `POST` and `PATCH` are not retried, since a replayed `POST` can create the same
310-
resource twice. Pass `retry` to change any of that:
311-
312-
```typescript
313-
const response = await httpRequest('https://api.example.com/v1/orders', {
314-
method: 'POST',
315-
body: { sku: 'ABC-123' },
316-
timeoutMs: 10000, // bounds one attempt; each retry gets a fresh timeout
317-
retry: {
318-
maxRetries: 4,
319-
initialDelayMs: 1000, // the first delay; later ones grow from it
320-
backoffStrategy: 'exponential', // 1s, 2s, 4s, 8s
321-
backoffFactor: 2,
322-
backoffMaxDelayMs: 30000,
323-
retryMethods: ['GET', 'HEAD', 'POST']
324-
}
325-
});
326-
```
327-
328-
`backoffStrategy` controls how the delay grows, given an `initialDelayMs` of `d` and a `backoffFactor` of `f`:
329-
330-
| Strategy | Delays |
331-
|---|---|
332-
| `constant` | `d, d, d, …` |
333-
| `linear` | `d, 2d, 3d, …` |
334-
| `exponential` (default) | `d, d×f, d×f², …` |
335-
336-
`backoffFactor` applies only to `exponential`; the other strategies ignore it. Every computed delay
337-
is capped at `backoffMaxDelayMs`.
338-
339-
A `Retry-After` response header overrides the computed delay unless `respectRetryAfter` is `false`.
340-
It is honoured as the server sent it — `backoffMaxDelayMs` does not apply to it — so set
341-
`maxRetryAfterMs` if you need a ceiling on how long a server can ask you to wait.
342-
343-
Set `retryNetworkErrors: false` to retry only on response status codes, leaving connection
344-
failures, DNS errors, and timeouts to fail on the first attempt.
345-
346-
`timeoutMs` sits outside `retry` deliberately — it bounds a single attempt whether or not
347-
retrying is enabled, so a call that just needs a deadline does not have to open a bag of retry
348-
options to get one:
349-
350-
```typescript
351-
// one attempt, five second ceiling, no retrying involved
352-
const response = await httpRequest('https://api.example.com/v1/orders', { timeoutMs: 5000 });
353-
```
354-
355-
Two cases never retry, regardless of settings: a `ReadableStream` body (it is consumed by the first
356-
attempt and cannot be replayed) and a request cancelled through `signal`.
357-
358-
Use `wait` to pause between calls of your own:
359-
360-
```typescript
361-
import { wait } from '@uipath/uipath-typescript';
362-
363-
await wait(1000); // milliseconds
364-
```
254+
```

docs/helpers.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Helper Methods
2+
3+
Standalone functions you call directly — no `UiPath` instance, no service class.
4+
5+
- **`httpRequest`** calls any URL, with optional retries, backoff, and a per-attempt timeout. It
6+
sends no UiPath authentication and adds no UiPath headers, so it is for third-party endpoints —
7+
use the SDK's service methods for UiPath itself.
8+
- **`wait`** pauses for a duration, useful between calls you are pacing yourself.
9+
10+
## Error contract
11+
12+
Because it proxies arbitrary third-party calls, `httpRequest` follows a different contract from the
13+
SDK's service methods.
14+
15+
**A status the server returned is never an exception.** A 404 or a 500 comes back as a resolved
16+
response with `ok: false` — branch on the status rather than catching:
17+
18+
```typescript
19+
import { httpRequest } from '@uipath/uipath-typescript/core';
20+
21+
const response = await httpRequest('https://api.example.com/v1/orders');
22+
23+
if (response.ok) {
24+
console.log(response.data);
25+
} else {
26+
console.log('Request failed with status', response.status);
27+
}
28+
```
29+
30+
**`data` is `unknown`.** The body is whatever the server sent — the shape you expect on a success,
31+
an error payload on a 4xx or 5xx, and nothing at all on a 204. Give it a type once you know which
32+
you have:
33+
34+
```typescript
35+
if (response.ok) {
36+
const order = response.data as { id: string };
37+
console.log(order.id);
38+
}
39+
```
40+
41+
**A request that never produced a response still throws.** DNS failures, refused connections,
42+
a timeout, and caller cancellation all surface as a `NetworkError`:
43+
44+
```typescript
45+
import { httpRequest, NetworkError } from '@uipath/uipath-typescript/core';
46+
47+
try {
48+
await httpRequest('https://api.example.com/v1/orders', { timeoutMs: 5000 });
49+
} catch (error) {
50+
if (error instanceof NetworkError) {
51+
console.log('The request never reached the server:', error.message);
52+
}
53+
}
54+
```
55+
56+
One other case throws: asking for `responseType: 'json'` explicitly when the body does not parse
57+
raises a `ServerError`. Without an explicit `responseType`, an unparseable body is returned as raw
58+
text instead — auto-detection is a guess, and a host can label an HTML error page as JSON.
59+
60+
## Retries and backoff
61+
62+
The idempotent methods — `GET`, `HEAD`, `PUT`, `DELETE`, `OPTIONS`, per RFC 9110 — are retried up
63+
to twice by default on a transient failure: a transport error, or a `408`, `429`, `500`, `502`,
64+
`503`, or `504`. `POST` and `PATCH` are not retried, since a replayed `POST` can create the same
65+
resource twice. Pass `retry` to change any of that:
66+
67+
```typescript
68+
const response = await httpRequest('https://api.example.com/v1/orders', {
69+
method: 'POST',
70+
body: { sku: 'ABC-123' },
71+
timeoutMs: 10000, // bounds one attempt; each retry gets a fresh timeout
72+
retry: {
73+
maxRetries: 4,
74+
initialDelayMs: 1000, // the first delay; later ones grow from it
75+
backoffStrategy: 'exponential', // 1s, 2s, 4s, 8s
76+
backoffFactor: 2,
77+
backoffMaxDelayMs: 30000,
78+
retryMethods: ['GET', 'HEAD', 'POST']
79+
}
80+
});
81+
```
82+
83+
`backoffStrategy` controls how the delay grows, given an `initialDelayMs` of `d` and a `backoffFactor` of `f`:
84+
85+
| Strategy | Delays |
86+
|---|---|
87+
| `constant` | `d, d, d, …` |
88+
| `linear` | `d, 2d, 3d, …` |
89+
| `exponential` (default) | `d, d×f, d×f², …` |
90+
91+
`backoffFactor` applies only to `exponential`; the other strategies ignore it. Every computed delay
92+
is capped at `backoffMaxDelayMs`.
93+
94+
A `Retry-After` response header overrides the computed delay unless `respectRetryAfter` is `false`.
95+
It is honoured as the server sent it — `backoffMaxDelayMs` does not apply to it — so set
96+
`maxRetryAfterMs` if you need a ceiling on how long a server can ask you to wait.
97+
98+
Set `retryNetworkErrors: false` to retry only on response status codes, leaving connection
99+
failures, DNS errors, and timeouts to fail on the first attempt.
100+
101+
`timeoutMs` sits outside `retry` deliberately — it bounds a single attempt whether or not
102+
retrying is enabled, so a call that just needs a deadline does not have to open a bag of retry
103+
options to get one:
104+
105+
```typescript
106+
// one attempt, five second ceiling, no retrying involved
107+
const response = await httpRequest('https://api.example.com/v1/orders', { timeoutMs: 5000 });
108+
```
109+
110+
Two cases never retry, regardless of settings: a `ReadableStream` body (it is consumed by the first
111+
attempt and cannot be replayed) and a request cancelled through `signal`.
112+
113+
Use `wait` to pause between calls of your own:
114+
115+
```typescript
116+
import { wait } from '@uipath/uipath-typescript/core';
117+
118+
await wait(1000); // milliseconds
119+
```

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ plugins:
8585
- getting-started.md: Introduction to UiPath TypeScript SDK
8686
- authentication.md: Authentication setup
8787
- pagination.md: Pagination guide
88+
- helpers.md: Helper methods guide
8889
API Reference:
8990
- api/interfaces/AssetServiceModel.md: Asset service methods
9091
- api/interfaces/JobServiceModel.md: Job service methods
@@ -177,6 +178,7 @@ nav:
177178
- Authentication: authentication.md
178179
- OAuth Scopes: oauth-scopes.md
179180
- Pagination: pagination.md
181+
- Helper Methods: helpers.md
180182
- Error Handling: error-handling.md
181183
- API Reference:
182184
- Services:

src/core/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,15 @@ export * from './errors';
5151
// Pagination (common across all services)
5252
export * from '../utils/pagination';
5353

54+
// HTTP helpers for calling non-UiPath endpoints
55+
export { httpRequest } from '../utils/http/http-request';
56+
export { wait } from '../utils/wait';
57+
export type {
58+
HttpRequestInit,
59+
HttpResponse,
60+
RetryOptions,
61+
BackoffStrategy,
62+
} from '../models/common/http.types';
63+
5464
// Export telemetry
5565
export * from './telemetry';

0 commit comments

Comments
 (0)