I have developed my server backend using PHP and now I am looking to establish communication between the frontend (typescript) and backend.
For each of my API requests, I desire to receive a standardized response. Hence, every response from the server follows this structure:
type Response<T> = {
status: boolean,
statusCode: number | null,
statusText: string | null,
statusDetails: string | null,
data: T | null
};
Note: Some API requests may not return any data like login
and logout
, in which case the key data
can be null as well.
If the data is returned, it should adhere to the generic type T
associated with the specific API request. For example:
type ApiInitData = {
user: {
online: boolean | null,
name: string | null,
userGroupName: string | null,
language: string | null,
decimals: number | null,
},
language: {
supportedLanguages: string[];
defaultLanguage: string
}
}
Upon successful completion of an API request, I expect to receive an object of type Response<ApiInitData>
. In such cases, the key data
in Response<T>
will not be null
.
My objective is to create a Typescript function that returns a Promise. Within the .then
block of the Promise, I aim to control the subsequent actions, such as displaying an error message.
Example:
Api.login(username, password).then((response) => {
if(response.status === true){
// Proceed with the operation
}else{
// An error has occurred
console.log(response.statusCode);
console.log(response.statusText);
}
});
In case of an error, the Promise should be coerced into the Response<null>
type to ensure consistency in the returned object format.
The following code snippet illustrates my approach:
//
// Login Example
//
static login = (username: string, password: string) => {
const data = {
'api': 'login',
'username': username,
'password': password
};
return this.request<Response<null>>(data);
}
//
// Initialization Data Example
//
static getInitData = () => {
const data = {
'api': 'getInitializationData'
};
return this.request<Response<ApiInitData | null>>(data);
}
//
// Universal request function for all API calls
//
static request = <T>(data: object, options: RequestInit = {}, url: string | null = null) => {
const requestOptions = this.prepareRequest(data, options, url);
return fetch(requestOptions.url, requestOptions.options)
.then((response) => {
if (response.ok) {
return response.json() as Promise<T>;
} else {
const result: Response<null> = {
status: false,
statusCode: response.status,
statusText: response.statusText,
statusDetails: null,
data: null,
}
return new Promise<Response<null>>((resolve) => { resolve(result) });
}
}).catch((error) => {
const result: Response<null> = {
status: false,
statusCode: 503,
statusText: error,
statusDetails: null,
data: null,
}
return new Promise<Response<null>>((resolve) => { resolve(result) });
});
}
The typescript compiler is throwing an error message: The argument of type "(response: Response) => Promise | Promise<Response>" cannot be assigned to the parameter of type "(value: Response) => T | PromiseLike". (line 4 of my request function).
I am unsure how to resolve this issue, so I have two questions:
- How can I rectify the error?
- Is my approach a commonly used solution to this problem? Or is there a different recommended approach currently prevalent in this scenario?