0

I want to:

  1. Insert into database, get an id from that, then
  2. insert another set of data and get an id from that, and finally
  3. insert both id's into a mapping table.

Within one transaction with SQLite it worked, but using Turso edge database the async function returns the id's from the second insert, but won't console.log() those values within the async function. However, I'm able to console.log() the value of the first insert:

function buildRouter(env: Env): RouterType {
    const router = Router();

    router.post("/api/main-form", async (request) => {
        const db = buildDB(env)
        const formData = await request.json();

        const loadedFormInfo: any = await db.transaction(async (tx) => {
            // Insert data into formDataTable and retrieve auto-incremented ID
            const insertedFormId: any = tx.insert(formDataTable)
                .values({
                    dateOption: formData.dateOption,
                    timeZone: formData.timeZone,
                    utcTime: new Date(formData.utcTime),
                    localTime: formData.localTime,
                    title: formData.title,
                    tag: formData.tag,
                    latitude: formData.latitude,
                    longitude: formData.longitude,
                    createdAt: new Date()
                })
                .returning({formId: formDataTable.id});

            let formId = await insertedFormId.values();
            formId = formId[0].id

            // return formId

            // Insert data into proponentsTable using the retrieved formId
            const proponentsData: Proponent[] = formData.proponents;
            let insertedProponentIds: Number[] = [];
            proponentsData.forEach(async proponent => {
                let insertedProponentId = tx.insert(proponentsTable)
                    .values({
                        formId: formId,
                        team: proponent.team,
                        name: proponent.name,
                        email: proponent.email,
                        createdAt: new Date()
                    }).returning({proponentId: proponentsTable.id});

                let proponentId = await insertedProponentId.values();
                proponentId = proponentId[0].id;
                insertedProponentIds.push(proponentId);
            });

            console.log(insertedProponentIds)  // <--- returns an empty array
            return insertedProponentIds // <--- responds with actual array

// third insert would go here //

        });

        console.log('outside: ', loadedFormInfo)
        const responseBody = JSON.stringify( {'return_val': loadedFormInfo} );
        const response = new Response(responseBody, { status: 200, headers: { 'Content-Type': 'application/json' } });

        return response;
    });

    router.all("*", () => new Response("Not Found.", { status: 404 }));

    return router;
};

The final insert is not getting the value of the second insert so is not able to map. I tried:

// Insert data into proponentsTable using the retrieved formId
const proponentsData: Proponent[] = formData.proponents;
let insertedProponentIds: Number[] = [];
for (const proponent of proponentsData) {
    try {
        const insertedProponentId = await tx.insert(proponentsTable)
            .values({
                formId: formId,
                team: proponent.team,
                name: proponent.name,
                email: proponent.email,
                createdAt: new Date()
            })
            .returning({ proponentId: proponentsTable.id });

        if (insertedProponentId && insertedProponentId[0] && insertedProponentId[0].id) {
            const proponentId = insertedProponentId[0].id;
            insertedProponentIds.push(proponentId);
        } else {
            console.error("Invalid insertedProponentId data:", insertedProponentId);
        }
    } catch (error) {
        console.error("Error inserting proponent:", error);
    }
}

console.log(insertedProponentIds)
return insertedProponentIds

Got this:

[mf:inf] POST /api/main-form 500 Internal Server Error (757ms)
⎔ Reloading local server...
[mf:inf] Updated and ready on http://127.0.0.1:8787/
Invalid insertedProponentId data: SQLiteInsert {
  session: _LibSQLSession,
  dialect: SQLiteAsyncDialect,
  config: Object,
  run: ,
  all:
  ...
}
Invalid insertedProponentId data: SQLiteInsert {
  session: _LibSQLSession,
  dialect: SQLiteAsyncDialect,
  config: Object,
  run: ,
  all:
  ...
}
Array(0) [  ]
outside:  Array(0) [  ]
[mf:inf] POST /api/main-form 200 OK (49ms)

user4157124
  • 2,809
  • 13
  • 27
  • 42
avnav99
  • 532
  • 5
  • 16
  • 1
    It looks like you're experiencing an issue with asynchronous behavior and proper handling of promises in your code. The primary problem you're facing is related to how you're using forEach with asynchronous operations inside your transaction. The forEach loop is not waiting for the asynchronous operations to complete before moving on to the next iteration, which is likely causing you to get an empty array when you try to log insertedProponentIds. – Golam Moula Aug 06 '23 at 15:29
  • Side question: why even bother with adding a handful of meaningless `: any` type hints if you're not going to bother actually writing typescript? It doesn't help you, it doesn't help keep your code correct (the main reason TS even exists), and it doesn't help people reading your code if they need to review it. Just stick to JS if all you're going to do is add `:any` so it doesn't need a TS compile pass. – Mike 'Pomax' Kamermans Aug 06 '23 at 15:30
  • @Mike'Pomax'Kamermans im just learning TS, and i first want to make sure the logic even works before dealing with the countless errors i get from TS...if you have any advice on how to find out what the exact types are that would be awesome. for example `SQLiteInsert` i tried with `import type { SQLiteInsert } from "@libsql/client/web";` but it doesn't work – avnav99 Aug 06 '23 at 15:35
  • @GolamMoula ahh ok so i guess i need a for loop then – avnav99 Aug 06 '23 at 15:36
  • 1
    @avnav99 you either need a `for` loop, or you can collect an array of promises from your input array using `.map` instead of `.forEach` and then do `await Promise.all(promiseArray)`. See https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop – Alnitak Aug 06 '23 at 16:40
  • but note that the `.map` version is preferred if you want the individual async tasks to start in parallel. The `for .. of` version will force the tasks to be run in series. – Alnitak Aug 06 '23 at 16:47
  • 1
    @avnav99 I can recommend not doing things that way. Start typing the moment you write code, don't try to add it in later: adding it in as you _write_ code means your code editor will immediately be able to help you figure out problems before you ever even _run_ anything. But the one thing you _don't_ add is `: any`: that type hint literally means "I have no idea what this is, I'm just adding this so `tsc` stops complaining" and is almost always a sign that you're doing typing wrong (there are _very_ few cases in which `any` in code _you_ wrote is good type hinting) – Mike 'Pomax' Kamermans Aug 06 '23 at 16:58
  • @Mike'Pomax'Kamermans hey might i agree with you of course, just very difficult to find what types and where to import them from. if i knew how to remove typescript from my project and have everything js i'd do that at this point – avnav99 Aug 09 '23 at 15:54
  • 1
    That's easy enough: just use the `.js` file extension, and then update your package.json to not run the `tsc` compiler step (but it might be even easier to just take ownership of your project in that case and set up a fresh one since you'll basically just need react and esbuild without any of the create-react-app additionals) – Mike 'Pomax' Kamermans Aug 09 '23 at 16:08

1 Answers1

0

Here's what I did:

// Insert data into proponentsTable using the retrieved formId
const proponentsData: Proponent[] = formData.proponents;
let insertedProponentIds: Number[] = [];
for (const proponent of proponentsData) {
    try {
        let insertedProponentId = await tx.insert(proponentsTable)
            .values({
                formId: formId,
                team: proponent.team,
                name: proponent.name,
                email: proponent.email,
                createdAt: new Date()
            }).returning({ proponentId: proponentsTable.id });

        let proponentId = await insertedProponentId.values();
        // proponentId = proponentId.map(proponent => proponent.id);
        proponentId = proponentId[0].id
        insertedProponentIds.push(proponentId);
    } catch (error) {
        console.error("Error inserting proponent:", error);
        throw error;
    }
}

console.log(insertedProponentIds)
return insertedProponentIds
user4157124
  • 2,809
  • 13
  • 27
  • 42
avnav99
  • 532
  • 5
  • 16