Using REST API for a Discord bot

Viewed 289

I am trying to make my own discord bot (in discord.js v13). I found the official discord.js guide on this topic and their example code, and I am trying to use/reuse the code from there to build my own bot.

The idea is to take a user input after the slash command "item" (so /item: firebrand), pass that user input into the URL (which allows you to get the information about that item from the backend/API) and use the response that is received to populate the various fields of an embed, which are sent back to the user.

I am able to pass the user input and add it to the URL, which returns a usable link, but the code breaks before the embed can be created. I put in a few console.log commands into my code to see where things are breaking.

See the location of console.log in the code below.

  • Console log 1: {"items":[{"id":14,"name":"Frost Brand","type":"1-h wpn","constlevel":4,"mainlevel":1,"mpath":"W1","gemcost":"5W","screenshot":"/items/14/screenshot"}],"similarity":-0}
  • Console log 2: https://dom5api.illwiki.com/items?match=fuzzy&name=frostbrand
  • Console log 3: [object Object]
  • Console log 4: [object Object]
  • Console log 5: undefined - TypeError: Cannot read properties of undefined (reading 'length')

My guess is that I am getting the TypeError because { list } is undefined. That is probably because itemSearchResult.body is giving back [object Object] as a response, but I have no clue how to fix that.

const { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageEmbed } = require('discord.js');
const { request } = require('undici');
const { ITEM_URL, BASE_URL } = require('../utils/utils');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('item')
        .setDescription('Replies with information about an item')
        .addStringOption(option => option.setName('item_name').setDescription('Enter the name of the item')),
    async execute(interaction) {
        async function getJSONResponse(body) {
            let fullBody = '';
            for await (const data of body) {
                fullBody += data.toString();
                console.log(`Console log 1: `+fullBody);
            }
            return JSON.parse(fullBody);
        }
        const itemName = interaction.options.getString('item_name');
        
        const itemSearchResult = await request(ITEM_URL + encodeURIComponent(itemName));
        console.log(`Console log 2: `+ITEM_URL + encodeURIComponent(itemName)); 
        console.log(`Console log 3: `+itemSearchResult.body);
        console.log(`Console log 4: `+itemSearchResult.body.toString())
        const { list } = await getJSONResponse(itemSearchResult.body);
        console.log(`Console log 5: `+list)

        if (!list.length) {
            await interaction.reply(`No results found for **${itemName}**.`);
        }

        const [answer] = list;
        
        const itemEmbed = new MessageEmbed()
            .setColor('#000000')
            .setTitle(answer.name)
            .setURL('X')
            .setAuthor({ name: 'Author' })
            .setDescription('Lot of hassle, but hey, it was working!')
            .setImage(BASE_URL + answer.screenshot)
            .setTimestamp()
            .setFooter({ text: 'A small step for X, a giant leap for X' });
        await interaction.reply({ embeds: [itemEmbed] });
    },
};

I tried searching for the error message/similar cases online and on StackOverflow, but found nothing. Any help is greatly appreciated!

2 Answers

As you mentioned in your answer, there is no list item in the returned object. It should be items. Also, you received [object Object] because you implicitly coerced the returned object to a string (i.e. console.log(`Console log 3: `+itemSearchResult.body`).

The reason I'm answering though is that you could get rid of that ugly getJSONResponse() function and use a built-in body mixin to simplify your code:

async execute(interaction) {
  const itemName = interaction.options.getString('item_name');
  const { body } = await request(ITEM_URL + encodeURIComponent(itemName));
  const { items } = await body.json();

  if (!items.length)
    await interaction.reply(`No results found for **${itemName}**.`);

  const [answer] = items;
  const itemEmbed = new MessageEmbed()
    .setColor('#000000')
    .setTitle(answer.name)
    .setAuthor({ name: 'Author' })
    .setDescription('Lot of hassle, but hey, it was working!')
    .setImage(BASE_URL + answer.screenshot)
    .setTimestamp()
    .setFooter({ text: 'A small step for X, a giant leap for X' });
  await interaction.reply({ embeds: [itemEmbed] });
},

enter image description here

Had a friend take a look at my code and help out.

Issue was with const { list } = await getJSONResponse(itemSearchResult.body); because the API wasn't passing a list.

The working/correct code is:

const itemName = interaction.options.getString('item_name');
        
const itemSearchResult = await request(ITEM_URL + encodeURIComponent(itemName));
const response = await getJSONResponse(itemSearchResult.body);

const items = response.items;
const firstItem = response.items[0];
Related