Nextjs failing on getServerSideProps for no reason

Viewed 221

I am learning NextJS .. I have an API (in Laravel) that has an endpoint /api/user which is protected with a token and just returns a very simple user object as below;

{
    "data": {
        "id": "2",
        "name": "Testing User",
        "email": "testing@mctestface.com",
        "updated_at": "2022-08-23T10:39:32.000000Z",
        "created_at": "2022-08-23T10:39:32.000000Z"
    }
}

I am trying to create a user settings page in NextJS ... I am hitting the user endpoint, with the token which is stored as a cookie, if the response is 200 then it sets the props .. If it fails, it should redirect the user to the login page. but whenever i hit the page it shows the 404 page, which means it seems to be entering into the catch part of the try block ... I have tested the api endpoint with the token and it works perfectly, its just when i use it as below it fails;

import React from 'react'

import httpRequest from '@/lib/httpRequest'
import { getCookie } from '@/lib/session'

const Settings = ({ dashboardUser }) => {
    return (
        <div>
            <p>Protected Page</p>
        </div>
    )
}

export async function getServerSideProps({ req }) {
    try {
        const resDashboardUser = await httpRequest.get({
            url: '/api/user',
            token: getCookie('token', req)
        })

        if (resDashboardUser.status === 200) {
            return {
                props: {
                    dashboardUser: resDashboardUser.data
                }
            }
        }
    } catch (error) {
        if (error?.response?.status === 401) {
            return {
                redirect: {
                    destination: '/login',
                    permanent: false
                }
            }
        }

        return {
            notFound: true
        }
    }
}

export default Settings

The following is the get function of the httpRequest

get: ({ baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL, url, token, params }) => {
    return axios({
        timeout: process.env.NEXT_PUBLIC_API_TIMEOUT,
        method: 'get',
        baseURL: baseUrl,
        url: url,
        headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            Authorization: 'Bearer ' + token || ''
        },
        params: params
    });
},

Any help would be greatly appreciated.

The error I am seeing is as follows;

Promise { <pending> }
error - unhandledRejection: AxiosError: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1138:16) {
  port: 80,
  address: '127.0.0.1',
  syscall: 'connect',
  code: 'ECONNREFUSED',
  errno: -111,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: '30000',
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function] },
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      Authorization: 'Bearer 8|jcL6IL08oMqKET2r0Skhjx1Dw02UTeToNukQpqm4',
      'User-Agent': 'axios/0.27.2'
    },
    method: 'get',
    baseURL: 'http://localhost',
    url: '/api/user',
    data: undefined
  },
  request: <ref *1> Writable {
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: false,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: true,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      constructed: true,
      prefinished: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: true,
      errored: null,
      closed: false,
      closeEmitted: false,
      [Symbol(kOnFinished)]: []
    },
    _events: [Object: null prototype] {
      response: [Function: handleResponse],
      error: [Function: handleRequestError],
      socket: [Array]
    },
    _eventsCount: 3,
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: 10485760,
      protocol: 'http:',
      path: '/api/user',
      method: 'GET',
      headers: [Object],
      agent: undefined,
      agents: [Object],
      auth: undefined,
      hostname: 'localhost',
      port: null,
      nativeProtocols: [Object],
      pathname: '/api/user'
    },
    _ended: true,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 0,
    _requestBodyBuffers: [],
    _onNativeResponse: [Function (anonymous)],
    _currentRequest: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Socket],
      _header: 'GET /api/user HTTP/1.1\r\n' +
        'Accept: application/json\r\n' +
        'Content-Type: application/json\r\n' +
        'Authorization: Bearer 8|jcL6IL08oMqKET2r0Skhjx1Dw02UTeToNukQpqm4\r\n' +
        'User-Agent: axios/0.27.2\r\n' +
        'Host: localhost\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: {},
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/api/user',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'localhost',
      protocol: 'http:',
      _redirectable: [Circular *1],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    _currentUrl: 'http://localhost/api/user',
    _timeout: null,
    [Symbol(kCapture)]: false
  }
}
3 Answers

Instead of making an http request from getServerSideProps to an api route, move the code from api/users to getServerSideProps. Do the server-side work in getServerSideProps.

getServerSideProps is server-side code even though it is in a client-side file. When you call a server-side route (e.g., api/users) from getServerSideProps or other static functions, it doesn't work.

The example says from external API. Here is another answer about it.

So let me know how that goes.

Meanwhile . . .

A few notes that might be irrelevant after you refactor, if you're not sending an http request to get the cookie. But worth mentioning anyway.

When making http requests in NextJS, fetch is easier than httpRequest, assuming it works for your situation.

When sending https requests, try adding async / await to handle Promises that axios or fetch or httpRequest returns.

Here is an example.

get: async ({ baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL, url, token, params }) => {
    return await axios({
        timeout: process.env.NEXT_PUBLIC_API_TIMEOUT,
        method: 'get',
        baseURL: baseUrl,
        url: url,
        headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            Authorization: 'Bearer ' + token || ''
        },
        params: params
    });
},

When you see Promise { <pending> }, that means you need to handle the Promise to get the actual data you want. A Promise is just a placeholder for a value that doesn't exist when the page loads. The value doesn't exist at load time because axios has to request and receive it.

When you see error - unhandledRejection: AxiosError:, that suggests maybe the error is because the code doesn't handle the Promise.

You can handle Promises using async / await or .then(). Which one to use is typically driven by the situation and your preference. Here, async / await makes sense.

However, for your situation, I think the http request is problematic because of how getServerSideProps and api routes don't work together. Good luck!!

When you make request in getServerSideProps, you have to pass full url. when you are on server, you have no access to host. next-absolute-url package will get us the host

import absoluteUrl from 'next-absolute-url'

export async function getServerSideProps({ req }) {
const { origin } = absoluteUrl(req);

try {
    const resDashboardUser = await httpRequest.get({
        url: `${origin}/api/user`,
        token: getCookie('token', req)
    })
.....

You need to specify the port where the Laravel API is running on in the NEXT_PUBLIC_BACKEND_URL value.

Assuming the Laravel API is running on port 8080 you should update the environment variable to the following.

NEXT_PUBLIC_BACKEND_URL=http://localhost:8080
Related