I'm working on a project that involves the FCC API, but I'm struggling to grasp the flow of the code

Having some trouble with my code and seeking help to understand what's going wrong. I want to avoid copying and pasting, but rather comprehend my actions. The issue arises when creating a new Url object in the post route. It seems like the function getHighestShort doesn't wait for its promise to resolve, always returning shortMax as 1. If I log certain lines of code, I notice that the if statement inside the getHighestShort function is resolved after saving the Url object, causing it to always have short_url set to 1. Any hints or guidance would be greatly appreciated, as I've tried using await before the new Url creation to ensure it holds up the rest of the code. -42-year-old self-learner- This post might come across as scattered :)

require('dotenv').config();
const express = require('express');
const cors = require('cors');

const bodyParser = require('body-parser')
const mongoose = require('mongoose')
const Url = require('./schemas/urlModel');


mongoose.connect('mongodb://localhost:27017/urlshortener', { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('connection open'))
  .catch((err) => console.log(err))

const app = express();

// Basic Configuration
const port = process.env.PORT || 3000;


app.use(cors());

app.use('/public', express.static(`${process.cwd()}/public`));

app.use(bodyParser.urlencoded({ extended: false }))


app.get('/', function(req, res) {
  res.sendFile(process.cwd() + '/views/index.html');
});

// Your first API endpoint
//find input url in db and return the long version
app.get('/api/shorturl/:input', async (req, res) => {
  try {
    const {input} = req.params
    // const shortUrlNr = parseInt(input)
    console.log(input)
    console.log(Url.findOne( {short_url: `${input}`} ))
    const found = await Url.findOne( {short_url: `${input}`} )
    res.redirect(`https://${found.full_url}`)
  } catch (err) {
      console.log(err)
      console.log(res.status)
  }
})

app.post('/api/shorturl/new', async (req, res, next) => {
  try {
    const { url } = await req.body
    //check for valid url, if not respond with json object as asked in tests.
    if (!validURL(url)) {
      return res.json({error: 'invalid url'})
    } else {
      //find url in db and get json response with the corresponding object. 
      //If it is already in the db, than redirect to this website as asked in tests. 
        const foundUrl = await Url.findOne( {full_url: `${url}`} )
        if (foundUrl !== null) {
          res.redirect(`https://${foundUrl.full_url}`)
        } else {
        //if the url is not there, we''ll create a new entry with that url.
        const newUrl = await new Url( {full_url: `${url}`,  short_url: `${getHighestShort()}`} )
        await newUrl.save()
        res.json(newUrl)
        } 
    }
  } catch(err) {
    console.log('catch error post request')
    return next(err)
  }
})

app.listen(port, function() {
  console.log(`Listening on port ${port}`);
});

function validURL(str) {
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
    '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(str);
}

function getHighestShort() {
  let shortMax = 1
  //make an array from the collection with short values sorted descending
  //and than pick the first one which should be the highest value.
  //if the array is empty just return 1 as it is the first one
  Url.find({}).sort({short_url: 1}).exec()
    .then(res => {
      if (res.length !== 0) {
        shortMax = res[0].short_url + 1
      } 
    })
    .catch(err => {
      console.log('catch err server.js function getHighestShort')
      console.log(err)
    })
    return shortMax
}

model

const mongoose = require('mongoose')
const Schema = mongoose.Schema

const urlSchema = new Schema( 
    {
        full_url: {
            type: String,
            required: false
        },
        short_url: Number
    }
)

const Url = mongoose.model('Url', urlSchema)

module.exports = Url

Answer №1

When using the await operator, it waits for a promise to be resolved in order to function as expected.

For instance, in the line:

const { url } = await req.body

The use of await is unnecessary in this context since req.body is not a promise but an object. Therefore, it is equivalent to:

const { url } = req.body

If your code isn't working, let's first examine the getHighestShort function. Does it return a promise? (Spoiler alert: it does not).

To address this, we must modify the function so that it returns a promise (and subsequently wait for that promise to resolve).

Firstly, alter getHighestShort to return a promise:

function getHighestShort() {
    let shortMax = 1
    // Create an array from the collection with short values sorted in descending order
    // Then select the first value which should be the highest
    // If the array is empty, return 1 as the initial value
    return new Promise((resolve, reject) => {
        Url.find({}).sort({short_url: 1}).exec()
            .then(res => {
                if (res.length !== 0) {
                    shortMax = res[0].short_url + 1;
                }
                resolve(shortMax)
            })
            .catch(err => {
                console.log('Error caught in server.js function getHighestShort')
                console.log(err)
                // You can decide on error handling logic here
                resolve(shortMax)
            })
    })
}

I suggest refining this code for better readability, like so:

async function getHighestShort() {
    let shortMax = 1
    try {
        const results = await Url.find({}).sort({short_url: 1}).limit(1).exec()
        if (results.length !== 0) {
            shortMax = results[0].short_url + 1
        }
        return shortMax
    } catch (e) {
        console.log('Error caught in server.js function getHighestShort')
        console.log(e)
        return shortMax
    }
}

Note the addition of limit(1); there is no need to fetch the entire collection each time.

It's important to note that there are incorrect assumptions about the atomicity of operations. What if two requests are processed simultaneously? This could result in two URLs having the same short_url.

I'll leave this issue for you to handle independently.

Secondly, now that getHighestShort returns a promise, we must await its completion:

const short_url = await getHighestShort();
const newUrl = await new Url( {full_url: `${url}`,  short_url: short_url} )

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 are some strategies for efficiently displaying a large amount of data on a screen using Javascript and HTML without sacrificing performance?

Imagine having a hefty 520 page book with over 6,200 lines of text ranging from 5 to 300 characters each. The challenge lies in efficiently displaying this content on the screen for users to read without causing lag or performance issues. Attempting to re ...

Is JQuery the ultimate solution for creating a dynamic multi-language website?

Embarking on a new project that requires support for multiple languages. My plan is to create a jQuery/AJAX based application with all the code in jQuery, simply calling JSONs for data. What would be the most effective approach for implementing multi-lan ...

Encountering a bindings issue when trying to utilize libxml-xsd within an electron application

Seeking guidance on validating an XML file against its schema within an electron application. Prior to adding the 'libxml-xsd' require statement to my angular2 service, it functioned properly. However, upon inclusion of this statement: const xs ...

Create distinct 4-digit codes using a random and innovative method, without resorting to brute force techniques

I am working on developing an application that requires generating random and unique 4-digit codes. The range of possible codes is from 0000 to 9999, but each day the list is cleared, and I only need a few hundred new codes per day. This means it's fe ...

Guide on how to store a CSS image in a server using PHP

Looking to create a button generator using JavaScript on my website, similar to the one found here http://css-tricks.com/examples/ButtonMaker/. In addition to generating buttons, I also want to include a save button so that users can save the button image ...

Is it possible to load a JS file without using the require function?

Is there a method to load a JavaScript file without using require, but with fs for instance? I am aware that for JSON files I can utilize: const jsonFile = JSON.parse(fs.readFileSync("/jsonfile.json")) Can the same be done for a JavaScript file? I am inq ...

Tips for eliminating empty trailing values and Carriage Returns from a JavaScript array

I needed a way to eliminate empty elements and Carriage Returns from the end of an array. Here's an example of what my array looks like: Input arr: ['', 'Apple', '', 'Banana', '', 'Guava', & ...

Unable to utilize the resolved value received from a promise and returned from it

Within the code snippet below, I am retrieving a Table object from mysql/xdevapi. The getSchema() and getTable() methods return objects instead of promises. The purpose of this function is to return a fulfilled Table object that can be used synchronously i ...

Error: Unable to access the `insertUsername` property as it is not defined

When I attempt to submit the login form created by the new.ejs file, instead of being redirected to the expected page, I am encountering an error message that reads: Cannot read property 'insertUsername' of undefined This same error message is ...

Modifying the page header content using JavaScript

There's this snippet of code that alters the image on another page: <div class="imgbx"> <button onclick="window.location.href='index.html?image=images/xr-black.jpg&tit=XR-black'" >Invisible ...

The face textures are not being applied correctly to the model imported in THREE.js

I'm having trouble importing a model exported from blender using the THREEJS exporter. The model loads correctly in my scene with the materials applied, such as the car appearing yellow and the glass being transparent as intended. However, I am facin ...

Connecting HTML POST to MongoDB using NodeJS

I am in the process of developing a blog application using Node, Express, and MongoDB. My goal is to create an HTML form that will allow users to submit new blog posts directly to the database. While I have successfully implemented GET and POST requests us ...

What is the reason behind Chrome automatically refreshing the URL when no response is received for a certain amount of time

Within my node application, I have an anchor tag that, upon being clicked, triggers an Express GET route. This route then makes various API calls and displays the response within an EJS template. In cases where the API response from the Express route take ...

Updating the parent's reference from a child component in Vue 3

In one of my child components, I have a component named Navbar that includes an option for logging out. <a @click="(event) => {event.preventDefault();}"> Logout </a> This Navbar component has been imported into my parent compon ...

The functionality of PHP in relation to the jQuery post feature seems to be malfunction

After developing the JavaScript functionality for this contact form, below is the HTML structure without any CSS styling included: <!-- Contact Form --> <div class="cws-widget"> <div class="widget-title" style="color:#fec20b;">S ...

Trouble with CSS transitions not functioning while altering React state

Each time a user clicks on an accordion, the content should smoothly show or hide with a transition effect. However, the transition only seems to work when opening a closed accordion, not when closing an already open one! To get a clearer idea of this iss ...

What is the process for using Discriminators, the Mongo DB API, and Mongoose to insert data into Cosmos DB?

Issue Overview I am currently facing a challenge in writing documents to my Cosmos DB using the Mongo DB API and Mongoose for Object Modeling. To optimize costs, I aim to store all documents in a single collection by utilizing Discriminators. The project ...

ERROR UnhandledTypeError: Unable to access attributes of null (attempting to retrieve 'pipe')

When I include "{ observe: 'response' }" in my request, why do I encounter an error (ERROR TypeError: Cannot read properties of undefined (reading 'pipe'))? This is to retrieve all headers. let answer = this.http.post<ResponseLog ...

Storing JSON objects directly into MongoDB can be easily achieved with Meteor

Currently exploring Meteor and so far, I am enjoying it :-) However, I am facing challenges while attempting to directly store a JSON object into miniMongo - even though I believed that was the intended purpose :-) testVar = {"test":"this is from the ob ...

Are there options available in nightwatchjs for making intricate decisions with selectors?

When using the NightWatch JavaScript Selenium tool, it is important to establish good practices for identifying different parts of the GUI before running tests. For example, distinguishing between options A and B and creating separate tests accordingly. An ...