Using data retrieved from a JSON response to make HTTP requests in a recursive manner

I am in the process of extracting all names from an API that provides a JSON response structured like this:

{
    "data" : [
        {
            "attributes" : {
                "name" : "Prog1"
                ...
            },
            "id" : "12345",
        },
        {
            "attributes" : {
                "name" : "Prog2"
                ...
            },
            "id" : "67890",
        },
        ...
    ],
    "links" : {
        "next" : "https://api.company.com/v1/programs?page%5Bnumber%5D=3",
        "self" : "https://api.company.com/v1/programs?page%5Bnumber%5D=2",
        "prev" : "https://api.company.com/v1/programs?page%5Bnumber%5D=1",
    }
}

The goal is to continue making requests and recording the names until no more "next" link is available, at which point the process should stop.

Presented below is the current function:

async function getPrograms(url: string, user: string, pass: string) {
    try {
        const response = await fetch(url, { 
            method: 'GET',
            headers: {Authorization: 'Basic ' + Buffer.from(`${user}:${pass}`).toString('base64')},
        });

        if (!response.ok) {
            throw new Error(`Error! status: ${response.status}`);
        }

        console.log(response);

        const result = (await response.json()) as GetProgramsResponse;

        result.data.forEach((program) => {
            // More functionality will be added here
            console.log(program.attributes.name);
        })

        console.log(result.links.next);

        if (result.links.next) {
            getPrograms(result.links.next, user, pass)
        }

    } catch (error) {
        if (error instanceof Error) {
            console.log('error message: ', error.message);
            return error.message;
        } else {
            console.log('unexpected error: ', error);
            return 'An unexpected error occured';
        }
    }
};

getPrograms(url, u, p);

It seems like the program stops progressing after about 6 iterations without completing the task. It's likely due to the asynchronous nature of the requests, but I'm unsure how to handle waiting for all iterations to finish since I'm still learning TypeScript.

I welcome guidance on resolving this issue and any feedback on the overall proposed solution.

Thank you!

Answer №1

Summary: A crucial await statement is missing (floating promise) in this section of the code:

if (result.links.next) {
    await getPrograms(result.links.next, user, pass) // I've correctly awaited this call.
}

To understand how async/await works in TypeScript/JavaScript, check out this helpful article.

Furthermore, there are a few other issues to address in this snippet.

  1. Utilizing recursion for calling multiple endpoints may not be the most efficient approach. It can lead to increased call stack size and code complexity. In extreme cases, it could cause the program to crash due to a full call stack. Consider using a while loop instead (iterate while link.next exists).

  2. Rather than using someArray.forEach, consider utilizing the classic for const of loop. While it doesn't offer significant performance benefits, it enhances readability. Additionally, you can invoke an async function within this loop.

for (const program of result.data) {
  await saveToDb(program)
}
  1. Attempting to streamline the try/catch scope. Having extensive chunks of code within a single try/catch block is considered poor practice.

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

What is the best way to obtain the value of a nested formBuilder group?

Currently, my nested form is structured like this: this.form = this.formBuilder.group({ user: this.formBuilder.group({ id: ['', Validators.required], name: ['', Validators.required], phone: ['' ...

Error occurs when trying to merge and export namespaces in TypeScript

I'm currently developing an app that utilizes TS and OpenLayers. In order to create a dependency tree, I have included import and export statements in my *.ts files. One of the files is 'openlayers.ts', which extends the base OpenLayers lib ...

How can NestJS customize its built-in services such as ConfigService using a TypeScript interface?

The configuration documentation for NestJS provides an example of achieving type safety with the ConfigService by using an interface named EnvironmentVariables. This interface is then annotated during injection in the constructor like this: constructor(pri ...

What are the best methods for exchanging code among different TypeScript projects?

Imagine having two separate projects, each with its own unique file structure: /my-projects/ /project-a/ lib.ts app.ts tsconfig.json /project-b/ app.ts // import '../project-a/lib.ts' tsconfig.json How can I ac ...

Modifying a group of Components in Angular using a Service

Managing a set of Components involves changing their properties using a Service. The Components have a minimal model and are meant to remain compact. They are being rendered with *ngFor. The Service possesses a large Object and should possess the abilit ...

Proven method for transforming an Object containing a Map into a JSON Object

Is there a way to convert an object containing a map to JSON without losing the map data? When I use JSON.stringify(), the map data gets cleared. How can I solve this issue? frieghtChargesJSon:string; //declared variable frieghtChargesJSon=JSON.stringify ...

Angular - Implementing *ngIf based on URL parameters

Is it possible to display an element based on specific queryParams included in the URL? For example: ngOnInit() { this.route.queryParams.subscribe(params => { console.log(params); }); } If I want to achieve something similar to this: ...

What is the best approach to testing the React Hook "useEffect" that is used to make an API call with Typescript?

Currently, I am working on writing Jest-enzyme tests for a basic React application using Typescript along with the new React hooks. The main issue I am facing is with properly simulating the api call made within the useEffect hook. Within the useEffect, ...

Navigating "this" in Angular and jQuery

When working in my angular-7 class and using jQuery, I encountered a problem where I needed to store all text from h4 tags in an array. However, the issue was that when using this, it would only refer to Angular's version of this rather than jQuery&ap ...

Issue with importing and exporting external types causing failures in Jest unit tests for Vue 2

I am in the process of creating a package that will contain various types, enums, consts, and interfaces that I frequently use across different projects. To achieve this, I have set up a main.ts file where I have consolidated all the exports and specified ...

In an Electron-React-Typescript-Webpack application, it is important to note that the target is not a DOM

Rendering seems to be working fine for the mainWindow. webpack.config.js : var renderer_config = { mode: isEnvProduction ? 'production' : 'development', entry: { app: './src/app/index.tsx', //app_A: './src/a ...

Unlocking the Power of RxJS with Observable Sharing

Let's take a look at a function that contains the code below: private foo() { let obs: Observable<any> = this._http.get<number>('/foo') obs.subscribe(x=> { console.log("foo : " + x) }); this.blah(ob ...

What is the secret to planning an event that spans a significant period of time?

Let's say there's this div in the code: <div [hidden]="!isNotificationDisplayed" class="notification"> <h2 align="center" class="set-margin" #notification>Node was...!</h2> <h4 alig ...

Creating a promise instance with the axios create method

As someone who is new to TypeScript, I am learning as I go along. One thing I want to do is create an axios instance that can be reused in my code by passing props only where needed. The framework I'm using is React. // Located in a utils folder // a ...

Experimenting with async generator using Jest

It has become clear that I am struggling with the functionality of this code, especially when it comes to testing with Jest. Despite my efforts to use an await...of loop, I am not getting any output. The file path provided to the generator is correct and I ...

Typescript error: The 'prev' argument does not match the parameter type

Upon implementing this code snippet export const resetErrors = (setErrors: (errors: Array<ErrorInterface>) => void, field: string): void => setErrors((prev: Array<ErrorInterface>): void => prev.filter((el: ErrorInterface) => el.fiel ...

What is the relationship between an odd number and the value 1 in JavaScript that results in a 'true' outcome?

I encountered a straightforward problem, but the solution has left me perplexed. function transformArray(numbers) { // If 'i' is an odd number, i & 1 will evaluate to 1 or true return numbers.map(i => (i & 1) ? i * 3 : i * 2); } co ...

Tips for targeting a specific element with providers in Ionic

By using the specified pattern, I am aiming to achieve a unique toolbar or header for only certain pages. Is there a way to accomplish this without injecting the provider and keeping the page as a standalone? My understanding of Ionic is still developing, ...

The withLatestFrom operator will not trigger with a null value

Working on an Angular project that utilizes @ngrx/effect, we are incorporating an observable stream with the withLatestFrom rxjs operator. Here is a glimpse of our observable effect stream: @Effect() handleUsersData$ = this.actions$ .pipe( ofType(HAND ...

Can the getState() method be utilized within a reducer function?

I have encountered an issue with my reducers. The login reducer is functioning properly, but when I added a logout reducer, it stopped working. export const rootReducer = combineReducers({ login: loginReducer, logout: logoutReducer }); export c ...