Server side rendering with Apollo: getaddrinfo ENOTFOUND - node.js

I'm running Apollo/React with Express and I'm trying to get server side rendering to work. The Apollo docs suggest the following initialisation code for connecting to the API server:
app.use((req, res) => {
match({ routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
const client = new ApolloClient({
ssrMode: true,
networkInterface: createNetworkInterface({
uri: 'http://localhost:3000', // Instead of 3010
opts: {
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
},
}),
});
const app = (
<ApolloProvider client={client}>
<RouterContext {...renderProps} />
</ApolloProvider>
);
getDataFromTree(app).then(() => {
const content = ReactDOM.renderToString(app);
const initialState = {[client.reduxRootKey]: client.getInitialState() };
const html = <Html content={content} state={initialState} />;
res.status(200);
res.send(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`);
res.end();
});
});
});
which uses the match() function from React Router v3 (as evidenced by package.json in the "GitHunt" example linked from the docs). I'm using React Router v4 from which match() is absent, so I refactored the code as follows, using renderRoutes() from the react-router-config package.
app.use((req, res) => {
const client = new ApolloClient(/* Same as above */)
const context = {}
const app = (
<ApolloProvider client={client}>
<StaticRouter context={context} location={req.originalUrl}>
{ renderRoutes(routes) }
</StaticRouter>
</ApolloProvider>
)
getDataFromTree(app).then(/* Same as above */)
})
My understanding is that <StaticRouter> obviates the use of match(). However react-router-config provides a matchRoutes() function which seems to provide a similar functionality (albeit without the callback) if needed.
When I visit http://localhost:3000, the page loads as expected and I can follow links to subdirectories (e.g. http://localhost:3000/folder). When I try to directly load a subdirectory by typing in the name in the address bar, my browser keeps waiting for the server to respond. After about six seconds, Terminal shows one of the following errors (not sure what causes the error to change on subsequent tries):
(node:1938) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 1): Error: Network error: request to
http://localhost:3000 failed, reason: getaddrinfo ENOTFOUND localhost
localhost:3000
or
(node:8691) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 1): Error: Network error: request to
http://localhost:3000 failed, reason: socket hang up
I've been struggling with this problem for a few hours now, but can't seem to figure it out. The solution to a similar problem seems unrelated to this case. Any help will be much appreciated!
Further information
If I don't kill the nodemon server, after some time I get thousands of the following errors:
POST / - - ms - -
(node:1938) UnhandledPromiseRejectionWarning:
Unhandled promise rejection (rejection id: 4443): Error: Network
error: request to http://localhost:3000 failed, reason: socket hang up
If I do kill the server, however, I immediately get this error instead:
/Users/.../node_modules/duplexer/index.js:31
writer.on("drain", function() {
^
TypeError: Cannot read property 'on' of undefined
at duplex (/Users/.../node_modules/duplexer/index.js:31:11)
at Object.module.exports (/Users/.../node_modules/stream-combiner/index.js:8:17)
at childrenOfPid (/Users/.../node_modules/ps-tree/index.js:50:6)
at kill (/Users/.../node_modules/nodemon/lib/monitor/run.js:271:7)
at Bus.onQuit (/Users/.../node_modules/nodemon/lib/monitor/run.js:327:5)
at emitNone (events.js:91:20)
at Bus.emit (events.js:188:7)
at process. (/Users/.../node_modules/nodemon/lib/monitor/run.js:349:9)
at Object.onceWrapper (events.js:293:19)
at emitNone (events.js:86:13)
Also, port 3000 is correct. If I change the number, I get a different error instead:
(node:2056) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 1): Error: Network error: request to
http://localhost:3010 failed, reason: connect ECONNREFUSED
127.0.0.1:3010

Are you running your server / project inside a container / containers. I had the same issue as this and ended up doing the following to fix it.
const networkInterface = createNetworkInterface({
uri: process.browser
? 'http://0.0.0.0:1111/graphql'
: 'http://my-docker-container-name:8080/graphql'
})
I have an internal docker network created for my containers in my docker-compose.yml, which allows the containers to communicate with each other, however the browser communicates to the GQL server on a different URL, causing the issue you described on SSR the getaddrinfo ENOTFOUND, so although it was working client side, on the SSR it would fail.
I am using nextJS framework which gives the ability to detect the browser or SSR, i'm sure you could do the same outside of nextJS.

I finally found out that the error was due to renderToString and renderToStaticMarkup not being made available by importing ReactDOM.
The import statement import ReactDOM from 'react-dom' had to be replaced by import ReactDOMServer from 'react-dom/server'.
Also, uri had to point to http://localhost:3000/graphql instead of http://localhost:3000.

Related

Fetch error when refreshing page with await block in SvelteKit

I am building a sveltekit app that crashes when I refresh the page. Below is the code for the Home page that displays a map with points. The points are fetched from an api (also running on localhost). I initially had the await call inside an onMount function (worked fine) but I thought it would be tidier to use an await block. When the app is running everything works great - I can navigate to and from the Home page and the map loads as expected. However, if I refresh the browser on the Home page or navigate directly to the Home page after starting the app it crashes.
Update:
I now realize that using an await block or sveltkit's load function will run the fetch function on the server side before the page loads. This appears to fail because the map component relies on window. Disabling SSR fixes the issue although I would expect that the data object could be fetched via a load function and this not interfere with component creation, i.e. SSR shouldn't need to be disabled.
Svelte page:
<script>
const API_PATH = import.meta.env.VITE_API_PATH;
import Map from '$lib/Leaflet/Map.svelte';
import Point from '$lib/Leaflet/Vector/Point.svelte';
const lat = -41.273;
const lng = 173.279;
const zoom = 15;
async function getSites() {
const res = await fetch(API_PATH + '/sites');
const sites = await res.json();
if (res.ok) {
return sites;
} else {
throw new Error(sites);
}
}
let promise = getSites();
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<main>
<Map center={[lat, lng]} {zoom}>
{#await promise then sites}
{#each Object.values(sites['features']) as site}
<Point lat={site['geometry']['coordinates'][1]} lng={site['geometry']['coordinates'][0]}>
</Point>
{/each}
{/await}
</Map>
</main>
I get the following error in terminal:
Got request /
file:///.../node_modules/#sveltejs/kit/dist/install-fetch.js:6246
reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error));
^
FetchError: request to http://localhost:8000/sites failed, reason: connect ECONNREFUSED ::1:8000
at ClientRequest.<anonymous> (file:///.../node_modules/#sveltejs/kit/dist/install-fetch.js:6246:11)
at ClientRequest.emit (node:events:520:28)
at Socket.socketErrorListener (node:_http_client:442:9)
at Socket.emit (node:events:520:28)
at emitErrorNT (node:internal/streams/destroy:164:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
type: 'system',
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED',
erroredSysCall: 'connect'
}
And in the browser console:
Loading failed for the module with source “http://localhost:3000/#fs/.../.svelte-kit/runtime/client/start.js”.
Thanks for the support.

Error: getaddrinfo ENOTFOUND on Vercel with Next.JS and serverless-mysql

I am using Next.js and in the API side i have write a dummy code to get rows from my database with this library : serverless-mysql
I have followed the example on the documentation and on my computer this working very fine, I can connect to the database et get the rows. My Database is on my VPS not on my localhost.
But when i deploy my code on Vercel, and I try to access to /api/hello
In my vercel log I have this error :
[GET] /api/hello
{
error: Error: Error: getaddrinfo ENOTFOUND "**.***.**.**"
at connect (/var/task/node_modules/serverless-mysql/index.js:80:15)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Object.query (/var/task/node_modules/serverless-mysql/index.js:182:5)
at async excuteQuery (/var/task/.next/server/pages/api/hello.js:33:25)
at async handler (/var/task/.next/server/pages/api/hello.js:59:24)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils.js:102:9)
at async Server.handleApiRequest (/var/task/node_modules/next/dist/server/next-server.js:1064:9)
at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:951:37)
at async Router.execute (/var/task/node_modules/next/dist/server/router.js:222:32)
at async Server.run (/var/task/node_modules/next/dist/server/next-server.js:1135:29)
}
(I have replaced the real Ip showed in the message by "** . *** . ** . **")
My database accept connection from outside because I can access to it on my computer.
I have also correctly configured the .env var in project settings.
Thank you very much for your help
You will need to set the environment variables both on your vercel dashboard and your nextjs app.
In your .env file
NEXT_PUBLIC_VERCEL_URL = "http://localhost:3000";
In your code, reference the variable
export const getBaseUrl = () => {
if (process.env.NODE_ENV === "development") {
return "http://localhost:3000";
}
return process.env.NEXT_PUBLIC_VERCEL_URL;
}
You can then execute the utility function anywhere in your code.
On vercel, set the environment variable to VERCEL_URL

Redis sentinel connection is timing out from nodeJS

Am trying to connect redis sentinel instance from nodeJS using ioredis. Am not able to connect redis sentinel instance despite trying multiple available options. We have not configured sentinel password. But, able to connect same redis sentinel instance from .net core using StackExchange.Redis. Please find below nodeJS code,
import { AppModule } from './app.module';
import IORedis from 'ioredis';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const ioredis = new IORedis({
sentinels: [
{ host: 'sentinel-host-1' },
{ host: 'sentinel-host-2' },
{ host: 'sentinel-host-3' },
],
name: 'mastername',
password: 'password',
showFriendlyErrorStack: true,
});
try {
ioredis.set('foo', 'bar');
} catch (exception) {
console.log(exception);
}
await app.listen(3000);
}
bootstrap();
Error we got is,
[ioredis] Unhandled error event: Error: connect ETIMEDOUT
node_modules\ioredis\built\redis\index.js:317:37)
at Object.onceWrapper (node:events:475:28)
at Socket.emit (node:events:369:20)
at Socket._onTimeout (node:net:481:8)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
Connection String used from .net core is below,
Redis_Configuration = "host-1,host-2,host-3,serviceName=mastername,password=password,abortConnect=False,connectTimeout=1000,responseTimeout=1000";
Answering this for the benefit of others. Everything is fine, but this nodeJS package is resolving redis instances into private IPs which i cannot access from my local. So, had to put it over subnet group and make it work. However, FYI - .net core package does not resolve into private IPs, hence i was able to access instances from my local itself.
"The arguments passed to the constructor are different from the ones you use to connect to a single node"
Try to replace password with sentinelPassword.

socket.io in useEffect produces Proxy Error (ECONNRESET)

Summary
I've built a prototype for an app that is going to have some sort of chat functionality. As of right now the frontend React just pulls data via useEffect. To implement a more dynamic chat feeling I thought about using socket.io. When instantiating socket.io client-side as suggested (in useEffect) I am getting an ECONNRESET error.
Setup:
Backend: Node.js + express.js (listening on Port 5000)
Frontend: React
Frontend: Proxy for local development in package.json like this "proxy": "http://localhost:5000"
Problem:
When initializing my socket client-side like this:
const Flow = () => {
...
const fetchsocketData = () => {
const socket = io();
console.log("trying socket stuff");
socket.on("FromBackEnd", data => {console.log(data)});
};
useEffect(() => fetchsocketData(),[])
...
return (<div>Yolo</div>)
}
The proxying (as defined in the package.json) works nicely with e.g. Axios-calls, however, with socket.io, I get the following error on the server-side:
Proxy error: Could not proxy request
/socket.io/?EIO=3&transport=polling&t=N5v5GOe&sid=yugXlgWYsoqJRqcxAAAT
from localhost:3000 to http://localhost:5000. See
https://nodejs.org/api/errors.html#errors_common_system_errors for
more information (ECONNRESET).
And the following error on the client-side:
websocket.js:116 WebSocket connection to
'ws://localhost:3000/socket.io/?EIO=3&transport=websocket&sid=QKMDK2qmVGT3eud2AAAA'
failed: Error during WebSocket handshake: Unexpected response code:
400
This seems to be a temporal thing though, as the socket.io-connection is ultimately established and emits the test messages.
To make things a little weirder: If I move the socket instantiation into a user-triggered event:
const manuallyInstantiateSocket = () => {
const socket = io();
console.log("trying socket stuff");
socket.on("FromBackEnd", data => {console.log(data)});
}
And call this on a click of a button all works as expected. No error on the back-end side of things. The front-end error persists though.
What am I missing here?

Unhandled promise rejection warning in Webpack, for Vue.js

In an attempt to add a "server-side" state machine service to a single page application of Vue.js (cli), I edited following lines in webpack-dev-server/lib/Server.js:
const app = this.app = new express(); // eslint-disable-line
var globalStore ={ numRequests : 0 };
// I added this by copy-pasting another app.all('/',..) request handler.
app.all('/compute', (req, res, next) => {
globalStore.numRequests++;
res.send("num request="+globalStore.numRequests);
return next();
});
then it gave this warning each time counter is incremented:
(node:12956) UnhandledPromiseRejectionWarning: Error
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the
client
Still, it increments the counter, returns it successfully everytime I visit {mydomainname}/compute. Everytime it gives same warning.
I guess I'm making a simple error but can't see. Project was produced by this command:
vue init webpack vueapp01
If I delete "return next" and "res.send ('invalid host header')" lines, no warning is produced. Do I have to call "return" here always?

Resources