Search code examples
javascriptmodel-view-controllermongoosekoa

Koa/Node.js - Use multiple models within one controller function


I am attempting to create a new 'Vault' from a Post Request from my frontend (built in React) using Mongoose Schema.

When I hit the create button in my app, the Post Request initiates, but returns:

POST http://localhost:3000/vaults/create 500 (Internal Server Error)

When my controller function createVault() is initiated, it will successfully create a 'Vault' from the model (see below):

//Create a vault
module.exports.createVault = async (ctx, next) => {
  if ('POST' != ctx.method) return await next();
    try {
      if (!ctx.request.body.name) {
      ctx.status = 404
  }
  //Create new vault
  const vault = await Vault.create({
    name: ctx.request.body.name,
    url: ctx.request.body.url,
    description: ctx.request.body.description
  });

   await vault.save();

   //Return vault
   ctx.body = vault;
   ctx.status = 201;
 }
 catch (error) {
   if (error) {
     console.log('Error in the createVault controller:', error);
     ctx.status = error.response.status;
     ctx.body = error.response.data;
   }
 }
}

However, my problem occurs when I try to add a second Schema model; I am attempting to create a 'Crypt' from each of the items in the ctx.request.body.crypts array (see below):

//Create a vault
module.exports.createVault = async (ctx, next) => {
  if ('POST' != ctx.method) return await next();
  try {
    if (!ctx.request.body.name) {
      ctx.status = 404
    }
    //Create new vault
    const vault = await Vault.create({
      name: ctx.request.body.name,
      url: ctx.request.body.url,
      description: ctx.request.body.description
    });
    //Create new crypts
    const promises = await ctx.request.body.crypts.map(crypt => Crypt.create({
      name: crypt
    }));
    //Add reference to vault
    const crypts = await Promise.all(promises);
    vault.crypts = crypts.map(crypt => crypt._id);
    await vault.save();

    //Return vault and crypts
    ctx.body = [vault, crypts];
    ctx.status = 201;
  }
  catch (error) {
    if (error) {
      console.log('Error in the createVault controller:', error);
      ctx.status = error.response.status;
      ctx.body = error.response.data;
    }
  }
};

The error I receive say I cannot map over an undefined object, although I am using const crypts = await Promise.all(promises);

Can anyone suggest a correct way around this problem? Many thanks.


Solution

  • I managed to fix my problem by creating a function called cleanBody(body) which manually parsed the data for me to use.

    I logged typeof ctx.request.body which returned a string and revealed my problem. The cleanBody(body) function just check if the body was an object, then used JSON.parse() if it was a string (see below):

    const cleanBody = body => {
      return typeof body !== 'object' ? JSON.parse(body) : body;
    };
    

    My mistake was assuming APIs from Postman and APIs called in the app would pass the same data, even when everything looks the same