Try to send an HTTP request with parameter validation.
If the parameter validation fails, throw "parameter invalid" error and stop the following executions.
If the code
of the API response doesn't equal '0'
, throw res.message
error else return the res
.
I want to use try...catch...
statements in the main
function to catch any error mentioned above. And use the correct res.result
in the try
block.
import * as t from 'io-ts';
import * as T from 'fp-ts/lib/Task';
import * as TE from 'fp-ts/lib/TaskEither';
import * as E from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/lib/function';
import { failure } from 'io-ts/lib/PathReporter';
export interface ApiResponse<Result = any> {
code: string;
message: string | null;
result: Result;
}
const GetArticlesByPageRequestDTOCodec = t.type({
currentPage: t.number,
pageSize: t.number,
})
type GetArticlesByPageRequestDTO = t.TypeOf<typeof GetArticlesByPageRequestDTOCodec>;
type GetArticlesByPageQueryPayload = {
pagination: any;
};
const getArticlesByPage = async (data: GetArticlesByPageRequestDTO) => {
return {
code: '0',
message: '',
result: {
resultList: [
{ id: 1, title: 'test' },
{ id: 2, title: 'test1' },
],
totalItem: 100,
},
};
};
const getArticlesByPageTaskEither = (data: GetArticlesByPageRequestDTO) =>
TE.tryCatch<Error, ApiResponse>(
() =>
getArticlesByPage(data).then((res) => {
if (res.code !== '0') {
throw new Error(res.message);
}
return res;
}),
(reason) => new Error(String(reason)),
);
const getArticlesByPageService = ({ pagination }: GetArticlesByPageQueryPayload) => {
const reqDTO: GetArticlesByPageRequestDTO = {
pageSize: pagination.pageSize,
currentPage: pagination.current,
};
return pipe(
reqDTO,
GetArticlesByPageRequestDTOCodec.decode,
E.mapLeft((errors) => {
console.error(
`Validation failed for input: ${JSON.stringify(reqDTO, null, 2)}. Error details: ${failure(errors).join('\n')}`,
);
return new Error('parameter invalid');
}),
TE.fromEither,
TE.chain(getArticlesByPageTaskEither),
)();
};
async function main() {
try {
const res = await getArticlesByPageService({ pagination: { current: 1, pageSize: 10 } });
console.log(res.result)
} catch (e) {
console.error(e)
}
}
But the type of res
is E.Either<Error, ApiResponse<any>>
, so how to convert it to the Promise<ApiResponse<any>>
type.
When I try to use res.result
, got an error:
Property 'result' does not exist on type 'Either<Error, ApiResponse<any>>'.
Property 'result' does not exist on type 'Left<Error>'
There are multiple options. Firstly, you can just match on the Either
in your main function
async function main() {
pipe(
await getArticlesByPageService({
pagination: { current: 1, pageSize: 10 },
}),
E.match(
(e) => console.error(e),
(res) => console.log(res.result)
)
);
}
Another option is to modify the getArticlesByPageService
function to return TE.TaskEither<Error, ApiResponse<any>>
instead of a promise (by simply removing the function evaluation at the very end of the function) and do something like this.
async function main() {
await pipe(
getArticlesByPageService({
pagination: { current: 1, pageSize: 10 },
}),
TE.orElseFirst((e) => TE.fromIO(Console.error(e))),
TE.chainFirstIOK((res) => Console.log(res.result))
)();
}
It is a good idea to perform side-effect only in IO
or Task
containers. Therefore, I'd personally prefere the that one.