Getting Errors When Retrieving Data with Apostrophe Symbol ' in Node.js

I am currently developing a Next.js API page to extract data from a series of URLs. Interestingly, the URLs containing an apostrophe character ' fail to return any data, while those without it work perfectly fine. Surprisingly, when I execute the same code in Chrome's dev console or a RESTful Client, the URLs with the apostrophe do return data successfully. Both situations establish a successful connection (HTTP 200), but the discrepancy in outcomes occurs exclusively within the Next.js project. Any insights on what could be causing this behavior difference?

UPDATES:

  1. I attempted using both encodeURI and encodeURIComponent, but unfortunately, they did not resolve the issue.
  2. Even the substitution of AYR%27KA instead of AYR'KA did not rectify the problem.
  3. Upon comparing the URL passed to the fetch function with the value retrieved from response.url, I noticed that in the browser, everything remains consistent. However, in Next.js, the AYR'KA transforms into AYR%27KA.
  4. Collaborating with @Phil and thorough investigation highlighted Node.js's URL default component as the root cause. Here is the NPM link for reference and here is their GitHub repository link. It was discovered that when node-fetch constructs a Request object, they invoke URL.parse on the string provided. Exploring the file "url.js" on their GitHub, particularly lines 353-360 showcased how specific characters, including the apostrophe/tick mark ', are forcibly escaped despite being non-restrictive characters. (NOTE: This update reflects changes made on July 24th, 2021, and refers to the latest version of the code at the time.)

*NOTE: To avoid CORS issues, testing can be conducted via Browser Dev Tools directly from the website here

const worksEverywhereURL = "https://robertsspaceindustries.com/api/starmap/star-systems/STANTON",
    brokenInNodeURL = "https://robertsspaceindustries.com/api/starmap/star-systems/AYR'KA";

async function getJSONFrom(url){
    const options = {method: "POST"};
    let response, json;
    
    console.log("url: ",url)
    response = await fetch( url, {method:"POST"} );
    console.log("response url: ",response.url)
    json = response.json();

    return json;
}

function checkResponse(json) {
    try {
        return json.data.resultset.length > 0;
    } catch(err) {
        return false;
    }
}

const example1 = await getJSONFrom( worksEverywhereURL );
const example2 = await getJSONFrom( brokenInNodeURL );

console.log( checkResponse(example1) ); //"true" in chrome and "true" in next.js api page
console.log( checkResponse(example2) ); //"true" in chrome and "false" in next.js api page
//Ongoing output comparison in browser vs. command prompt detailed in original text.

Displaying below, the API page code snippet indicating an empty resultset being returned:

export default async function handler(req, res) {
    const worksEverywhereURL = "https://robertsspaceindustries.com/api/starmap/star-systems/STANTON",
        brokenInNodeURL = "https://robertsspaceindustries.com/api/starmap/star-systems/AYR'KA";

    async function getJSONFrom(url){
        const options = {method: "POST"};
        let response, json;

        response = await fetch( url, {method:"POST"} );
        json = response.json();

        return json;
    }

    function checkResponse(json) {
        try {
            return json.data.resultset.length > 0;
        } catch(err) {
            return false;
        }
    }

    const example1 = await getJSONFrom( worksEverywhereURL );
    const example2 = await getJSONFrom( brokenInNodeURL );

    console.log( checkResponse(example1) ); //"true" in chrome and "true" in next.js api page
    console.log( checkResponse(example2) ); //"true" in chrome and "false" in next.js api page
}

For cases where the response returns incorrectly, the structure resembles:

{
  "success": 1,
  "code": "OK",
  "msg": "OK",
  "data": {
    "rowcount": 0,
    "totalrows": 0,
    "estimatedrows": false,
    "pagesize": 0,
    "pagecount": null,
    "page": 1,
    "offset": 0,
    "startrow": 0,
    "resultset": []
  }
}

Whereas a correct response contains:

{
  "success": 1,
  "code": "OK",
  "msg": "OK",
  "data": {
    "rowcount": 1,
    "totalrows": 1,
    "estimatedrows": false,
    "pagesize": 0,
    "pagecount": null,
    "page": 1,
    "offset": 0,
    "startrow": 0,
    "resultset": [
      {
        "id": "398",
        "code": "AYR'KA",
        "description": "Primarily a military system focused on supporting, housing, and training Xi’an ground troops. Its close proximity to the Perry Line made it strategically important during the Human / Xi’an cold war. Once relations normalized, Xi’an forces from the Perry Line systems withdrew to here.  ",
        "frost_line": "179.10000000"
        ...
      }
    ]
  }
}

In Relation to Update 4: Provided are snippets extracted from url.js on GitHub signifying the intricate dynamics behind the unreachable target URL (disconnected portions originating from a dependency's dependency).

-Referencing Lines 56-70 alongside their explanatory comments:

/*
 * RFC 2396: characters reserved for delimiting URLs.
 * We actually just auto-escape these.
 */
delimiters = [
  '<', '>', '"', '`', ' ', '\r', '\n', '\t'
],

// RFC 2396: characters not allowed for various reasons.
unsafe = [
  '{', '}', '|', '\\', '^', '`'
].concat(delimiters),

// Compliant with RFCs yet susceptible to XSS attacks. Always warrant escaping.
autoEscapeChars = ['\''].concat(unsafe),

-Further exploration into lines 348-361 inclusive of insightful observations where the previously defined characters come into play:

/*
 * First and foremost, ensure all "autoEscape" characters undergo
 * proper escaping, regardless of whether encodeURIComponent detects
 * the necessity
 */
for (var index = 0, size = autoEscapeChars.length; index < size; index++) {
  var aechar = autoEscapeChars[index];
  if (remaining.indexOf(aechar) === -1) { continue; }
  var escChar = encodeURIComponent(aechar);
  if (escChar === aechar) {
    escChar = escape(aechar);
  }
  remaining = remaining.split(aechar).join(escChar);
}

Answer №1

It appears that the issue stems from the version of node-fetch being used in Next.js.

The newer v3.x version (which is still in beta) utilizes new URL() instead of Node's url.parse(), causing the problem at hand.

To potentially resolve this, consider using new URL() directly to avoid any parsing issues.

One approach could be:

async function getJSONFrom(url) {
  const response = await fetch(new URL(url), { method: "POST" })
  if (!response.ok) {
    throw new Error(`${response.status}: ${await response.text()}`)
  }

  return response.json()
}

If the above solution does not work (as indicated by a review of the source code), you can try specifying the desired version in the package.json file:

"resolutions": {
  "next/node-fetch": "3.0.0-beta.10"
}

For more information on selective version resolutions, refer to

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

Toggle the visibility of an element using a checkbox in JavaScript

In my scenario, there are 8 checkboxes where only one is checked by default. If you click on the unchecked checkboxes twice, the table linked to them will hide. Ideally, I want the unchecked checkboxes to be hidden by default and the checked one to be sh ...

Stop all file uploads using jQuery

I have integrated the jQuery File Upload plugin () into my website for image uploads. Here is my code snippet: $('#fileupload').fileupload({ url: 'server/index.php', dataType: 'json', dropZone: $('#dropzone&a ...

Increase the jQuery Array

After successfully initializing an Array, how can I add items to it? Is it through the push() method that I've heard about? I'm having trouble finding it... ...

What is the best method for displaying an HTML string within an HTML file in Angular 5?

I have declared an array in my TypeScript file like this: import {Component, OnInit} from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'app-foo', template: ...

Implementing dynamic data binding in JavaScript templates

I've been experimenting with jQuery and templates, and I managed to create a basic template binding system: <script type="text/template" id="Template"> <div>{0}</div> </script> Furthermore... var buffer = ''; v ...

Mongo DB - querying elements in nested arrays

I have a unique data structure that looks like this { "level1": [ { "id": 1, "level2": [ [ { "id": 1 ...

Issue with specific MongoDB pipeline causing change stream not to trigger

I'm currently tackling a project that requires monitoring MongoDB documents for groups managed by a user. To achieve this, I have implemented change streams in MongoDB. However, I've encountered an issue where the change stream fails to trigger w ...

Loading JavaScript in the background using Java and HtmlUnit

I am currently facing a challenge while navigating a website using HtmlUnit. This particular website adjusts certain buttons and displays or hides certain elements based on JavaScript events. For instance, there is a text input box along with a button th ...

Mongoose aggregate not returning any results

I'm currently working on using Mongoose aggregate to group businesses. I have a MongoDB script that is functioning properly. Below, you'll find the NodeJS code with Mongoose as well as the MongoDB code: History.aggregate() .cursor({ bat ...

How can JavaScript determine if this is a legitimate JS class?

I'm currently in the process of converting a React.js project to a next.js project. In my project, there's a file named udf-compatible-datafeed.js. import * as tslib_1 from "tslib"; import { UDFCompatibleDatafeedBase } from "./udf-compatibl ...

"Repeating SignalR Messages: Issue of Duplication when Stopping and Restarting

Whenever I stop and start the connection, messages sent to the client by the hub are duplicated. If I follow this sequence: $.connection.hub.stop() $.connection.hub.start() {...} and a message is sent from the server hub to the client, it is initially re ...

Tips for showcasing checkbox options on an HTML page (utilizing ejs) following the retrieval of information from MongoDB and making it editable for the User

Currently, I store data in mongo for a multiple-choice question and indicate the correct answers. In mongo, I save them like this: question.options.option1.check = "on" for the correct answers. When I navigate to the "edit" page, I retrieve the specific ...

I'm having trouble with my script only fetching the first row of my PHP table. Can someone please take a look at my code

Below is my JavaScript snippet: $('input#name-submit').on('click', function() { var name = $('input#name-submit').val(); if($.trim(name) != ''){ $.post('getmodalreasonUT.php', {name: name}, ...

What is the method for configuring environment variables in the Lumber framework?

Installing Lumber CLI npm install -g lumber-cli -s Next, lumber generate "adminpanel_test" --connection-url "mysql://root@localhost:3306/admin-dev" --ssl "false" --application-host "localhost" --application-port "3310" Error: lumber is not recognized a ...

"Learn the art of refreshing data in AngularJS following the use of $emit event handling

I am in need of assistance with AngularJS. How can I re-initialize a variable in scope after using emit? Here is an example code snippet: $scope.uiConfig = {title: "example"}; $scope.$emit('myCustomCalendar', 'Data to send'); $scop ...

Angular 4 with Universal: Implementing 404 Status Code in Header for /404 Page Component

After researching and reviewing numerous StackOverflow inquiries, I have come to the conclusion that headers are derived from responses served by servers, making it a non-issue. I attempted to rectify the situation from my server.ts file but unfortunately ...

In Chrome, it seems like every alternate ajax request is dragging on for ten times longer than usual

I've been running into an issue with sending multiple http requests using JavaScript. In Chrome, the first request consistently takes around 30ms while the second request jumps up to 300ms. From then on, subsequent requests alternate between these two ...

PHP encountering a bad escaped character while parsing JSON using JSON.parse

I'm encountering an issue with JSON parsing. In my PHP code, I have the following: json_encode(getTeams(),JSON_HEX_APOS); This returns a large amount of data. Sample data: To provide more clarity, let's assume I have this: my_encoded_data ...

When modifying v-model directly in a Vue instance, the Vuetify component on the screen may not update its displayed value

Within my Vue component example (utilizing Vue 2 and Vuetify 1), I have implemented an input field for user data entry. However, I am looking to programmatically alter the input field's data to a specific value. For instance, in this scenario, I aim ...

Issue with data synchronization between form field and database using Angular with MongoDB and Node.js

I am currently working on developing a contact application that allows users to save contact information directly into the database. For some reason, the form data is not updating in the database when I try to add new contacts. Interestingly, if I manually ...