3

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
  }
}
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
CodeSauce
  • 255
  • 3
  • 19
  • 39
  • Oh sorry ... Its a header – CodeSauce Aug 23 '22 at 11:38
  • Currently developing both on localhost ... not a CORS issue as the login and register pages work perfectly – CodeSauce Aug 23 '22 at 11:46
  • can you try to log the error to the console in the first line of the catch block before any IF condition ? – Shadi Amr Aug 23 '22 at 14:06
  • and please double check of the baseURL inside the request, maybe it contains "/api" and as i see here you are also sending "/api" in the subURL (url parameter) – Shadi Amr Aug 23 '22 at 14:09
  • @ShadiAmr ... Im finding that i cant actually console log anything inside the `getServerSideProps` ... I guess thats becuase its doing it before rendering which im finding really frustrating The baseURL works fine, i have tested the code outside of the `getServerSideProps` and there isnt any problems .. I cant figure it out – CodeSauce Aug 23 '22 at 14:13
  • It seems as though its just returning the `notFound: true` straight away as its showing a 404 when i go to that page .... Its strange – CodeSauce Aug 23 '22 at 14:14
  • YES you can see the logs even inside getServerSideProps , but not in the browser , in terminal you 'll see it – Shadi Amr Aug 23 '22 at 14:15
  • And since it's a 404 , so it's mostly doesn't recognize the endpoint at all – Shadi Amr Aug 23 '22 at 14:16
  • so now just to ensure that everything is fine with the request , can you do this : `axions.get(${baseURL}/${url}, { headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: 'Bearer ' + token || '' } });` instead ? – Shadi Amr Aug 23 '22 at 14:20
  • Oh, it is logging to the terminal, i didnt know that .... If i `console.log(error)` it spits out a LOT of data – CodeSauce Aug 23 '22 at 14:22
  • `AxiosError: connect ECONNREFUSED 127.0.0.1:80` – CodeSauce Aug 23 '22 at 14:24
  • In my .env.local i have `NEXT_PUBLIC_BACKEND_URL=http://localhost` which is being used in the httpRequest get function – CodeSauce Aug 23 '22 at 14:25
  • i would like to see the whole error not only this line maybe i can then find some reference – Shadi Amr Aug 23 '22 at 14:33
  • @ShadiAmr I have updated the question with the error i am seeing in the console – CodeSauce Aug 23 '22 at 14:42
  • ok did you try to add await here : `return await axios({ ...` and don't forget to add async to the method : `get: async ({ baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL, url, token, params }) => {` – Shadi Amr Aug 24 '22 at 09:56
  • What port is the Laravel API running on locally? – juliomalves Aug 29 '22 at 16:41
  • From what I can see you trying to get a request to plain `localhost/api./user` So your fix should be to update the NEXT_PUBLIC_BACKEND_URL=http://localhost:[laravelPort] Cause you do not have anything running on port `80` ;] – T04435 Aug 31 '22 at 15:23

3 Answers3

1

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!!

Maker
  • 66
  • 5
  • Hey, thank you for the suggestion, but its still doing the same thing – CodeSauce Aug 24 '22 at 22:46
  • No problem. I updated my answer. Sorry I didn't see the problem sooner. I think I got distracted with the httpRequest because I'm not used to seeing that. I'm more used to fetch. Anyway, I think my edited answer will get you past this error. – Maker Aug 25 '22 at 00:06
  • Are you talking about moving the actual get method from the `httpRequest.js` file and move it into the `getServerSideProps` ? – CodeSauce Aug 25 '22 at 00:21
  • Move the code you have in api/users into getServerSideProps. – Maker Aug 25 '22 at 00:24
  • `api/users` is the backend route im calling ... Its not part of the frontend – CodeSauce Aug 25 '22 at 00:28
  • getServerSideProps can’t call internal backend routes. So what you do is move the code from the backend route into getServerSideProps. Then you don’t need to call the route. getServerSideProps is backend code. But it lives in a client-side file. – Maker Aug 25 '22 at 00:36
0

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)
    })
.....
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
-1

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
juliomalves
  • 42,130
  • 20
  • 150
  • 146