fusionauth-typescript-client
fusionauth-typescript-client copied to clipboard
Improve ClientResponse typing
Currently the Client Response is typed as:
export default class ClientResponse<T> {
public statusCode: number;
public response: T;
public exception: Error;
wasSuccessful() {
return this.statusCode >= 200 && this.statusCode < 300;
}
}
The thing is if a API function returns a value it is always a successful response, because in cause of an error the Error is thrown (ergo the function does not return normally).
try {
const response = await client.retrieveRefreshTokens(user);
if (response.wasSuccessful()) { // Always true
const token = response.response.refreshToken; // response.response: RefreshTokenResponse
const error = response.exception.message; // No compile error, but will throw a `exception is undefined` error
}
} catch (e) {
console.log(e); // e is a ClientResponse without response
}
So both response
and exception
properties should be marked as optional:
export default class ClientResponse<T> {
public statusCode: number;
public response?: T;
public exception?: Error;
wasSuccessful() {
return this.statusCode >= 200 && this.statusCode < 300;
}
}
But because either the function returns a response or throws an exception they should be the same type:
class ClientResponse<T> {
public statusCode: number;
public response: T;
}
class ErrorResponse<T> {
public statusCode: number;
public exception: Error;
}
wasSuccessful
can be dropped in this case, because the function either returns something (wasSuccessful = true
) or it throws an error (wasSuccessful = false
).
As an alternative functions could never throw, but return either ClientResponse<T>
or ErrorResponse
:
type ClientResponse<T> = {
statusCode: number;
response: T;
wasSuccessful: true;
}
type ErrorResponse = {
statusCode: number;
exception: Error;
wasSuccessful: false;
}
type ClientResponse<T> = Response<T> | ErrorResponse;
I used types instead of classes because of type-hinting:
const response = await client.retrieveRefreshTokens(userId);
if (response.wasSuccessful) {
const token = response.response.refreshToken; // response.response: RefreshTokenResponse
} else {
throw new Error(response.exception.message); // response.exception: Error
}