I have seen many examples of using TaskEither
to say make an hhtp request or read a file etc.
What I am trying to do is simulate finding item in a DB by ID, so possible outputs of the operation could be:
- 找到了那件物品
- 找不到ID为的项目
- 某些错误(即数据库连接)
这样的界面应该是TaskEither<Error, Option<A>>
:
type TaskEO<A> = TaskEither<Error, Option<A>>
由于我将以HTTP响应的形式发送结果(对GET
查询的响应),因此我希望能够清楚地区分上述3种场景.每种类型的响应代码为:
- 200多个有效载荷
- 404
- 500
我已经编写了以下代码,将3种可能的场景处理为相应的HTTP响应:
import * as O from "fp-ts/Option";
import * as TE from "fp-ts/TaskEither";
import * as E from "fp-ts/Either";
import { pipe } from "fp-ts/function";
type TaskEO<A> = TE.TaskEither<Error, O.Option<A>>;
const getGoodStuff = (id: string): TaskEO<string> => TE.of(O.some(`result for ${id}`));
const getBadStuff = (id: string): TaskEO<string> =>
TE.left(new Error(`failed fetching ${id}`));
const getEmptyStuff = (id: string): TaskEO<string> => TE.of(O.none);
getGoodStuff("123")()
.then((e) =>
pipe(
e,
E.fold(
(error) => `500: Internal Server Error`,
(stuff) =>
pipe(
stuff,
O.match(
() => `404: Not Found Error`,
(value) => `200: Yay we got: "${value}"`
)
)
)
)
)
.then(console.log);
您可以将getGoodStuff
调用替换为其他get...Stuff
个函数中的任何一个,以查看它确实正确地处理了不同的响应!很棒的东西!
However, and here is the question for YOU dear reader, I have a feeling that there is a smarter way of structuring this composition. Unfortunately my knowledge of fp-ts is still somewhat limited.
How would YOU write the code above?
Thanks x
EDIT个 我把范围缩小到这样的范围:
enum HttpResponseCode {
OK = 200,
NOT_FOUND = 404,
INTERNAL_SERVER_ERROR = 500
}
type HttpResponse = {
code: HttpResponseCode;
payload: unknown;
}
const toHttpResponse = <A>(e: E.Either<Error, O.Option<A>>): HttpResponse =>
E.fold(
(error) => ({ code: HttpResponseCode.INTERNAL_SERVER_ERROR, payload: "Internal Server Error" }),
O.match(
() => ({ code: HttpResponseCode.NOT_FOUND, payload: "Resource not found" }),
(value) => ({ code: HttpResponseCode.OK, payload: value })
)
)(e)
然后可以通过以下方式与Express路由处理程序一起使用:
async (req, res) => {
await findStuffById(req.params.stuffId)()
.then(toHttpResponse)
.then(({ code, payload }) => res.status(code).send(payload))
}