HTTP & Adhoc Services
These two services form the HTTP communication layer of the application. httpService is the low-level transport that abstracts platform differences, while adhocService is the high-level wrapper that every feature and library should use for backend calls. Understanding both is important, but in practice you will almost always use adhocService.
httpService
Introduction
httpService is the low-level HTTP transport layer. It exists because the application must run both in a web browser (where Angular's HttpClient is used) and on native mobile devices (where the Cordova HTTP plugin bypasses CORS restrictions). Rather than scattering if (cordova) ... else ... checks throughout the codebase, httpService encapsulates this branching in one place. It also integrates online/offline detection, so any HTTP error automatically triggers a connectivity check before being propagated to callers.
When to use
Use httpService directly only when you need to make a request that should not include the automatic session/device/language/place parameters that adhocService adds. Examples include:
- Requests to third-party APIs (Google Maps, external services)
- The internal
check()call for server reachability - Requests during the very early bootstrap phase before
stateServicehas session data
For all backend API calls, use adhocService instead.
How it works
-
Platform branching: On every
get(),post(), orhead()call, the service checksplatformService.is('cordova'). If true, it delegates to the Cordova HTTP plugin (this.ionic.sendRequest). Otherwise, it uses Angular'sHttpClient. -
Offline short-circuit: If the device is known to be offline, every request immediately returns an error observable with
{ status: 0 }instead of attempting a network call. -
Error handling and connectivity verification: When any HTTP request fails, the service does not immediately propagate the error. Instead, it calls
onlineService.checkOnline(), which pings the backend'sonlineendpoint with up to 2 retries (250 ms apart). If the server is reachable, the original HTTP error is propagated (it was a real server error). If the server is unreachable after 2 retries, the status transitions to offline and a polling loop begins until connectivity is restored. -
Lazy initialisation: The
onlineServiceis created lazily on first access, which means connectivity monitoring does not begin until the first HTTP operation or connectivity check.
import { httpService } from '@unpispas/upp-base';
constructor(private http: httpService) {}
Types
type HttpResponseTypes = "json" | "blob";
type HttpSerializer = "urlencoded" | "json" | "utf8" | "multipart" | "raw";
Properties
| Property | Type | Description |
|---|---|---|
IsOnline | boolean | Current internet connection status. Reflects the combined result of browser/Cordova events and server reachability checks. |
OnOnline | Observable<boolean> | Emits whenever the online/offline status changes. Subscribe to this to update UI indicators or pause/resume operations. |
Static properties
| Property | Type | Description |
|---|---|---|
httpService.headers | object | Default request headers: Accept: text/html, Content-Type: application/x-www-form-urlencoded. Used as the fallback when no custom headers are provided. |
Methods
| Method | Signature | Description |
|---|---|---|
get | (url, withCredentials?, headers?, responseType?, timeout?): Observable<any> | HTTP GET request. Returns an offline error observable when not connected. Default timeout: 60,000 ms. withCredentials defaults to true to send cookies for same-origin requests. |
post | (url, data, headers?, responseType?, serializer?, timeout?): Observable<any> | HTTP POST request. Default serializer: 'json'. For multipart uploads, pass serializer: 'multipart' and omit the Content-Type header (the browser sets it automatically with boundary). Default timeout: 60,000 ms. |
head | (url, headers?): Observable<any> | HTTP HEAD request. Compares the Content-Type header in the response against the one in the request headers to verify the resource type matches expectations. Useful for checking if a URL points to an expected file type. |
check | (): Observable<any> | Pings the backend online endpoint (AppConstants.baseURL + 'online') to verify server reachability. Used internally by the online-monitoring subsystem but can also be called manually. |
Online/offline monitoring
The service internally creates an onlineService that:
- Cordova: subscribes to the
Networkplugin'sonConnect/onDisconnectevents and checksnetwork.type !== 'none'. - Browser: listens to
windowonline/offlineevents and readsnavigator.onLine. - Server check: on any HTTP error, performs a
check()call with up to 2 retries (250 ms apart). After 2 failures the status transitions to offline, and the service enters a polling loop (waitOnline) that retries every 500 ms until the server responds.
This dual-layer approach (local connectivity events + server reachability) means the IsOnline flag reflects true end-to-end connectivity, not just whether the device has a network interface.
Usage examples
Basic GET request
this.http.get('https://api.example.com/data').subscribe({
next: (data) => console.log(data),
error: (err) => {
if (err.status === 0) {
console.log('Device is offline');
} else {
console.error('Server error:', err.status);
}
}
});
Monitoring connectivity changes in a component
ngOnInit() {
this.http.OnOnline.subscribe(online => {
this.connectionIcon = online ? 'wifi' : 'wifi-off';
});
}
POST with custom serializer (Cordova multipart)
this.http.post(url, formData, {}, null, 'multipart').subscribe(response => {
console.log('Upload complete');
});
adhocService
Introduction
adhocService is the high-level request helper that the rest of the application should use for all backend communication. It wraps httpService and automatically injects four query parameters into every request: device, session, lang, and place. These parameters identify the calling device, authenticate the request, specify the language for server-side translations, and scope the request to the active place (establishment). This eliminates the need for every caller to manually construct these parameters.
When to use
Use adhocService.DoRequest() for every request to the UnPisPas backend. Use adhocService.Download() when you need to download a file through the server's download proxy.
Only fall back to httpService directly for third-party API calls or very early bootstrap requests where session data is not yet available.
How it works
import { adhocService } from '@unpispas/upp-base';
constructor(private adhoc: adhocService) {}
Constructor dependencies
| Dependency | Purpose |
|---|---|
languageService | Provides the lang query parameter so the server returns translated error messages. |
stateService | Provides device, session, and place query parameters for authentication and scoping. |
httpService | Underlying transport layer. |
toastService | Shows error toasts on HTTP failures (when showtoast is true). |
downloadService | Used by the Download method to fetch file content. |
Methods
DoRequest
DoRequest(
url: string,
params?: any,
post?: any,
showtoast?: boolean,
headers?: any,
responseType?: HttpResponseTypes | null,
serializer?: HttpSerializer | null,
timeout?: number | null
): Promise<any>
| Parameter | Default | Description |
|---|---|---|
url | -- | Relative URL appended to AppConstants.baseURL. Example: 'model/list' becomes https://server.com/api/model/list. |
params | null | Key-value object added as query parameters. Example: { type: 'products' } adds &type=products. |
post | null | Request body. If provided, a POST is sent; otherwise GET. Pass an object and it will be JSON-serialized. |
showtoast | true | Whether to show a danger toast on failure. Set to false for background requests where you handle errors silently. |
headers | null | Custom headers. Falls back to httpService.headers if not provided. |
responseType | null | Response type override ('json' or 'blob'). |
serializer | null | Cordova serializer override. Only relevant on native mobile. |
timeout | null | Request timeout in ms. Defaults to 60,000 ms via httpService. |
Automatically appended query parameters: device, session, lang, place. These are read from stateService and languageService at call time, so they always reflect the current state.
Download
Download(
url: string,
filename: string,
headers?: any,
download?: boolean,
target?: string | null
): Promise<boolean>
Downloads a file using downloadService, creates a Blob, and either triggers a browser download or opens the file in a new tab.
- Shows the loading indicator (
toastService.ShowWait()) during the fetch. - Creates an anchor element, sets
hrefto an object URL, and clicks it programmatically. - Cleans up the object URL after 60 seconds.
- Returns
trueon success,falseif the download fails.
Usage examples
GET request -- fetch a list of products
// adhocService automatically appends ?device=X&session=Y&lang=Z&place=W&type=products
const data = await this.adhoc.DoRequest('model/list', { type: 'products' });
if (data.errorcode === 0) {
this.products = data.payload;
}
POST request -- save data to the server
// When a `post` body is provided, the request becomes a POST
const result = await this.adhoc.DoRequest('model/save', null, {
name: 'New Product',
price: 9.99
});
Silent request -- no error toast
// Pass showtoast=false for background polling or optional features
try {
const status = await this.adhoc.DoRequest('system/status', null, null, false);
this.serverVersion = status.version;
} catch {
// silently ignore -- this is not critical
}
Download a PDF report
const success = await this.adhoc.Download(
'https://server.com/reports/daily.pdf',
'daily-report.pdf',
{ 'Content-Type': 'application/pdf' }
);
if (!success) {
this.toast.ShowAlert('danger', 'Download failed');
}
Open a file in a new browser tab instead of downloading
await this.adhoc.Download(
'https://server.com/reports/daily.pdf',
'', // filename is ignored when download=false
{ 'Content-Type': 'application/pdf' },
false, // do not download -- open instead
'_blank' // target window
);
Common patterns
Error handling in a component
try {
const response = await this.adhoc.DoRequest('tickets/close', null, { ticketId });
if (response.errorcode !== 0) {
this.toast.ShowAlert('warning', `Server error: ${response.errorcode}`);
return;
}
// success path
} catch (error) {
if (error.status === 0) {
// Device is offline -- adhocService already shows a toast if showtoast=true
return;
}
// Other HTTP error -- toast already shown by adhocService
}
Checking the backend response pattern
The UnPisPas backend always returns { errorcode: number, ...payload }. An errorcode of 0 means success. Any other value is an application-level error. This is distinct from HTTP-level errors (status codes), which are caught by httpService and trigger the offline check.
Gotchas and tips
- Do not construct
AppConstants.baseURLyourself --DoRequestprepends it automatically. Pass only the relative path. paramsvalues are not URL-encoded byadhocService-- if you pass user input, ensure it is safe or encode it yourself.- Offline requests reject immediately with
{ status: 0 }. Check for this in your catch handler if you need to distinguish offline from server errors. - The
showtoastparameter istrueby default. For background or polling requests, always passfalseto avoid flooding the user with error toasts. Downloaduses the server-side download proxy (common/download.php), which fetches the file and returns it as base64. This means the file is fetched by the server, not directly by the browser. This is by design to handle authenticated or cross-origin resources.
Related services
| Service | Relationship |
|---|---|
stateService | Provides device, session, and place for every request. |
languageService | Provides the lang parameter and translates error messages. |
toastService | Displays error toasts and the loading indicator during downloads. |
downloadService | Handles the actual file download for adhocService.Download(). |
viewService | Exposes IsOnline / OnOnline from httpService for UI components. |