0

So I am doing a small app, to retrieve all characters that were featured in a Star Wars using swapi.dev/ .

The way it should work, is that once I select a film, all characters that were in it should appear, however, upon selecting different films, I get different results back, as an example, if I select Star Wars Episode 4, I get up to 10 characters, where I should obviously have more (18 if I am correct), if I select Episode 1, I get only 3 people. This is the code I am using to get the people:

async function fetchPeople() {
    let res = await fetch('https://swapi.dev/api/people/');
    let data = await res.json();
    return data.results;
}


export const showPeople = async (movies) => {
    const people = await fetchPeople();
    const matchingPeople = [];
    console.log('char count: ', movies.characters.length);
    console.log('peopel count: ', people.length);
    movies.characters.forEach((movieCharacter) => {
        people.forEach((person) => {
            const isMatching = person.url === movieCharacter;
            if (isMatching) {
                matchingPeople.push(person);
            }
        });
    });
    return matchingPeople;
}

Now for the tricky part: when I console.log "char count", I get the correct amount of characters (in the case of Episode 4 18 characters, that are in the API, however in the console.log "peopel count", I get only up to 10. It gets weirder.

Here is the code for the React component in getting to show the movies:

import React from 'react'
import { Card, Grid, Button } from 'semantic-ui-react'
import {showPeople} from '../controllers/ShowPeople'
// import fakedata from '../fakedata.json'


const Movies = ({data, setPeople}) => {
    console.log(setPeople)
    return (
        <>
        <h1>Movies</h1>
        <Grid columns={3}>
            {data && data.map((movies, i) => {
                return (
                    <Grid.Column key={i}>
                        <Card>
                            <Card.Content>
                                <Card.Header>{movies.title}</Card.Header>
                                <Card.Description>
                                    <strong>Episode</strong>
                                    <p>{movies.episode_id}</p>
                                    <strong>Release</strong>
                                    <p>{movies.release_date}</p>
                                </Card.Description>
                            <Button onClick={ async () => {
                                const people = await showPeople(movies)
                                console.log('test people ',people)
                                setPeople(people)
                            } }>
                                Show People
                            </Button>
                            </Card.Content>
                        </Card>
                    </Grid.Column>
                )
            })}
        </Grid>
    </>
    )
}

export default Movies;

Now, in console.log("test people"), I get different array results, depending on the movie I select, sometimes 6 people, sometimes 3, sometimes 10, but no more than 10 people.

Chrome console screenshot:

What could be the problem? At first I thought the API was restricting my requests, but I doubt I achieve 10k request per day on this API. Maybe The issue would be with the showPeople function?

Full code on github for more clarity

mrvanagas
  • 37
  • 1
  • 9
  • There are only 10 elements on the first page of `https://swapi.dev/api/people/` - If you look at [that page](https://swapi.dev/api/people/), you see it has a property called "next" with a different url that gives you the next 10, etc. – James Sep 06 '21 at 13:15
  • A common practice with apis is that the result is limited to x numbers. The star wars api uses 10 results per 'page'. It is done for multiple reasons but it results that you need an other api request. If you check the next property in the json request, you see that it will contain a "?page=2". If you add that, you get 10 more (or in your case 8 more for Episode 4). – Wojtek322 Sep 06 '21 at 13:19
  • @James oh crap, you are right! Then how I can code the program, to take advantage of the "next" property? Should the API link be written in a certain way? – mrvanagas Sep 06 '21 at 13:19
  • Check the documentation of that api for the possbilities Some apis let you change the "page size" to a certain amount. If the star wars api offers this option, you can do that via a query parameter. If the api does not offer this solution, you can check the documentation again. If there is no next page available, the next property will be null. You can keep fetching and append the results to a js array till there are no api requests left. Or you can do some maths and send multiple api requests. Be careful not making an infinite loop, you might get blacklisted for spamming it to much. – Wojtek322 Sep 06 '21 at 13:24

1 Answers1

2

You might want to check the documentation again of the api.

Take this url for example: api/people/?page=7

{
    "count": 82, 
    "next": "https://swapi.dev/api/people/?page=8", 
    "previous": "https://swapi.dev/api/people/?page=6", 
    "results": [...] //10 results
}

As you can see, this shows 10 results again. So we can conclude that the api will always return a maximum of 10 items per api call in the results property.

Most APIs offer 2 different solutions.

  1. They might have a property that lets you change the pageSize. For example, this might be used to return 50 objects in one api call.
  2. The most common is that they will have a page property. You will need a second api call to get the next x (10) items.

I can think of 2 different solutions.

You either work with the next property. This is a good way if the data set is quite large and you want to work with pagination.

You can also do some simple maths and work with the count property. For example, the wanted api call have 82 items. You can divide that by 10. You will have 8.2 and round it up. You now know it will have 9 pages so you can send 9 api calls. But keep in mind, this is not that friendly for an API and some public APIs can have some low rate limiting. If you send to much requests in a short amount of time, you might get blacklisted.

If you have received the results, append the results to a local array and you can do whatever you want with it (i.e. displaying the information).

But an important note is performance. Do you want your customers that they load in 82 different pictures? You might want to also work with pages or the data loads in when you scroll down via a new api call.

Wojtek322
  • 584
  • 7
  • 20
  • Thanks for an informative answer! I also looked over stackoverflow for similiar problems and I have found a solution, that uses recursion, which worked for me perfectly. Upon further looking into it, the program does several API calls and loads all the data inside the memory if I am not mistaken. Here is the link of the solution: https://stackoverflow.com/questions/49129245/javascript-using-fetch-and-pagination-recursive – mrvanagas Sep 06 '21 at 16:39