I have an express server serving react pages. I am trying to show the work to a client via a ngrok tunnel. But whenever I try to log in at the ngrok URL for my server it fails to connect back to my localhost where the express routes for auth are.
I feel like it's a simple error I'm making. The browser is trying to make a request to the express server at localhost:5000, but there is nothing at localhost on my client's network.
This is my app.js
const createError = require('http-errors');
const express = require('express');
const debug = require('debug')('app');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
require('dotenv').config()
const ensureToken = require('./middleware/ensureToken');
const path = require('path');
const app = express();
const { init } = require('./utils/cron/createCron');
const cors = require('cors')
// Routers
const authRouter = require('./routes/auth')
const botRouter = require('./routes/bot')
const proxyRouter = require('./routes/proxy');
const winstonLogger = require('./utils/log/logger');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
const whiteList = ['http://localhost:3000/', 'http://localhost:3000']
const corsOptions = {
origin: (origin, callback)=>{
if (whiteList.indexOf(origin) !== -1){
callback(null, true)
}else{
callback(new Error('Not Allowed by CORS'))
}
optionsSuccessStatus:200
}
}
// app.use(cors(corsOptions))
app.use(cors({ credentials: true, origin: true }))
app.use(express.static(path.join(__dirname, 'build')));
// Base Routes
app.use('/api/auth', authRouter);
app.use(ensureToken) //Add this before routes that need to be protected ny valid token
app.use('/api/bot', botRouter);
app.use('/api/proxy', proxyRouter);
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(process.env.PORT, ()=>{
winstonLogger.info(`Listening on ${process.env.PORT}`)
debug(`Listening on ${process.env.PORT}`);
console.log(`Listening on ${process.env.PORT}`);
init()
})
module.exports = app;
Any help is appreciated!!
I'm quite new to this whole backend business and I'm wondering if anyone can see where I'm going wrong. I've got an express server in my Nuxt app which is serving out an API. When I run the localhost:3000/api/salesforce/:id route - my vscode terminal generates a response - but it doesn't show up on the browser. Which in turn makes it inaccessible to Nuxt.
In my nuxt.config.js:
serverMiddleware: {
'/api': '~/api'
},
/api/index.js:
const express = require('express')
// Create express instance
const app = express()
// Require API routes
const users = require('./routes/users')
const test = require('./routes/test')
const salesforce = require('./routes/salesforce')
// Import API Routes
app.use(users)
app.use(test)
app.use(salesforce)
// Export express app
module.exports = app
// Start standalone server if directly running
if (require.main === module) {
const port = process.env.PORT || 3001
app.listen(port, () => {
// eslint-disable-next-line no-console
console.log(`API server listening on port ${port}`)
})
}
Then in /api/routes/salesforce.js:
const e = require('express')
const { Router } = require('express')
const router = Router()
const jsforce = require('jsforce');
// require the .env file for login
require('dotenv').config();
const { SF_USERNAME, SF_PASSWORD, SF_TOKEN, SF_LOGIN_URL } = process.env;
if (!(SF_USERNAME && SF_PASSWORD && SF_TOKEN && SF_LOGIN_URL)) {
console.error(
'Cannot start app: missing mandatory configuration. Check your .env file.'
);
process.exit(-1);
}
const conn = new jsforce.Connection({
loginUrl: SF_LOGIN_URL
});
conn.login(SF_USERNAME, SF_PASSWORD + SF_TOKEN, err => {
if (err) {
console.error(err);
process.exit(-1);
}
});
// get OPPORTUNITY specific
router.get('/salesforce/:id', function (req, res, next) {
const id = req.params.id
console.log(req.params.id)
const sfData = conn.query(`SELECT Id, Name, StageName FROM Opportunity WHERE Name = '` + id + `'`, (err, res)=>{
if(err){
console.log(err)
return "error";
} else {
console.log(res.records[0])
let sanitisedData = res.records[0]
return sanitisedData;
}
})
res.json(sfData.json)
})
If anyone can tell me where I'm going wrong that would be greatly appreciated, I'm kind of stuck here.
I am trying to start my nestJs server and It keeps giving me this error:
UnhandledPromiseRejectionWarning: Error: You must await server.start() before calling server.applyMiddleware()
at ApolloServer
I'm not even sure where to debug from as I am still very new at NestJs and GraphQL.
This is a known bug with an open issue and a merged PR to fix it. For now, you can downgrade to apollo-server-express#^2
A complete working code is:
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const http = require("http");
const app = express();
const typeDefs = `
type Query{
totalPosts: Int!
}
`;
const resolvers = {
Query: {
totalPosts: () => 100,
},
};
let apolloServer = null;
async function startServer() {
apolloServer = new ApolloServer({
typeDefs,
resolvers,
});
await apolloServer.start();
apolloServer.applyMiddleware({ app });
}
startServer();
const httpserver = http.createServer(app);
app.get("/rest", function (req, res) {
res.json({ data: "api working" });
});
app.listen(4000, function () {
console.log(`server running on port 4000`);
console.log(`gql path is ${apolloServer.graphqlPath}`);
});
I faced this issue when upgrading Ben Awad's Graphql-Next-Typeorm[...] stack, simply adding an await to server start fixed the warnings
const apolloServer = new ApolloServer({
introspection: true,
schema: await buildSchema({
resolvers: [__dirname + '/resolvers/**/*.js'],
validate: false
}),
context: ({ req, res }) => ({
req,
res,
redis: redisClient
}),
formatError
});
// added this line
await apolloServer.start();
apolloServer.applyMiddleware({
app,
cors: false
});
For Apollo Server Express 3.0 and above, you need to define an async function that takes in typeDefs and resolvers parameters, then assign the server to the same Apollo initialization as before as shown here
async function startApolloServer(typeDefs, resolvers){
const server = new ApolloServer({typeDefs, resolvers})
const app = express();
await server.start();
server.applyMiddleware({app, path: '/graphql'});
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}${server.graphqlPath}`);
})
}
startApolloServer(typeDefs, resolvers);
downgrading is not the option (at least anymore)
here is the solution =>
https://javascriptsu.wordpress.com/2021/08/02/apollo-error-must-await-server-start/
const server = new ApolloServer({ typeDefs, resolvers });
const app = express();
server.start().then(res => {
server.applyMiddleware({ app });
app.listen({ port: 3000 }, () =>
console.log("nice")
)
})
You can put everything in an async function and execute the function in your server(app,index...).js. You may also check the npm package.
https://www.npmjs.com/package/apollo-server-express
For example:
const express = require('express')
, http = require('http')
, path = require('path');
const { ApolloServer } = require('apollo-server-express');
async function startExpressApolloServer() {
const { typeDefs } = require('./graphql/schemas/schema');
const { resolvers } = require('./graphql/resolvers/resolver');
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
const app = express();
server.applyMiddleware({ app, path: '/api/graphql' });
await new Promise(resolve => app.listen({ port: 3001 }, resolve));
console.log(`Server ready at http://localhost:3001${server.graphqlPath}`);
return { server, app };
}
startExpressApolloServer();
I had the same type of problem. I was using TypeScript, Express, ApolloServer. What I did-
async function a(){
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
server.applyMiddleware({ app, path: '/graphql' });
}
a();
This is not a bug. As per the documentation, the Apollo server needs to be instantiated in an async function. This is the recommended setup for Apollo Express:
import { ApolloServer } from 'apollo-server-express';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
import express from 'express';
import http from 'http';
async function startApolloServer(typeDefs, resolvers) {
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
server.applyMiddleware({ app });
await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
}
There are already some great answers here. But we should know why and where we should call server.start(). From apollo docs -
Always call await server.start() before calling
server.applyMiddleware and starting your HTTP server. This allows
you to react to Apollo Server startup failures by crashing your
process instead of starting to serve traffic.
One other option is to downgrade your apollo to any 2.x.x. It solved my problem
This is my working server:
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import typeDefs from './schema';
const app = express();
const server = new ApolloServer({
typeDefs,
mocks: true
});
server.start().then(() => {
server.applyMiddleware({
app,
cors: true,
});
});
const PORT = 4000;
app.listen(PORT, () => {
console.log(
`GraphQL endpoint and playground accessible at http://localhost:${PORT}${server.graphqlPath}`,
);
});
The key thing here is to wrap the "applyMiddleware" function call inside the "server.start" async function.
In v3, if you use apollo-server-express the start function is required https://www.apollographql.com/docs/apollo-server/api/apollo-server/#start.
You can do something like this.
const app = express()
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
....
export const startup = async () => {
await server.start()
server.applyMiddleware({ app, path: `/api/${configs.region}/graphql` })
return app
}
// call startup in another file to get app
It is not ok to start the apollo server in advance. What happens with the case when I have to explicitly use http/https. Please see the following case:
const server = new ApolloServer({
typeDefs: [KeycloakTypeDefs, typeDefs], // 1. Add the Keycloak Type Defs
schemaDirectives: KeycloakSchemaDirectives, // 2. Add the
formatError: new ApolloErrorConverter(),
resolvers: resolvers,
context: ({ req }) => {
return makeContextWithDependencies(req);
}
});
server.applyMiddleware({ app });
http.createServer(app).listen(config.server.port, os.hostname());
const options = {
key: fs.readFileSync(config.server.ssl.keyFile, "utf8"),
cert: fs.readFileSync(config.server.ssl.certFile, "utf8"),
passphrase: config.server.ssl.passphrase
};
https
.createServer(options, app)
.listen(config.server.securePort, os.hostname());
console.log(
"Server waiting for requests on ports: " +
config.server.port +
"," +
config.server.securePort
);
We must wait for the server to get ready before adding middleware to it.
const app = express();
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [HelloResolver],
validate: false,
}),
});
await apolloServer.start(); // First start the server then apply middleware on it
apolloServer.applyMiddleware({ app });
you can do like that, it works for me.
const server = new ApolloServer({ schema });
const startApollo = async () => {
try {
await server.start();
server.applyMiddleware({ app, path: "/api"})
} catch (error) {
console.log(error);
}
}
I'm prototyping a NextJS implementation with the following server code:
import express from 'express';
import next from 'next';
import path from 'path';
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
dotenv.config();
app.prepare().then(() => {
const server = express();
// Custom build resources aliases
// ---------------------------------------------------------------------
server.use('/favicon.ico', express.static(path.join(__dirname, 'static', 'images', 'icons', 'favicon.ico')));
// ---------------------------------------------------------------------
// Custom/dynamic routes
// ---------------------------------------------------------------------
server.get('/p/:id', (req, res) => {
const actualPage = '/post';
const queryParams = { title: req.params.id };
app.render(req, res, actualPage, queryParams);
});
// ---------------------------------------------------------------------
// Default route
// ---------------------------------------------------------------------
server.get('*', (req, res) => handle(req, res));
// ---------------------------------------------------------------------
// Express: Listener
server.listen(process.env.WEB_PORT, () => {
console.log(`Server listening on port: ${process.env.WEB_PORT}...`);
});
}).catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
As apparent, my static assets sit in a /static/ folder. Of these, I have a favicon file at /static/images/icons/favicon.ico. I am able to visit this file using https://schandillia.com/static/images/icons/favicon.ico. However, when I try hitting https://schandillia.com/favicon, it throws a 404. Am I not using express.static() correctly?
I'm trying to use the Express Router with Next.js using their custom-express-server example as my boilerplate. The only difference is that I'm trying to define the routes externally on routes/router.js as follows:
Code in server.js:
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const routes = require('./routes/router')
app.prepare()
.then(() => {
const server = express()
server.use('/', routes)
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
module.exports = app;
Code in routes/router.js:
const express = require('express'),
app = require('../server.js'),
router = express.Router();
router.get('/a', (req, res) => {
return app.render(req, res, '/b', req.query)
})
router.get('/b', (req, res) => {
return app.render(req, res, '/a', req.query)
})
router.get('/posts/:id', (req, res) => {
return app.render(req, res, '/posts', { id: req.params.id })
})
module.exports = router;
At this point, even when I'm importing "app" from server.js, app is not available within router.js.
Is my logic incorrect?
If it's not, then why is app not available within router.js?
Just solved it. This issue is known as a circular dependency, and it should be avoided at all costs... unless the pattern you're using (like the boilerplate I used, I guess...) requires it.
To solve it, just export from file "A" the dependency that file "B" uses before you require file "B" on file "A".
...And that's it pretty much.
You might also try using next-routes, which I use on all of my Next project:
// server.js
const { createServer } = require('http');
const next = require('next');
const routes = require('./routes');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handler = routes.getRequestHandler(app);
app.prepare().then(() => {
createServer(handler).listen(port, err => {
if (err) {
throw err;
}
console.log(`> Ready on http://localhost:${port}`);
});
});
Then you can configure your routes in the routes.js file without accessing the app:
// routes.js
const nextRoutes = require('next-routes');
const routes = (module.exports = nextRoutes());
routes
.add('landing', '/')
.add('blog', '/blog', 'blog')
.add('blog-post', '/blog/:postId', 'blog')