Cannot GET / error using express - node.js

I'm new to nodeJS, I'm trying to follow this tutorial.
My code:
// server/index.js
import express from 'express';
import { graphqlExpress, graphiqlExpress } from 'graphql-server-express';
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import bodyParser from 'body-parser';
import { createServer } from 'http';
import { Schema } from './data/schema';
import { Mocks } from './data/mocks';
const GRAPHQL_PORT = 8000;
const app = express();
const executableSchema = makeExecutableSchema({
typeDefs: Schema,
});
addMockFunctionsToSchema({
schema: executableSchema,
mocks: Mocks,
preserveResolvers: true,
});
// `context` must be an object and can't be undefined when using connectors
app.use('/graphql', bodyParser.json(), graphqlExpress({
schema: executableSchema,
context: {}, // at least(!) an empty object
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}));
const graphQLServer = createServer(app);
graphQLServer.listen(GRAPHQL_PORT, () => console.log(`GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}/graphql`));
reports an error Cannot GET /
I've read that maybe the createServer function is deprecated, but I'm not sure how to fix it.

When you use express, you have to be explicitly define the routes used by your application. For example, if you define a route with app.get('/hello', handler) then any GET requests to localhost/hello will get routed to that handler. It can then execute whatever logic and return a response, be that a JSON object, a webpage, etc.
Express will only handle the routes you've defined in this way. So if you've only defined a route for requests that GET /hello, it will not know how to GET /foo, or GET your root path /. If you wanted to implement a way to POST or PUT to /hello, that would need to be a different route as well.
You can use app.use in a similar way to implement middleware in your application. While middleware typically takes your request, manipulates it and pass it on, it can also be used to break up your routing logic.
In the case of GraphQL, requests are typically made using the POST method, but the specification does allow for both POST and GET requests. To do this, we would have to define handlers for both app.get('/graphql') and app.post('/graphql'). The graphqlExpress middleware you're importing and using conveniently does that for you.
So with your set up, you've created some routes that allow you to POST to and GET from localhost:8000/graphql. You've also enabled GraphiQL on localhost:8000/graphiql. If you don't see any errors in the console when you start your server, you should be able to navigate to the GraphiQL page at localhost:8000/graphiql and play around with your schema.
But these are the only routes you've set up on your server. If you attempt to navigate anywhere else, like the root at localhost:8000/, express won't know how to handle that request and you will see the error you reported.

Use app.listen(port, function(){console.log('server started')}); instead of createServer(app).

Related

Jest and supertest: Test keep exceeding timeout

Hello I am a bit confused by this error I have encountered.
I am working on an Universal React App using Webpack 5 and Express.
I want to implement Jest support by using the React-testing-Library for the frontend (which work) and supertest for the backend (this is where I am blocked).
I am following this basic tutorial recommended by the jest doc himself in order to use jest on an node express environment.
But everytime I get this error:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
Here are my code:
server.js
import app from './app.js';
import { mongooseConnection, disconnectMongoDB } from "./routers/services/url/urlDB.js"; // we call mongooseConnect var to connect only once into the mongoDB database
const PORT = process.env.PORT || 8080;
// the server listen on the port set by node on localhost.
app.listen(PORT, () => {
console.log(
`Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
);
});
// when when we shut down the app we execute a callback function before closing the server
process.on('exit', function() {
disconnectMongoDB();
});
app.js
import express from 'express';
import path from 'path';
import cors from 'cors';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import { enableHMR } from './reload/hotReload.js';
let app = express();
// if we have set the environnent on production then:
if (envIsProduction()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( PRODUCTION ) |");
console.log("|_______________________________________|");
console.log(" ");
app.use(express.static(path.join(__dirname,'../client'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( DEVELOPMENT ) |");
console.log("|_______________________________________|");
console.log(" ");
enableHMR(app); // we enable the Hot MPodule Reload on the frontend and the backend
}
app.use(cors());
app.use(express.urlencoded({extended:false}));
app.use(express.json());
//Hot reload!
//ALL server routes are in this module!
app.use((req, res, next) => {
require("./routers/routers")(req, res, next);
});
export default app;
routers.js
import renderPage from "./renderpage/renderPage.js";
import { serverRoutes, reactRouterRoutes, getReactRouterRoutesString } from "./routes.js";
import express from "express";
import routerLoginDB from "./request/routerLoginDB.js";
import routerSignupDB from "./request/routerSignupDB.js";
const router = express.Router();
// Put all your server routes in here
// When the user connect to the root of the server we send the page
router.get(serverRoutes.root, renderPage);
// When the user send a get request by the /click route a console.log and a respone is send.
router.get(serverRoutes.click, (req, res)=>{
res.status(200).send("Click");
});
// when this user want to login into his account, we ask for the routerLoginDB to handle it
router.post(serverRoutes.login,routerLoginDB);
// when this user want to signup into his account, we ask for the routerSignupDB to handle it
router.post(serverRoutes.signup, routerSignupDB);
// For all the routes that only react-router need to use, if we refresh on a nested route of the react-router from the client side then we redirect it to the root route "/"
router.get(reactRouterRoutes,(req,res) => {
res.redirect("/");
});
router.get("*", (req,res) =>{
res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;
module.exports = router;
app.test.js
import request from '../utils/test-node-utils.js'
describe("Test the /click path", () => {
test("It should response the GET method", () => {
return request
.get("/click")
.expect(200);
});
});
and finally test-node-utils.js
import supertest from "supertest";
import app from "../serverside/app.js";
const request = supertest(app);
export default request;
Don't believe what the error say because I think it is more deep than that.
I have tried to increased the jest timeout value but it keep being stuck and reach the timeout limit.
I have done exactly like the tutorial say without using my project structure and it worked but when I try to implement the tutorial in my backend structure, it don't work with supertest.
I think it is related to my files or backend structure that make it don't work with the test.
Thanks in advance for your help
I've recently debugged a similar issue where my Jest tests would run successfully (or not) in my dev. environment but when I would try and package the app as a Docker image all my tests would time out.
It turned out that by commenting out the line which setup CORS, which for me I only turned on in production builds (should have been a clue), the tests started to run again when building the image.
...
const NODE_ENV = process.env.NODE_ENV;
const app = express();
NODE_ENV.toUpperCase() === 'PRODUCTION' && app.use(cors);
...
I mentioned this as I can see from your snippet above that you are also using the cors middleware and that, in your case, it's set all the time.
Perhaps not your issue, and you may want CORS in your tests for some reason, but try commenting it out and see if your tests run.

How to use Express middleware after Apollo-Server v2 middleware?

I'm having this weird requirement(because I got nothing on the internet on how to use it. So, I guess it's only me) while using Express and Apollo Server.
I want to use an Express middleware after using the Apollo Server middleware but I can't.
Example:
const { ApolloServer } = require('apollo-server-express');
const express = require('express')
const app = express();
const server = new ApolloServer({typedefs, resolvers})
app.use(...); // Middleware-1
app.use(...); // Middleware-2
app.use(server.getMiddleware()); // Apollo server middleware
app.use(...); // Middleware-3
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
In the above code, the Middleware-3 is never being called. I've searched a lot about that but I got nothing.
Is there any way to invoke Middleware-3 after Apollo Server Middleware?
Thank you.
Edit:1
I forgot to mention that I don't want to modify the response from the ApolloServer. I already have some Express middlewares which I don't want to refactor/modify/write completely new ones to be able to use along with Apollo. So, is there any hacky way to follow the app.use() order even after ApolloServer middleware?
You can try to assign
res.end2 = res.end
res.end = ()=>{}
in any middleware called before ApolloMidleware and then call res.end2 to the send response
Apollo Server calls res.end after sending the execution results of your GraphQL request. This finalizes the response, so no other middleware will be called afterward.
If you need to format the response, you can utilize the formatResponse or formatErrors options, but you cannot use Express middleware.

Order of middleware parsing with body-parser

Alright so I am trying to understand Node.js and Typescript, so I tried working on a simple script which is as follows;
app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Routes } from "./routes/crm_routes";
class App {
public app;
public routePrv : Routes = new Routes();
constructor() {
this.app = express();
this.routePrv.routes(this.app);
this.config();
}
private config():void {
this.app.use(bodyParser.json);
this.app.use(bodyParser.urlencoded({ extended: false }));
}
}
export default new App().app;
./lib/routes/crm_routes.ts
import {Request, Response} from "express";
export class Routes {
public routes(app): void {
app.route('/').get((req, res) => res.json({name : "ohmygodnotthisagain"}));
}
}
server.ts
import app from "./app";
app.listen(3000, () => console.log('Example app listening on port 3000!'));
Now I was playing around so I put this.config() above this.routePrv.routes(this.app), and my server stopped routing to / altogether.
When I put them back in the above order it started working again.
Now I tried to understand the cause of this, and am confused, is it because body-parser needs to be the last middleware called such that auth, extra checks, etc middleware complete working or are there something else?
Any and all help will be appreciated. Thanks!
PS: I'm pretty new to TS, pointers would be great.
Body parser (or the middleware in general) should be called before the actual route.
Your route is not working because you have a spelling error here:
this.app.use(bodyParser.json);
Should be:
this.app.use(bodyParser.json());
The route works when you put that code last, because its never actually executed (the route gets matched first and stops the execution, since you are not calling the next() function)

why isn't node.js (Express) serving this route? and how do I fix it?

I'm writing an application and I'm trying to find out why this post route for login is not registering.
If anyone has a better model for routes they can share or a solution to the problem please respond
import express from 'express';
const router = express.Router();
router.route('/login').post((req, res) => {
console.log('login');
let user = Users.getUser(req.body.email);
if (!user) {
console.log(`unable to find user: ${user}`);
res.status(401).send('Unable to find username');
return;
}
bcrypt.compare(req.body.pass, user.password, (err, goodComparison) => {
if (err) {
console.log(err);
res.status(401);
} else if (goodComparison) {
// Good password, generate key, send response
let key = Sessions.generateSession(user, token => {
let responseData = {
id: user.id,
username: user.username,
email: user.email,
admin: user.admin,
token: token
};
res.status(200).json(responseData);
});
// Invalid password
} else {
res.status(401);
}
});
});
export { router };
console.log('login'); is never being called due to 404, the route is not being served.
import express from 'express';
const router = express.Router();
import { router as loginRouter } from './routes/login.route';
/**
* TODO:
* Possibly automate routes, or json import
*/
export default {
/** Initiate routers */
registerRoutes: function() {
console.log('registering routes');
console.log(loginRouter);
router.use('/login', loginRouter); /* HERE */
}
};
in my app.js (yes it's being properly imported)
// Register routes
// Register routes
Routes.registerRoutes();
my app is 404ing the post for /login, but if I put the routing code directly in app.js it works
If this is an issue with the way I'm exporting the route or handling the routing or anything, I've done a lot of searching online for a solution and haven't been able to find it. Suggestions and solutions are very much appreciated!
The router created in your second code block with const router = express.Router(); is never hooked up to anything. It needs to be hooked to an express instance that is attached to an http server.
Just calling router.use(...) by itself does not cause anything to to anything. That router has to be hooked to an Express instance which is itself hooked to an http server that has been started for incoming http requests to get sent to the router.
In addition, it appears you're also trying to create a route for /login/login rather than just /login. When you do this:
router.use('/login', loginRouter);
That says that whatever requests are send to router, filter out the request so that only requests that start with /login are sent to loginRouter. Then, inside of loginRouter, you have this:
router.route('/login').post(...)
which says that for requests that came to router (which is actually loginRouter from the previous code), look for an additional path of /login on whatever was already matched. That means you would be looking for /login/login which is probably not what you're posting to.
FYI, your code is a lot harder to follow when you use different symbolic names for the same router in different files. If you're going to use loginRouter in one place, then for that specific router, use that same name in all your files. Your code will be a lot less confusing and a lot less likely for someone to make a mistake. You call it loginRouter in one place and router in another. And, you then have other routers that you also call router. Very confusing.

Graphql server + NodeJS, store data in cache with Redis

I'd like to know how I can improve the response time from connecting graphql to api. I have decided to use Redis. I don't know how exactly should I do it.
I have built my graphql server:
import express from 'express';
import {
graphqlExpress,
graphiqlExpress,
} from 'graphql-server-express';
import bodyParser from 'body-parser';
import { schema } from './src/schema';
import cors from 'cors';
import redis from 'redis';
const PORT = 4000;
const server = express();
const client = redis.createClient();
client.on('error', function (err) {
console.log('error' + err)
});
server.use('*', cors({ origin: 'http://localhost:3000' }));
server.use('/graphql', bodyParser.json(), graphqlExpress({
schema, context: { client }
}));
server.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql'
}));
server.listen(PORT, () =>
console.log(`GraphQL Server is now running on http://localhost:${PORT}`)
);
I have already imported redis here. My graphql server is connected to front page by cors, so I can render strings from api. In resolver I'm connecting to api by Node.js (if necessary I'll add it.), and I have some custom resolvers here. The response time from api (also affect rendering on page) is too slow- around 10~15s.
I recommend checking out Apollo Engine to get a better insight into how GraphQL is working, and make it easier to use add-ons such as a Redis cache. Once you get Apollo Engine working, you can use apollo-server-redis-cache to create a cache.
If your requests are taking 10-15s without a cache, I would also look to optimise them first before throwing Redis in front of them.

Resources