I created an api using NestJs and GraphQL, exposing a GraphQL server (Apollo) with a certain port.
Everything works fine as long as I am using http://localhost:3001/graphql endpoint. As soon as I change to http://<ip-address>:3001/graphql the connection cannot be established.
I modified main.ts file to include the host adress as well, and I was expecting to be able to reach the endpoint withing the same machine, but in vain.
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3001, '0.0.0.0', async () => {
console.log(`🚀 Server started at ${await app.getUrl()}`);
});
}
bootstrap();
Dependencies:
"#nestjs/graphql": "^10.2.0",
"graphql": "^16.6.0",
"#nestjs/apollo": "^10.2.0",
Related
I'm trying to create a simple WebSocket server that will run in a SvelteKit application. I found this tutorial online which shows how to do it using Socket.io, however I would like to use the ws module instead.
This is the vite.config.ts file that I've come up with so far:
import type { UserConfig } from 'vite';
import { sveltekit } from '#sveltejs/kit/vite';
import { WebSocketServer } from "ws";
const webSocketServer = {
name: "webSocketServer",
configureServer: () => {
const webSocketServer = new WebSocketServer({
port: 8080
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("test from server");
});
}
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
I can connect to this WebSocket server from the frontend (in +page.svelte, for example) and send messages between them.
But every time I make a change to my server's code and save the file, I get an error saying that "there's already something listening on port 8080" and my Vite dev server terminates. If I then start back up my Vite dev server by running npm run dev then it will all work fine again, until I make any change to the WebSocket server code and save the file, and then the same thing will repeat.
I would rather not have to re-start my Vite dev server every time I make a change to my WebSocket server's code. If possible, I would rather Vite automatically shut down my old webSocket server to make room for the new one before booting it up each time so that they won't conflict.
The tutorial I linked above shows this code snippet:
import adapter from '#sveltejs/adapter-node'
import preprocess from 'svelte-preprocess'
import { Server } from 'socket.io'
const webSocketServer = {
name: 'webSocketServer',
configureServer(server) {
const io = new Server(server.httpServer)
io.on('connection', (socket) => {
socket.emit('eventFromServer', 'Hello, World 👋')
})
},
}
/** #type {import('#sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter(),
vite: {
plugins: [webSocketServer],
},
},
}
export default config
This snippet is using an old SvelteKit version where Vite configuration was done inside the svelte.config.js file, so the layout is a little different, but it seems they're simply spinning up their Socket.io server directly inside the configureServer method just like I am, but they're tapping into the existing Vite http server instead of creating a new one like I am. I tried doing this and I still get the same problem. Every time I try httpServer.listen(8080); on Vite's http server I'm told that the server was already listening on a port and the dev server terminates. I also tried manually creating an http server using require("http").createServer() and using that, but (unsurprisingly) that also did not work and acted identically to my initial attempt shown at the beginning of this question.
The tutorial seems to be booting up a Socket.io server the same way I'm trying to boot up mine, but they don't seem to be running into any conflicts like I am. I checked Socket.io's documentation to see if perhaps the Server constructor has a built-in failsafe to make sure it doesn't listen on a port if it's already listening on that port (and avoid creating the error), but the docs didn't give any information in that regard, so I'm still unsure as to what's going on there.
Is there any way to do what I'm trying to do, or am I going about this entirely the wrong way? I can't find hardly any information about this anywhere on the internet.
And also, what's going on in the Socket.io example that allows it to work where mine won't? Is Socket.io doing something special?
Here's my attempt at using Vite's built-in http server. This behaves the same as my other attempts.
vite.config.ts
import type { UserConfig } from 'vite';
import { sveltekit } from '#sveltejs/kit/vite';
import { WebSocketServer } from "ws";
const webSocketServer = {
name: "webSocketServer",
configureServer: (server: any) => {
const httpServer = server.httpServer;
const webSocketServer = new WebSocketServer({
noServer: true
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("hi c:");
});
httpServer.on("upgrade", (request: any, socket: any, head: any) => {
webSocketServer.handleUpgrade(request, socket, head, socket => {
webSocketServer.emit("connection", socket, request);
});
});
httpServer.listen(8080);
}
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
ws supports initialising with an existing server like socket.io
Don't store the application logic in the vite/svelte config, as it will be needed for the production build when vite is not available (I'm not familiar with sveltekit though so maybe it does some magic?). Create a standalone file for the ws setup:
import { WebSocketServer } from "ws";
export const configureServer = (server) => {
const webSocketServer = new WebSocketServer({
server: server.httpServer,
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("test from server");
});
}
export const webSocketServer = {
name: "webSocketServer",
configureServer,
}
Then you should be able to use your version of webSocketServer like the socket.io example as they both attach to the '#sveltejs/adapter-node' server.
import adapter from '#sveltejs/adapter-node'
import { webSocketServer } from './sockets.js';
/** #type {import('#sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
vite: {
plugins: [webSocketServer],
},
},
}
export default config
configureServer can now be reused when you setup the custom server for production.
I have a nodejs application which starts asynchronously because of graphql.
require('custom-env').env();
import { DateTruncAggregateGroupSpecsPlugin } from './subgraphs/db/date_trunc_aggregate_group_specs_plugin';
import PgAggregatesPlugin from "#graphile/pg-aggregates";
import FederationPlugin from "#graphile/federation";
import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter";
const PostGraphileDerivedFieldPlugin = require("postgraphile-plugin-derived-field");
import express from "express";
import { ApolloServer, gql } from "apollo-server-express";
const { makeSchemaAndPlugin } = require("postgraphile-apollo-server");
import pg from 'pg';
import { makeExtendSchemaPlugin } from "graphile-utils";
import { readFileSync } from 'fs';
import { resolve } from 'path';
import resolvers from './resolvers';
export let app = express();
export let server: any;
const { PORT, NODE_ENV, SCHEMA, DATABASE_URL } = process.env;
async function main() {
const { schema, plugin } = await makeSchemaAndPlugin(
new pg.Pool({
connectionString: DATABASE_URL
}),
SCHEMA,
{
subscriptions: false,
appendPlugins: [
FederationPlugin,
ConnectionFilterPlugin,
PostGraphileDerivedFieldPlugin,
PgAggregatesPlugin,
DateTruncAggregateGroupSpecsPlugin,
makeExtendSchemaPlugin((build) => ({
typeDefs: gql(readFileSync(resolve(__dirname, '../graphs/custom.graphql'), { encoding: 'utf-8' })),
resolvers
}))
],
graphileBuildOptions: {
connectionFilterRelations: true
}
}
);
const graphql = new ApolloServer({
debug: false,
schema,
plugins: [plugin],
introspection: true
});
await graphql.start();
graphql.applyMiddleware({
app,
path: '/graphql'
});
server = this.app.listen(PORT, () => console.info(`🚀 Running on PORT ${PORT} 🚀`));
}
main();
The above is my express server that adds graphql to it.
As you can see, the starting of the server is asynchronous.
Now I am using supertest to test APIs end-to-end. Supertest requires app to be passed in.
I need server to start before all tests in my project and tests to be able to use app for supertest reuqest.
How do I do that. With regualar server it is easy as starting of server is not asynchronous, so my app is ready to use by tests. But not in this case. How do I carry out supertest requests.
I have launched my first node.js app using express.js for the backend and react.js for the frontend. I have it deployed as a background service on a VM with Ubuntu 20.04. The app itself is pretty simple. It makes an API call, gets the latest data and displays it to the enduser in a web browser interface. I initially deployed it on a free Heroku account, where it works fine and always display the latest info from the API call. But when I deployed it on a private VM with Ubuntu 20.04, I noticed that it does not show the latest API info anymore, i.e. it makes the initial API call during npm start and then probably caches it on the server. So, if I come to the app the next day, all the data is from yesterday. To see the latest data, I have to kill the background service on Ubuntu and then do "npm start", which starts the server and makes the API call. I then see the latest data in the browser. This is my first time working with all this technology, so if you have had a similar issue with deploying on Ubuntu, please let me know how I can fix it.
Here is my package.json "scripts":
"scripts": {
"start:dev": "nodemon --exec babel-node server/server.js --ignore public/",
"build": "babel server --out-dir dist",
"start": "node dist/server.js &",
"dev": "webpack -w --mode=development",
"prod": "webpack --mode=production"
}
Here is my server.js code:
import config, { nodeEnv, logStars } from "./config";
import apiRouter from "./api";
import sassMiddleware from "node-sass-middleware";
import path from "path";
import AMdataPullDone from "./AMdataPullDone";
import express from "express";
const server = express();
server.use(
sassMiddleware({
src: path.join(__dirname, "sass"),
dest: path.join(__dirname, "public"),
})
);
server.set("view engine", "ejs");
server.get("/", (req, res) => {
AMdataPullDone.then(({ initialMarkup, initialData }) => {
res.render("index", {
initialMarkup,
initialData,
});
}).catch(console.error);
});
server.use("/api", apiRouter);
server.use(express.static("public"));
server.listen(config.port, config.host, () => {
console.info("Express listening on port ", config.serverUrl);
});
And here is my index.js file with the reach App:
import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
ReactDOM.hydrate(
<App initialData={window.initialData} />,
document.getElementById("root")
);
AMdataPullDone.js code:
import React from "react";
import ReactDOMServer from "react-dom/server";
import App from "./src/components/App";
import generateToken from "./utils/generateToken";
import serverGetAMData from "./serverGetAMData";
const AMdataPullDone = new Promise((resolve, reject) => {
generateToken()
.then(({ access_token }) => {
serverGetAMData(
access_token,
"URL_to_external_API"
)
.then((segResult) => {
const allData = {
initialMarkup: ReactDOMServer.renderToString(
<App initialData={segResult} />
),
initialData: segResult,
};
return resolve(allData);
})
.catch((err) => {
return reject(err.response);
});
})
.catch(console.error);
});
export default AMdataPullDone;
serverGetAMData.js code:
import axios from "axios";
import config from "./config";
const serverGetAMData = async (access_token, apiUrl) => {
const folders = [];
await axios
.get(apiUrl, {
headers: {
Authorization: `Bearer ${access_token}`,
"x-api-key": config.AM_IMS_client_id,
"x-gw-ims-org-id": config.org_id,
}
})
.then((resp) => {
folders.push(...resp.data);
//return resp.data;
})
.catch(console.error);
return folders;
};
export default serverGetAMData;
I Use NestJs as a backend server and angular as a frontend
it's okay with me when I use chrome on pc and I can do all my requests
but when I use my android chrome with DevTools I receive this error
message: "Http failure response for http://localhost:3000/users/login: 0 Unknown Error"
here is a snapshot of the error message
enter image description here
it also send the same error with pc chrome if i didnt open CORS on NestJs
here Is My COSR Config
import { ValidationPipe } from '#nestjs/common';
import { NestFactory } from '#nestjs/core';
import { NextFunction, Request, Response } from 'express';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use((req: Request, res: Response, next: NextFunction) => {
console.log(req);
next();
});
app.useGlobalPipes(new ValidationPipe());
app.enableCors({
origin: true,
methods: ['GET', 'PUT', 'POST', 'DELETE'],
exposedHeaders: ['x-auth'],
});
await app.listen(3000);
}
bootstrap();
by the way when I log the request on my nest app
I didn't receive any request
I think NestJsrefused it immediately
I found Where is the problem
I was using the localhost as a Backend IP and the application was connecting on my local pc Backend and that was the problem
I solved this by editing the Backend IP to the production IP
and finally, everything works good ,, Thanks All
I'm new to NestJS and on every route my web app is trying to query, it fails on the OPTIONS request, getting:
{"statusCode":404,"error":"Not Found","message":"Cannot OPTIONS
/authenticate"}
however trying a direct GET or POST request works fine.
after some researches I've realised that I simply needed to enable CORS (Access-Control-Allow-Origin), which I can do by editing my main.ts and passing cors: true to the NestFactory.create options (second parameter).
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
await app.listen(3000);
}
bootstrap();
Some extra info on CORS, if you enable it via:
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
await app.listen(3000);
}
bootstrap();
This will allow Cross Origin Requests from any domain. Which is generally not security best practice.
If you want to allow CORS to intercept your preflight requests, but also only allow origin requests from within the server, you can use this config:
.....
const app = await NestFactory.create(ApplicationModule, {cors: {
origin: true,
preflightContinue: false,
}});
.....
Anyone still looking for the answer
app.enableCors();