I am currently working in a project based on a microservices architecture pattern.
Services are wired up by HTTP calls. They eventually call each others for fetching or putting some data.
I am working on the web page which envelops the whole microservices. I mean, the web page actually needs to send and retrieve data from all the services (except one).
There are several times where the web page behaves as a broker, i.e.:
Fetch some data from service 1,
fetch some data from service 2.
Send something to service 3 based on previous responses.
That's why I decided to implement some functions to avoid lots of duplicated code chunks. This is what I call the promise-based HTTP abstraction layer.
I am developing the web page in Typescript, by the way.
I want to mention what my request
function is. The request
function performs HTTP requests, unsurprisingly.
It is an axios call wrapper. However, for the sake of simplicity, since it is not that important I'll show only the header.
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
declare function request<TResult>(method: HttpMethod, url: string, body?: any): Promise<TResult>;
And, for comfort, I declared its prefixed argument different calls
const get = request.bind(this, 'GET');
const post = request.bind(this, 'POST');
// And so on ...
But I refactored this to take profit of the request
type argument
const get = <TResult>(url: string, body: any) => request<TResult>('GET', url, body);
const post = <TResult>(url: string, body: any) => request<TResult>('POST', url, body);
// And so on ...
And, a waitAll
function which requests to call the URLs passed in and returns a single Promise
which will be resolved whenever all the requests are done.
interface HttpRequest<TResult> {
url: string;
request: (url: string, body?: any) => Promise<TResult>;
body?: any;
};
const waitAll = (...reqs: HttpRequest<any>[]): Promise<any[]> =>
Promise.all(reqs.map(req => req.request(req.url, req.body)));
With this, I can do something like
(async () => {
const req1: HttpRequest<FirstResponse> = {
url: `http://example.com/one`,
request: post,
body: {
id: 9,
data: [`hi`, `there`]
}
};
const req2: HttpRequest<SecondResponse> = {
url: `http://example.com/one`,
request: get
};
const results = await waitAll(req1, req2);
const [r1, r2] = results;
const body = { ...r1, ...r2 };
const finalResponse = await post<ThirdResponse>(`http://example.com/three`, body);
// do something w/ final response
})();
This is working but I notice some wrong things.
It feels weird the HttpResponse<>
object, that wraps the url
and the body
that, in fact, will be called inside its own request
property function.
And when I call waitAll
, I'd expect my r1
and r2
to be of the matching type (I mean, r1
must be
of type FirstResponse
, and r2
must be of type SecondResponse
) just like finalResponse
is of type ThirdResponse
.
I am pretty sure there are lots of improvements to achieve a better-looking API and, the most important thing, take profit of Typescript type safety.
Any thoughts are welcome.