Search code examples
sveltekitsveltekit-adapter-node

sveltekit fetch to backend yields internal server 500 error, but curl to same succeeds, better way to debug?


doc-frontend-1          | TypeError: fetch failed
doc-frontend-1          |     at Object.fetch (node:internal/deps/undici/undici:16289:11)
doc-frontend-1          |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
doc-frontend-1          |     at load (/.svelte-kit/adapter-node/entries/pages/languages/_page.server.ts.js:5:20)
doc-frontend-1          |     at load_server_data (/.svelte-kit/adapter-node/index.js:558:18)
doc-frontend-1          |     at <anonymous> (/.svelte-kit/adapter-node/index.js:2041:18) {
doc-frontend-1          |   cause: Error: connect ECONNREFUSED 127.0.0.1:5005
doc-frontend-1          |       at __node_internal_captureLargerStackTrace (node:internal/errors:496:5)
doc-frontend-1          |       at __node_internal_exceptionWithHostPort (node:internal/errors:671:12)
doc-frontend-1          |       at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1549:16) {
doc-frontend-1          |     errno: -111,
doc-frontend-1          |     code: 'ECONNREFUSED',
doc-frontend-1          |     syscall: 'connect',
doc-frontend-1          |     address: '127.0.0.1',
doc-frontend-1          |     port: 5005
doc-frontend-1          |   }
doc-frontend-1          | }

The code is in a +page.server.ts file when it fails. I can do the equivalent in a +page.svelte and it succeeds. I am new to sveltekit, but have researched this issue a lot without success so far.

import { PUBLIC_BACKEND_API_URL, PUBLIC_LANG_CODES_NAMES_URL } from '$env/static/public'
import type { PageServerLoad } from './$types'

export const load: PageServerLoad<{
  langCodeNameAndTypes: Array<[string, string, boolean]>
}> = async () => {
  const url = `${PUBLIC_BACKEND_API_URL}${PUBLIC_LANG_CODES_NAMES_URL}`
  console.log('About to fetch...')
  const response = await fetch(url) // fails here!
  console.log('About to await json()...')
  let langCodeNameAndTypes: Array<[string, string, boolean]> = await response.json()
  if (!response.ok) {
    console.log('response was NOT ok')
    throw new Error(response.statusText)
  }
  console.log('Response was ok, returning it...')
  return { langCodeNameAndTypes }
}

Any ideas?

Update: I tried hardcoding use of http://127.0.0.1:5005/theurlineededtouse as referenced on this github issue provided by @Trevor V in the comments (thanks Trevor), but that did not make a difference.

Update 2: I tried making the same fetch using axios since it provides more info in its stack trace and got the following:

doc-frontend-1          | About to fetch http://127.0.0.1:5005/language_codes_and_names...
doc-frontend-1          | AxiosError: connect ECONNREFUSED 127.0.0.1:5005
doc-frontend-1          |     at AxiosError.from (file:///app/node_modules/axios/lib/core/AxiosError.js:89:14)
doc-frontend-1          |     at RedirectableRequest.handleRequestError (file:///app/node_modules/axios/lib/adapters/http.js:610:25)
doc-frontend-1          |     at RedirectableRequest.emit (node:events:519:28)
doc-frontend-1          |     at eventHandlers.<computed> (/app/node_modules/follow-redirects/index.js:38:24)
doc-frontend-1          |     at ClientRequest.emit (node:events:519:28)
doc-frontend-1          |     at Socket.socketErrorListener (node:_http_client:492:9)
doc-frontend-1          |     at Socket.emit (node:events:519:28)
doc-frontend-1          |     at emitErrorNT (node:internal/streams/destroy:169:8)
doc-frontend-1          |     at emitErrorCloseNT (node:internal/streams/destroy:128:3)
doc-frontend-1          |     at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
doc-frontend-1          |     at Axios.request (file:///app/node_modules/axios/lib/core/Axios.js:45:41)
doc-frontend-1          |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
doc-frontend-1          |     at async load (file:///app/build/server/chunks/5-aKd3-39A.js:7:3)
doc-frontend-1          |     at async load_server_data (file:///app/build/server/index.js:1841:18)
doc-frontend-1          |     at async file:///app/build/server/index.js:3324:18 {
doc-frontend-1          |   port: 5005,
doc-frontend-1          |   address: '127.0.0.1',
doc-frontend-1          |   syscall: 'connect',
doc-frontend-1          |   code: 'ECONNREFUSED',
doc-frontend-1          |   errno: -111,
doc-frontend-1          |   config: {
doc-frontend-1          |     transitional: {
doc-frontend-1          |       silentJSONParsing: true,
doc-frontend-1          |       forcedJSONParsing: true,
doc-frontend-1          |       clarifyTimeoutError: false
doc-frontend-1          |     },
doc-frontend-1          |     adapter: [ 'xhr', 'http' ],
doc-frontend-1          |     transformRequest: [ [Function: transformRequest] ],
doc-frontend-1          |     transformResponse: [ [Function: transformResponse] ],
doc-frontend-1          |     timeout: 0,
doc-frontend-1          |     xsrfCookieName: 'XSRF-TOKEN',
doc-frontend-1          |     xsrfHeaderName: 'X-XSRF-TOKEN',
doc-frontend-1          |     maxContentLength: -1,
doc-frontend-1          |     maxBodyLength: -1,
doc-frontend-1          |     env: { FormData: [Function], Blob: [class Blob] },
doc-frontend-1          |     validateStatus: [Function: validateStatus],
doc-frontend-1          |     headers: Object [AxiosHeaders] {
doc-frontend-1          |       Accept: 'application/json, text/plain, */*',
doc-frontend-1          |       'Content-Type': undefined,
doc-frontend-1          |       'User-Agent': 'axios/1.6.7',
doc-frontend-1          |       'Accept-Encoding': 'gzip, compress, deflate, br'
doc-frontend-1          |     },
doc-frontend-1          |     method: 'get',
doc-frontend-1          |     url: 'http://127.0.0.1:5005/language_codes_and_names',
doc-frontend-1          |     data: undefined
doc-frontend-1          |   },
doc-frontend-1          |   request: <ref *1> Writable {
doc-frontend-1          |     _events: {
doc-frontend-1          |       close: undefined,
doc-frontend-1          |       error: [Function: handleRequestError],
doc-frontend-1          |       prefinish: undefined,
doc-frontend-1          |       finish: undefined,
doc-frontend-1          |       drain: undefined,
doc-frontend-1          |       response: [Function: handleResponse],
doc-frontend-1          |       socket: [Function: handleRequestSocket]
doc-frontend-1          |     },
doc-frontend-1          |     _writableState: WritableState {
doc-frontend-1          |       highWaterMark: 16384,
doc-frontend-1          |       length: 0,
doc-frontend-1          |       corked: 0,
doc-frontend-1          |       onwrite: [Function: bound onwrite],
doc-frontend-1          |       writelen: 0,
doc-frontend-1          |       bufferedIndex: 0,
doc-frontend-1          |       pendingcb: 0,
doc-frontend-1          |       [Symbol(kState)]: 17580812,
doc-frontend-1          |       [Symbol(kBufferedValue)]: null
doc-frontend-1          |     },
doc-frontend-1          |     _maxListeners: undefined,
doc-frontend-1          |     _options: {
doc-frontend-1          |       maxRedirects: 21,
doc-frontend-1          |       maxBodyLength: Infinity,
doc-frontend-1          |       protocol: 'http:',
doc-frontend-1          |       path: '/language_codes_and_names',
doc-frontend-1          |       method: 'GET',
doc-frontend-1          |       headers: [Object: null prototype],
doc-frontend-1          |       agents: [Object],
doc-frontend-1          |       auth: undefined,
doc-frontend-1          |       family: undefined,
doc-frontend-1          |       beforeRedirect: [Function: dispatchBeforeRedirect],
doc-frontend-1          |       beforeRedirects: [Object],
doc-frontend-1          |       hostname: '127.0.0.1',
doc-frontend-1          |       port: '5005',
doc-frontend-1          |       agent: undefined,
doc-frontend-1          |       nativeProtocols: [Object],
doc-frontend-1          |       pathname: '/language_codes_and_names'
doc-frontend-1          |     },
doc-frontend-1          |     _ended: true,
doc-frontend-1          |     _ending: true,
doc-frontend-1          |     _redirectCount: 0,
doc-frontend-1          |     _redirects: [],
doc-frontend-1          |     _requestBodyLength: 0,
doc-frontend-1          |     _requestBodyBuffers: [],
doc-frontend-1          |     _eventsCount: 3,
doc-frontend-1          |     _onNativeResponse: [Function (anonymous)],
doc-frontend-1          |     _currentRequest: ClientRequest {
doc-frontend-1          |       _events: [Object: null prototype],
doc-frontend-1          |       _eventsCount: 7,
doc-frontend-1          |       _maxListeners: undefined,
doc-frontend-1          |       outputData: [],
doc-frontend-1          |       outputSize: 0,
doc-frontend-1          |       writable: true,
doc-frontend-1          |       destroyed: false,
doc-frontend-1          |       _last: true,
doc-frontend-1          |       chunkedEncoding: false,
doc-frontend-1          |       shouldKeepAlive: true,
doc-frontend-1          |       maxRequestsOnConnectionReached: false,
doc-frontend-1          |       _defaultKeepAlive: true,
doc-frontend-1          |       useChunkedEncodingByDefault: false,
doc-frontend-1          |       sendDate: false,
doc-frontend-1          |       _removedConnection: false,
doc-frontend-1          |       _removedContLen: false,
doc-frontend-1          |       _removedTE: false,
doc-frontend-1          |       strictContentLength: false,
doc-frontend-1          |       _contentLength: 0,
doc-frontend-1          |       _hasBody: true,
doc-frontend-1          |       _trailer: '',
doc-frontend-1          |       finished: true,
doc-frontend-1          |       _headerSent: true,
doc-frontend-1          |       _closed: false,
doc-frontend-1          |       _header: 'GET /language_codes_and_names HTTP/1.1\r\n' +
doc-frontend-1          |         'Accept: application/json, text/plain, */*\r\n' +
doc-frontend-1          |         'User-Agent: axios/1.6.7\r\n' +
doc-frontend-1          |         'Accept-Encoding: gzip, compress, deflate, br\r\n' +
doc-frontend-1          |         'Host: 127.0.0.1:5005\r\n' +
doc-frontend-1          |         'Connection: keep-alive\r\n' +
doc-frontend-1          |         '\r\n',
doc-frontend-1          |       _keepAliveTimeout: 0,
doc-frontend-1          |       _onPendingData: [Function: nop],
doc-frontend-1          |       agent: [Agent],
doc-frontend-1          |       socketPath: undefined,
doc-frontend-1          |       method: 'GET',
doc-frontend-1          |       maxHeaderSize: undefined,
doc-frontend-1          |       insecureHTTPParser: undefined,
doc-frontend-1          |       joinDuplicateHeaders: undefined,
doc-frontend-1          |       path: '/language_codes_and_names',
doc-frontend-1          |       _ended: false,
doc-frontend-1          |       res: null,
doc-frontend-1          |       aborted: false,
doc-frontend-1          |       timeoutCb: [Function: emitRequestTimeout],
doc-frontend-1          |       upgradeOrConnect: false,
doc-frontend-1          |       parser: null,
doc-frontend-1          |       maxHeadersCount: null,
doc-frontend-1          |       reusedSocket: false,
doc-frontend-1          |       host: '127.0.0.1',
doc-frontend-1          |       protocol: 'http:',
doc-frontend-1          |       _redirectable: [Circular *1],
doc-frontend-1          |       [Symbol(shapeMode)]: false,
doc-frontend-1          |       [Symbol(kCapture)]: false,
doc-frontend-1          |       [Symbol(kBytesWritten)]: 0,
doc-frontend-1          |       [Symbol(kNeedDrain)]: false,
doc-frontend-1          |       [Symbol(corked)]: 0,
doc-frontend-1          |       [Symbol(kChunkedBuffer)]: [],
doc-frontend-1          |       [Symbol(kChunkedLength)]: 0,
doc-frontend-1          |       [Symbol(kSocket)]: [Socket],
doc-frontend-1          |       [Symbol(kOutHeaders)]: [Object: null prototype],
doc-frontend-1          |       [Symbol(errored)]: null,
doc-frontend-1          |       [Symbol(kHighWaterMark)]: 16384,
doc-frontend-1          |       [Symbol(kRejectNonStandardBodyWrites)]: false,
doc-frontend-1          |       [Symbol(kUniqueHeaders)]: null
doc-frontend-1          |     },
doc-frontend-1          |     _currentUrl: 'http://127.0.0.1:5005/language_codes_and_names',
doc-frontend-1          |     [Symbol(shapeMode)]: true,
doc-frontend-1          |     [Symbol(kCapture)]: false
doc-frontend-1          |   },
doc-frontend-1          |   cause: Error: connect ECONNREFUSED 127.0.0.1:5005
doc-frontend-1          |       at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1602:16) {
doc-frontend-1          |     errno: -111,
doc-frontend-1          |     code: 'ECONNREFUSED',
doc-frontend-1          |     syscall: 'connect',
doc-frontend-1          |     address: '127.0.0.1',
doc-frontend-1          |     port: 5005
doc-frontend-1          |   }
doc-frontend-1          | }

Solution

  • It ended up being a problem with the load function in +page.server.ts. This is what worked:

    import { PUBLIC_LANG_CODES_NAMES_URL } from '$env/static/public'
    import { env } from '$env/dynamic/public'
    import type { PageServerLoad } from './$types'
    
    export const load: PageServerLoad = async () => {
      const url = `${env.PUBLIC_BACKEND_API_URL}${PUBLIC_LANG_CODES_NAMES_URL}`
      console.log(`About to fetch ${url}...`)
      const response = await fetch(url)
      const langCodeNameAndTypes: Array<[string, string, boolean]> = await response.json()
      if (!response.ok) {
        throw new Error(response.statusText)
      }
      return { result: langCodeNameAndTypes }
    }
    

    The difference that ended up mattering? Making langCodeNameAndTypes keyed via my arbitrary result of the key name result.

    N.B. Initially it was suspected that there might be a problem with the config of docker networking in the docker-compose.yml file, but I did check out the docker networking and it was all fine as evidenced by docker exec-ing into each service and verifying by ping and curl -f that communication was working properly as. well as using inspect.

    N.B. This code was transitioning from plain Svelte to SvelteKit with a desire to use dynamic private env vars. Above the code uses public dynamic env var, but that can easily be changed by changing the PUBLIC_BACKEND_API_URL to be BACKEND_API_URL both in the code and in the .env file.