Dealing with asynchronous operations in a pipeline with fp-ts

I'm currently exploring fp-ts and have been contemplating how to restructure my functions in order to steer clear of nested folds. While many online examples showcase a clean invocation of the pipe function, I am struggling to eliminate the nested folds.

A little background - Essentially, the aim of this code is to generate a Location first and if successful, proceed to create a Station. In case either operation encounters an error, the appropriate error message should be returned to the caller. If everything goes smoothly, a 201 status should be returned.

public async initialize(
    @requestParam('site') site: string,
    @request() req: Request,
    @response() res: Response
  ) {
    //using the same value at the moment
    const nameAndPublicId = LocationService.retailOnlineLocationName(site);
    
    const location: E.Either<ApiError, LocationDTO> = await this.locationService.createLocation(
      site,
      nameAndPublicId,
      nameAndPublicId
    );

    const stationName: string = StationService.retailOnlineStationName(site);

    pipe(
      location,
      E.fold(
        (err: ApiError) => ConfigController.respondWithError(err, res),
        async (loc: LocationDTO) => {
          pipe(
            await this.stationService.createStation(site, stationName, loc.id),
            E.fold(
              (err: ApiError) => ConfigController.respondWithError(err, res),
              (_: StationDTO) => res.status(201).send()
            )
          );
        }
      )
    );
  }

  static respondWithError(err: ApiError, res: Response) {
    res.status(err.statusCode).json(err);
  }

Answer №1

Consider a scenario where we are utilizing the Promise object in our code. The structure would involve chaining all the code for handling successful outcomes using .then, and only including a single handler for dealing with errors using .catch.

public async initialize(
  @requestParam('site') site: string,
  @request() req: Request,
  @response() res: Response
) {
  const stationName: string = StationService.retailOnlineStationName(site);

  const nameAndPublicId = LocationService.retailOnlineLocationName(site);
  
  // For demonstration purposes, let's assume here
  // that the service returns a Promise of actual value
  // rather than a Promise of Either
  await this.locationService.createLocation(
    site,
    nameAndPublicId,
    nameAndPublicId
  ).then((loc: LocationDTO) => {
    return this.stationService.createStation(site, stationName, loc.id)
  }).then((_: StationDTO) => {
    res.status(201).send()
  }).catch(err => {
    ConfigController.respondWithError(err, res),
  })
}

The functional programming version will maintain the same structure but utilize a different type. We can employ the TaskEither type to simulate a Promise.

public async initialize(
  @requestParam('site') site: string,
  @request() req: Request,
  @response() res: Response
) {
  const stationName: string = StationService.retailOnlineStationName(site);

  const nameAndPublicId = LocationService.retailOnlineLocationName(site);
  
  // Here, the service is expected to return a Promise of Either
  const createLocationTask = () => this.locationService.createLocation(
    site,
    nameAndPublicId,
    nameAndPublicId
  )

  const chainedTask = pipe(
    createLocationTask,
    TE.fold(
      TE.throwError, // pass through error
      (loc: LocationDTO) => async () => stationService.createStation(site, stationName, loc.id),
    ),
    TE.fold(
      // catch error
      (err: ApiError) => async () => ConfigController.respondWithError(err, res),
      (_: StationDTO) => async () => { res.status(201).send() },
    )
  )

  await chainedTask()
}

A TypeScript playground demo with placeholders is linked below.

TS Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Integrating additional JavaScript into an Ionic 2 project

Imagine we have a foo.js file containing a variable, function, and class that are not yet part of the project. Now suppose we want to access these elements in our home.ts method or make them globally available for use within a home.ts method. How can this ...

Utilizing Typescript to implement an interface's properties

After declaring an interface as shown below interface Base { required: string; } I proceeded to implement the interface in a class like this class MyClass implements Base{ method(): void { console.log(this.required); } } However, I ...

NX combined with Nest.js and TypeORM, further enhanced with Webpack and Migrations

Recently, I embarked on a project using NX (Nest.js + Angular) and set up TypeORM for database configuration. While everything runs smoothly in "serve" mode, I found myself struggling with configuring migrations. In a typical Nest.js project, all files in ...

Utilizing TypeScript: Importing a typed module within a d.ts file (from an npm package)

I am currently working on integrating a definition file into an npm package that has dependencies on React. The specific library in question can be found at https://github.com/eiriklv/react-masonry-component. In my TypeScript project, I have successfully ...

Issue with ESLint error in TypeScript PrimeReact async Button click handler

I am currently facing an issue with exporting data from a DataTable in PrimeReact. The onClick function for the Button does not allow async callbacks as flagged by eslint. Can someone guide me on how to properly call this function? const exportCSV = us ...

What is the best way to incorporate additional properties while utilizing useSession in Next.js with TypeScript?

I've encountered an issue while trying to add new properties using useSession() in a TypeScript environment. Although it works on console.log, there is an error related to the type mismatch. The useSession() function typically returns name, email, an ...

The absence of a semicolon following the interface declaration is the issue

I am facing a slight issue with ESLint and Typescript, particularly regarding semicolons after declaring interfaces. Additionally, I utilize VSCode as my editor with automatic formatting upon saving. Below is the configuration in my .eslintrc.json file: ...

The npm start command is unable to recognize the tsx file

I recently attempted to create a React app and incorporate TypeScript into it. However, upon running the app, I noticed that npm start was not recognizing the TypeScript file and failing to automatically generate tsconfig.json. Here is the sequence of ste ...

Issue: (SystemJS) Unable to find solutions for all parameters in $WebSocket: ([object Object], [object Object], ?)

Upon running the code snippet below, an error is thrown: Error: (SystemJS) Can't resolve all parameters for $WebSocket: ([object Object], [object Object], ?). app.component.ts import { Component } from '@angular/core'; import {$WebSocket} ...

Just starting out with React and encountering the error: Invalid element type, a string was expected

I seem to be going in circles with the following issue as I try to load the basics of a React app into the browser. An error message stating 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite c ...

Guide on printing in an Ionic application using print.js without the need to open the printer setup page

Our team is currently working on an Ionic web application that utilizes printer functionality. To enable printing, we have integrated the Print.js npm package. However, when we initiate the print method, a setup page displaying details such as printer na ...

TS: How can we determine the type of the returned object based on the argument property?

Assume we have the following data types type ALL = 'AA' | 'BB' | 'CC'; type AA = { a: number; }; type BB = { b: string; }; type CC = { c: boolean; }; type MyArg = { type: ALL }; I attempted to create a mapping between type n ...

Is there a way to retrieve the number of swipe up interactions from Instagram story insights using the graph API

Is there a way to retrieve the swipe up count displayed in Instagram insights? Since Facebook does not provide this data through their Graph API, how can I access it? I have already tried scraping without success and I am looking for a solution using eith ...

Creating a definition for the use of sweet alerts within a service and incorporating them through

Implementing sweet alert for displaying alert messages in angularJS2/typescript. Due to the repetitive nature of this code in different parts of the application, a service was created. @Injectable() export class AlertMessageService { constructor(pr ...

How can I display JSON values without revealing the parent in Angular 5 and Ionic 3?

I am trying to extract values from JSON without the parent keys. Here is the JSON structure I have: [ { "companies": [{ "id": 1, "name": "Prueba", "company_number": "23423423A", "latitude": 241241.12, "lo ...

What is preventing me from including an additional parameter in a function in TypeScript?

I am currently developing a task management application. I am facing an issue while attempting to incorporate the event and items.id into a button function for actions like delete, edit, or mark as completed. While this functionality works smoothly in pla ...

Utilizing Dual Destructuring for Handling Undefined Main Objects

Before we proceed, I want to clarify that my question is not a duplicate of ES6 double destructure Let's examine the code snippet related to Apollo Client GraphQL: import { gql, useQuery, useMutation } from '@apollo/client'; ... const { loa ...

Utilizing TypeScript Generics for Creating Arrays of Objects with Inherited Type Definitions

I'm exploring the concept of type inheritance for an array of objects, where one object's value types should inherit from another. While I'm unsure if this is achievable, it's definitely worth a try. Currently, I believe my best approac ...

Determine the implicit type of the assigned function, while also constraining the return type to be a subtype of a predefined

When writing multiple functions for server requests, I have encountered a dilemma with TypeScript. Each function must return a type that extends a specific predefined known type, but I also want TypeScript to infer the most accurate return type possible. ...

What is the reason behind the occurrence of `(User & { _id: Schema.Types.ObjectId; }) | null` when calling findById?

Today marks my debut using typescript and mongoose. Here's a glimpse of what I've worked on. Type export interface User extends Document { _id: ObjectId; lastName: string; } Schema const userSchema = new Schema<User>({ lastName: { t ...