Silinde87
Silinde87

Reputation: 41

NextJS API | App works in deploy and development mode but it doesn't when I run next build | ECONNREFUSED

Describe the bug

I have built an application with NextJS that has an internal API and I use the getStaticProps methods to query the API.

You can find the deployed app here: https://tailor-challenge.vercel.app/

And also the github repository here: enter link description here

The application works perfectly in development mode and the deploy I made in Vercel also works. But when I run the next build command I get the following error:

Build error occurred
FetchError: request to http://localhost:3000/api/restaurants failed, reason: connect ECONNREFUSED 127.0.0.1:3000
    at ClientRequest.<anonymous> (/home/silinde87/Jobs/tailor/TailorChallenge/node_modules/node-fetch/lib/index.js:1461:11)
    at ClientRequest.emit (node:events:376:20)
    at Socket.socketErrorListener (node:_http_client:474:9)
    at Socket.emit (node:events:376:20)
    at emitErrorNT (node:internal/streams/destroy:188:8)
    at emitErrorCloseNT (node:internal/streams/destroy:153:3)
    at processTicksAndRejections (node:internal/process/task_queues:80:21) {
  type: 'system',
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED'
}
info  - Collecting page data .npm ERR! code 1
npm ERR! path /home/silinde87/Jobs/tailor/TailorChallenge
npm ERR! command failed
npm ERR! command sh -c next build 

NextJS internal API

/api/restaurants/index.js

export default function handler(req, res) {
    const { method } = req;
    
    switch (method) {
        case 'GET':
            getRestaurants();
            break;
        default:
            res.status(405).end(`Method ${req.method} Not Allowed`);
    }

    function getRestaurants() {
        const restaurants = data;
        res.status(200).json(restaurants);
    }
}

/api/restaurants/[id].js

export default function handler(req, res) {
    const { id } = req.query;
    const { method } = req;

    switch (method) {
        case 'GET':
            getRestaurantById();
            break;
        default:
            res.status(405).end(`Method ${req.method} Not Allowed`);
    }

    function getRestaurantById() {
        const restaurants = data.find((el) => el.id.toString() === id.toString());
        if (restaurants) res.status(200).json(restaurants);
        else res.status(405).end(`There is no Restaurant with id: ${id}`);
    }
}

Pages from NextJS

pages/index.js <-- Where I fetch the restaurants list

export async function getStaticProps() {
    const res = await fetch(`${process.env.NEXT_PUBLIC_NEXTAUTH_URL}/api/restaurants`);
    const restaurants = await res.json();

    if (!restaurants) {
        return {
            notFound: true,
        };
    }

    return {
        props: {
            restaurants,
        },
    };
}

pages/[id].js

// Return a list of possible value for id
export async function getStaticPaths() {
    const res = await fetch(`${process.env.NEXT_PUBLIC_NEXTAUTH_URL}/api/restaurants`);
    const restaurants = await res.json();

    let paths = restaurants.map(({ id }) => {
        return {
            params: {
                id: id.toString(),
            },
        };
    });
    return { paths, fallback: false };
}

// Fetch necessary data for the restaurant details using params.id
export async function getStaticProps({ params }) {
    const res = await fetch(`${process.env.NEXT_PUBLIC_NEXTAUTH_URL}/api/restaurants/${params.id}`);
    const restaurantData = await res.json();

    return {
        props: {
            restaurantData,
        },
    };
}

Upvotes: 4

Views: 4470

Answers (1)

tperamaki
tperamaki

Reputation: 1028

I took this quote from the official documentation, link below:

Note: You should not use fetch() to call an API route in >getStaticProps. Instead, directly import the logic used inside >your API route. You may need to slightly refactor your code for >this approach.

Fetching from an external API is fine!

https://nextjs.org/docs/basic-features/data-fetching

Basically because the code inside getStaticProps or getStaticPaths is never executed in the browser, so you can safely call the data source directly there.

Also the failure happens because the app is not running when building, so the localhost api is not available. You can confirm this by running the app via ’yarn dev’ or ’npm run dev’ in another terminal window, and building the app in another terminal window. This way it should build just fine, but it's not recommended way of doing it. I would refactor the code so that there is no fetch to the api of this app in those getStaticProps and getStaticPaths functions.

Same goes for getServerSideProps, that is also just run on the server, so you shouldn't be doing fetches to the api there either. Only the browser should do fetches to the api, for example if you want the user to be able to post some data to the app.

Upvotes: 6

Related Questions