Skip to content

Cell - Http Component ​

The shared HTTP vocabulary of Cell and an HTTP client for calling other services, in the frontend and the backend.

bash
npm install @celljs/http

Constants ​

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 }.

ts
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):

OptionMeaning
url, method, baseURLbaseURL is prefixed to url unless url is absolute
headersmerged with the default headers, case-insensitively; undefined leaves a header out
paramsthe query string; an array repeats the key (tag=a&tag=b), undefined / null are left out
datathe 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
timeoutmilliseconds; no limit by default
signalan AbortSignal that cancels the request
responseTypejson (default: JSON is parsed, anything else stays text), text, arraybuffer, blob or stream (the ReadableStream of the body)
validateStatuswhich statuses resolve, default 2xx; null resolves every status
withCredentialsin 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.

ts
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.