Skip to content
Dimitrios

FP-TS ReaderTaskEither

Using ReaderTaskEither to interact with a database in FP-TS

ReaderTaskEither
import * as A from 'fp-ts/Array';
import { pipe } from 'fp-ts/function';
import * as O from 'fp-ts/Option';
import * as RTE from 'fp-ts/ReaderTaskEither';
import * as TE from 'fp-ts/TaskEither';
 
type Item = { id: string; name: string; year: number };
 
const items: Array<Item> = [
  { id: '1', name: 'foo', year: 2020 },
  { id: '2', name: 'bar', year: 2021 },
  { id: '3', name: 'baz', year: 2022 },
];
 
type Database = {
  findById: (id: string) => TE.TaskEither<Error, Item>;
  findManyByYear: (year: number) => TE.TaskEither<Error, Array<Item>>;
  findManyByName: (name: string) => TE.TaskEither<Error, Array<Item>>;
};
 
const createDB = (): Database => {
  return {
    findById: (id) =>
      pipe(
        items,
        A.findFirst((item) => item.id === id),
        O.fold(
          () => TE.left(new Error('not found')),
          (item) => TE.right(item)
        )
      ),
    findManyByYear: (year) =>
      pipe(
        items,
        A.filter((item) => item.year === year),
        (items) => TE.right(items)
      ),
    findManyByName: (name) =>
      pipe(
        items,
        A.filter((item) => item.name === name),
        (items) => TE.right(items)
      ),
  };
};
 
type Deps = Pick<Database, 'findById'>;
 
function getItemById(id: string): RTE.ReaderTaskEither<Deps, Error, Item> {
  return pipe(
    RTE.ask<Deps>(),
    RTE.chainTaskEitherK(({ findById }) => findById(id))
  );
}
 
function createDatabaseAndGetId(): TE.TaskEither<Error, Item> {
  const { findById } = createDB();
  return pipe(
    { findById }, // context
    getItemById('1')
  );
}