javascriptnode.jsnode-fetchinquirerjs

Repetitive prompting with validation in Inquirer.js when using a validation function featuring fetch


In the context of Inquirer.js, one of my question-prompts involves a validation function to check if the user-supplied Github username exists. This validation function uses an fetch function in order to check if the Github username exits.

The problem is that Inquirier repeats the prompt 3 times. Once with a ? prompt, again with a prompt, and a third time with a ? prompt, followed by the custom error message returned which lets the user know that the username did not appear to exist.

Screenshot here

This repetitive occurrence of the prompt is very confusing to the end user, and I want to know how to limit the prompt to displaying once only. I don't really understand this confusing repetitive prompt behavior to begin with.

// Include packages needed for this application

// const fs = require('fs');
import * as fs from "fs";
// const fetch = require('node-fetch');
import fetch from "node-fetch";

// const inquirer = require('inquirer');
import inquirer from "inquirer";



const validateUsername = async function (input) {
  console.log(input);

  const response = await fetch(
    `https://api.github.com/users/${input}`
  );
  // console.log(response);
  const data = await response.json();
  console.log(data.message);

  if (data.message) {
    if (data.message === "Not Found") {
      return "User was not found, please use a valid username.";
    }
  } else {
    return true;
  }
};


inquirer
  .prompt([
    {
      name: "projectName",
      message: "What the project name?",
      type: "input",
    },
    {
      name: "projectDescription",
      message: "What the project description?",
      type: "input",
    },
    {
      type: "input",
      name: "githubUser",
      message:
        "What is your Github username? (This is needed for Shields badges and is used to retrieve code size, repo size, repo file count and licence type)",

      validate: validateUsername,
    },
    {
      name: "githubRepoName",
      message:
        "What is the Github repository name? (This is needed for Shields badges and is used to retrieve code size, repo size, repo file count)",
      type: "input",
    },
    {
      name: "contribution",
      message:
        "Please provide your call to contributions and any relevant information here.",
      type: "input",
    },
    {
      type: "input",
      name: "licence",
      message: "What type of licence applies to your project?",
    },
    {
        type: "input",
        name: "twitter",
        message: "Twitter handle?  Enter without input to skip.",
    },
    {
        type: "input",
        name: "twitter",
        message: "Facebook handle?  Enter without input to skip.",
    },
  ])
  .then((answers) => {
  // do some stuff with answers
  }

Solution

  • I haven't tried this myself, but one idea would be to isolate the question that has async validation, and make you're own validator that calls inquirer, validates and calls again...

    // prompt user for gh name, check and retry if user name is invalid
    async function promptAndValidateGHUser(message) {
      const answer = await inquirer.prompt([{ type: 'input', name: 'ghuser', message }])
    
      const response = await fetch(
        `https://api.github.com/users/${answer.ghuser}`
      );
      // console.log(response);
      const data = await response.json();
      console.log(data.message);
    
      if (data.message) {
        if (data.message === "Not Found") {
          return promptAndValidateGHUser("User was not found, please use a valid username.")
        }
      } else {
        return answer;
      }
    }
    

    The way to call is to build a separate promise each for the questions before the GitHub question, the GitHub question itself, and the question after.

    const listA = [
      {
        name: "projectName",
        message: "What the project name?",
        type: "input",
      },
      // ...
      // up to, but not including the github question
    ]
    
    const ghMessage = "What is your Github username? (This is needed for Shields badges and is used to retrieve code size, repo size, repo file count and licence type)",
    
    const listB = [
      // the rest of the qustions
      {
        name: "contribution",
        message:
          "Please provide your call to contributions and any relevant information here.",
        type: "input",
      },
      // ...
    ]
    

    Then call them in a row, like this:

    async function prompts() {
      const answersA = await inquirer.prompt(listA)
      const gHubAnswer = await promptAndValidateGHUser(ghMessage)
      const answersB = await inquirer.prompt(listB)
    }