5.2 KiB
5.2 KiB
HTTP Requests: req( )
The req function creates a reactive HTTP request controller. It returns signals for loading, error, and data, plus a run method to execute the request and an abort method to cancel it. All signals update automatically as the request progresses.
Function Signature
req(config: {
url: string;
method?: string; // default: 'GET'
headers?: Record<string, string>;
}): {
run: (body?: any) => Promise<any>;
abort: () => void;
loading: Signal<boolean>;
error: Signal<string | null>;
data: Signal<any | null>;
}
| Parameter | Type | Required | Description |
|---|---|---|---|
url |
string |
Yes | The endpoint to call. |
method |
string |
No | HTTP method ('GET', 'POST', etc.). Default 'GET'. |
headers |
object |
No | Custom headers (will be merged with defaults). |
Returns: A controller object with reactive signals and methods.
Availability:
reqis exported from the SigPro module. In ESM you must import it (import { req } from 'sigpro') or inject all globals viasigpro(). In the IIFE classic script, it is automatically available onwindow. The examples below assume the function is already in scope.
Usage Patterns
1. Basic GET Request
const users = req({ url: '/api/users' });
// Start the request
users.run().catch(console.error);
// React to the response
watch(() => {
if (users.loading()) console.log('Loading...');
if (users.error()) console.error(users.error());
if (users.data()) console.log('Data:', users.data());
});
2. POST Request with Body
const createUser = req({ url: '/api/users', method: 'POST' });
const handleSubmit = async (formData) => {
try {
await createUser.run(formData);
alert('User created!');
} catch (err) {
// Error already in createUser.error()
}
};
3. Aborting a Request
const search = req({ url: '/api/search' });
// Abort if the user types again quickly
let timeout;
input({ onInput: (e) => {
clearTimeout(timeout);
search.abort(); // cancel previous in-flight request
timeout = setTimeout(() => search.run({ q: e.target.value }), 300);
}});
4. Reactive UI with Signals
const profile = req({ url: '/api/me' });
const App = () =>
div([
when(() => profile.loading(),
() => div("Loading...")
),
when(() => profile.error(),
() => div("Error: " + profile.error())
),
when(() => profile.data(),
() => div([
h2(profile.data().name),
p(profile.data().email)
])
)
]);
profile.run();
mount(App, '#app');
Request Lifecycle
When you call run(body?):
- Any previous request is aborted automatically.
loadingbecomestrue.erroris cleared.- A 10‑second timeout is set (auto‑abort).
- The request is sent using
fetch. - On success:
datais set with the parsed JSON,loadingbecomesfalse. - On error:
erroris set with the message,datais cleared,loadingbecomesfalse.
Important: The response body is parsed as JSON. If the response is not OK or the JSON parsing fails,
erroris set and the promise rejects.
Automatic Headers
- If
bodyis a plain object or array, the request automatically includesContent-Type: application/json(unless you override it inheaders). - If
bodyis aFormDatainstance, theContent-Typeis not set (browser will set it automatically with the correct boundary). - Other headers can be added via the
headersoption.
const upload = req({
url: '/upload',
method: 'POST',
headers: { 'X-Custom': 'value' }
});
const formData = new FormData();
formData.append('file', fileInput.files[0]);
upload.run(formData);
Error Handling
- Network errors, timeouts, and HTTP error statuses (4xx, 5xx) all set
errorand cause the promise to reject. - The
errorsignal contains a human‑readable message. - Abort errors (calling
abort()or timeout) are silent – they do not seterroror reject the promise?
Actually, according to the source: ife.name === 'AbortError', it does not callerror(e.message), but the promise still rejects with the AbortError. You can handle it with.catch()if needed.
Complete Example
const api = req({ url: 'https://jsonplaceholder.typicode.com/posts/1' });
const App = () =>
div({ class: "demo" }, [
when(() => api.loading(), () => p("⏳ Loading...")),
when(() => api.error(), () => p("❌ " + api.error())),
when(() => api.data(), () => div([
h2(api.data().title),
p(api.data().body)
])),
button({
onClick: () => api.run(),
disabled: () => api.loading()
}, "Fetch")
]);
mount(App, '#app');
Summary
| Member | Type | Description |
|---|---|---|
run(body?) |
(any) => Promise |
Starts the request. Returns a promise. |
abort() |
() => void |
Cancels the current request. |
loading |
Signal<boolean> |
true while request is in flight. |
error |
Signal<string|null> |
Contains an error message, or null. |
data |
Signal<any|null> |
Contains the parsed response (JSON), or null. |