Cell - Http Component
The shared HTTP vocabulary of Cell and an HTTP client for calling other services, in the frontend and the backend.
npm install @celljs/httpConstants
HttpStatus (HttpStatus.NOT_FOUND, ...), HttpMethod, MediaType (MediaType.APPLICATION_JSON, ...) and HttpHeaders (HttpHeaders.AUTHORIZATION, ...), used by @celljs/web, @celljs/mvc and @celljs/security.
HTTP client
RestOperations is an HTTP client on the fetch of the runtime (Node.js 18+, Bun, Deno, workerd, browsers), with no third-party dependency. Its API is the familiar subset of axios: get, delete, head, options, post, put, patch and request resolve to { status, statusText, headers, data, config }.
import { Autowired, Component } from '@celljs/core';
import { RestOperations } from '@celljs/http';
@Component()
export class WeatherClient {
@Autowired(RestOperations)
protected readonly rest: RestOperations;
async today(city: string) {
const { data } = await this.rest.get('https://api.example.com/weather', { params: { city } });
return data;
}
}The request config (RequestConfig):
| Option | Meaning |
|---|---|
url, method, baseURL | baseURL is prefixed to url unless url is absolute |
headers | merged with the default headers, case-insensitively; undefined leaves a header out |
params | the query string; an array repeats the key (tag=a&tag=b), undefined / null are left out |
data | the body: a string, URLSearchParams, FormData, Blob, binary data or a stream is sent as is, anything else as JSON (content-type: application/json unless set). GET and HEAD send no body |
timeout | milliseconds; no limit by default |
signal | an AbortSignal that cancels the request |
responseType | json (default: JSON is parsed, anything else stays text), text, arraybuffer, blob or stream (the ReadableStream of the body) |
validateStatus | which statuses resolve, default 2xx; null resolves every status |
withCredentials | in the browser, sends cookies cross-origin too (credentials: 'include') |
Answer headers have lower-case names. A failed request rejects with an HttpClientError whose code says why: CELL_HTTP_BAD_STATUS (the status did not pass validateStatus; error.response holds the answer), CELL_HTTP_TIMEOUT, CELL_HTTP_ABORTED or CELL_HTTP_NETWORK (error.cause is the error of fetch). HttpClientError.is(error, 'CELL_HTTP_BAD_STATUS') tests for one.
try {
await this.rest.post('/orders', order);
} catch (error) {
if (HttpClientError.is(error, 'CELL_HTTP_BAD_STATUS') && error.response!.status === 409) {
// ...
}
throw error;
}http.client in cell.yml holds the defaults of every request, with the options above; the frontend receives it too. The defaults send X-Requested-With: XMLHttpRequest, which tells @celljs/security to answer 401 instead of redirecting to the login page, and withCredentials: true, which only the browser uses.
Interceptors work as in axios, except that both kinds run in the order they were added: rest.interceptors.request.use(config => config) changes the config before the request is sent, rest.interceptors.response.use(onAnswer, onError) the answer or the failure; use returns an id for eject. To add them to the shared instance, inject RestOperations in a component and call use in a @PostConstruct method, or bind your own RestOperationsFactory (rebind: true). new DefaultRestOperations(config) gives a separate client.
On the server, URLs must be absolute (or relative to baseURL). Behind an HTTP proxy, the fetch of Node.js ignores HTTP_PROXY / HTTPS_PROXY unless NODE_USE_ENV_PROXY=1 is set, which only recent Node.js versions support; Bun and Deno read them by themselves.
For calls between Cell applications, @celljs/rpc gives typed proxies instead.