Nextjs with Expressjs POST route not found - express

I have recently integrated Expressjs with my Nextjs project and was trying to run as POST request to an API.
But I'm getting an error message
Cannot POST /api/auth/signin/`
I do not receive the same when I make a GET request. Based on the way I integrated ExpressJS from a tutorial, I'm curious if the error message for POST requests stems from not attaching the .post() method to the express server in my server side server file. Does this sound like the source of the issue?
ssr-server.js:
const express = require('express')()
const next = require('next')
const bodyParser = require('body-parser')
// Session Management
const session = require('express-session')
const redis = require('redis');
const redisClient = redis.createClient({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || '6379',
url: process.env.REDIS_URL || ''
});
const redisStore = require('connect-redis')(session);
redisClient.on_connect("error", function(error){
console.error(error);
})
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
console.log("Test")
app.prepare()
.then(() => {
const server = express()
// Session Management
server.use(session({
store: new redisStore({client: redis.RedisClient}),
secret: process.env.SESSION_SECRET,
saveUninitialized: false,
resave: false,
cookie: {
secure: process.env.SESSION_SECURE || false,
httpOnly: true,
}
}))
// Form body parsing
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({extended: true}))
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
/api/auth/signin.js:
export default (req, res) => {
if (req.method === 'POST') {
if (res.statusCode = 200){
return res.json({ body: req.body, message: 'Success' })
} else {
return res.json({ message: 'Error on POST'})
}
} else {
return res.json({ message: 'Error' })
}
}

Related

getting Can't find / on this server while deploying my app on localhost

when I tried to connect my application with API I'm getting error in my localhost saying
"status": "fail",
"message": "Can't find / on this server",
"error": { statusCode: 404, status: "fail", isOperational: true },
"stack": "Error: Can't find / on this server\n at C:\\Users/*/*app.js:66",
here is my app.js
const express = require("express");
const morgan = require("morgan");
const rateLimit = require("express-rate-limit");
const helmet = require("helmet");
const mongoSanitize = require("express-mongo-sanitize");
const xss = require("xss-clean");
const hpp = require('hpp');
const AppError = require("./API/Utils/appError");
const globalErrorHsndler = require("./API/controllers/errorController");
const usersRouter = require("./API/routes/usersRoute");
const app = express();
app.use(express.json({ limit: "10kb" }));
// DATA SANITIZATION against NoSQL query injection
app.use(mongoSanitize());
// DATA SANITIZATION against site script XSS
app.use(xss());
// PREVENT PARAMETER POPULATION
app.use(
hpp({
whitelist: [
"duration",
"difficulty",
"price",
"maxGroupSize",
"ratingsAverage",
"ratingsQuantity",
],
})
);
// SECURE HEADER HTTP
app.use(helmet());
//RATE LIMIT
const apiLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: "Too many requests, please try again later"
});
// apply to specific routes
app.use("/api", apiLimiter);
app.use(morgan("dev"));
//CUSTOM MIDDLE WARE
app.use((req, res, next) => {
console.log("Hey i am from middleware function 👋");
next();
});
app.use((req, res, next) => {
req.requestTime = new Date().toISOString();
next();
});
app.use("/api/v1/users", usersRouter);
//ERROR SECTION
app.all("*", (req, res, next) => {
console.log(`Received request for url: ${req.originalUrl}`);
const error = new AppError(`Can't find ${req.originalUrl} on this server`, 404);
console.log(`Data inside next AppError: ${error}`);
next(error);
});
//GLOBAL ERROR HANDLEING
app.use(globalErrorHsndler);
module.exports = app;
here is my userRouter.js
const express = require("express");
const userControllers = require("./../controllers/userControllers");
const authController = require("./../controllers/authController");
const router = express.Router();
router.post("/signup", authController.signup);
router.post("/login", authController.login);
router.post("/forgotPassword", authController.forgotPassword);
router.patch("/resetPassword/:token", authController.resetPassword);
router.patch("/updateMyPassword", authController.protect, authController.updatePassword);
router.patch("/updateMe", authController.protect, userControllers.updateMe);
router.delete("/deleteMe", authController.protect, userControllers.deleteMe);
//ROUTERS USERS
router
.route("/")
.get(userControllers.getAllUsers)
.post(userControllers.createUser);
router
.route("/:id")
.get(userControllers.getSingleUser)
.patch(userControllers.updateUser)
.delete(userControllers.deleteUser);
module.exports = router;
and here is server.js
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const app = require("./app");
const next = require("next");
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== "production";
const server = next({ dev });
const handle = server.getRequestHandler();
process.on("uncaughtException", err=>{
console.log("uncaughtException Shutting down Application");
console.log(err.name, err.message);
process.exit(1);
});
dotenv.config({ path: "./config.env" });
const DB = process.env.DATABASE.replace(
"<PASSWORD>",
process.env.DATABASE_PASSWORD
);
mongoose
.connect(DB, {
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
})
.then((con) => {
console.log("DB Connection Successfully");
})
server.prepare().then(() => {
app.get("*", (req, res) => {
return handle(req, res);
});
app.listen(port, () => {
console.log(`App running on port ${port}....`);
});
});
process.on("unhandledRejection", (err) => {
console.log("unhandledRejection Shutting down Application");
console.log(err.name, err.message);
server.close(() => {
process.exit(1);
});
});
I need to ask from experts as I'm new to this

How to setup authentication with graphql, and passport but still use Playground

After adding authentication to our backend Graphql server the "Schema" and "Docs" are no longer visible in the Graphql Playground. Executing queries when adding a token to the "HTTP HEADERS" in the Playground does work correctly when authenticated and not when a user isn't authenticated, so that's ok.
We disabled the built-in Playground from Apollo-server and used the middleware graphql-playground-middleware-express to be able to use a different URL and bypass authentication. We can now browse to the Playground and use it but we can't read the "Schema" or "Docs" there.
Trying to enable introspection didn't fix this. Would it be better to call passport.authenticate() in the Context of apollo-server? There's also a tool called passport-graphql but it works with local strategy and might not solve the problem. I've also tried setting the token in the header before calling the Playground route, but that didn't work.
We're a bit lost at this. Thank you for any insights you could give us.
The relevant code:
// index/ts
import passport from 'passport'
import expressPlayground from 'graphql-playground-middleware-express'
const app = express()
app.use(cors({ origin: true }))
app.get('/playground', expressPlayground({ endpoint: '/graphql' }))
app.use(passport.initialize())
passport.use(bearerStrategy)
app.use(
passport.authenticate('oauth-bearer', { session: false }),
(req, _res, next) => { next() }
)
;(async () => {
await createConnections()
const server = await new ApolloServer({
schema: await getSchema(),
context: ({ req }) => ({ getUser: () => req.user, }),
introspection: false,
playground: false,
})
server.applyMiddleware({ app, cors: false })
app.listen({ port: ENVIRONMENT.port }, () => { console.log(`Server ready`) })
})()
// passport.ts
import { IBearerStrategyOptionWithRequest, BearerStrategy, ITokenPayload } from passport-azure-ad'
import { Account } from '#it-portal/entity/Account'
export const bearerStrategy = new BearerStrategy( config,
async (token: ITokenPayload, done: CallableFunction) => {
try {
if (!token.oid) throw 'token oid missing'
const knownAccount = await Account.findOne({ accountIdentifier: token.oid })
if (knownAccount) return done(null, knownAccount, token)
const account = new Account()
account.accountIdentifier = token.oid
account.name = token.name
account.userName = (token as any).preferred_username
const newAccount = await account.save()
return done(null, newAccount, token)
} catch (error) {
console.error(`Failed adding the user to the request object: ${error}`)
}
}
)
I figured it out thanks to this SO answer. The key was not to use passport as middleware on Express but rather use it in the Graphql Context.
In the example code below you can see the Promise getUser, which does the passport authentication, being used in the Context of ApolloServer. This way the Playground can still be reached and the "Schema" end "Docs" are still accessible when run in dev mode.
This is also the preferred way according to the Apollo docs section "Putting user info on the context".
// apollo.ts
passport.use(bearerStrategy)
const getUser = (req: Express.Request, res: Express.Response) =>
new Promise((resolve, reject) => {
passport.authenticate('oauth-bearer', { session: false }, (err, user) => {
if (err) reject(err)
resolve(user)
})(req, res)
})
const playgroundEnabled = ENVIRONMENT.mode !== 'production'
export const getApolloServer = async () => {
return new ApolloServer({
schema,
context: async ({ req, res }) => {
const user = await getUser(req, res)
if (!user) throw new AuthenticationError('No user logged in')
console.log('User found', user)
return { user }
},
introspection: playgroundEnabled,
playground: playgroundEnabled,
})
}
The best thing is that you only need two functions for this to work: passport.use(BearerStrategy) and passport.authenticate(). This is because sessions are not used so we don't need to add it as Express middleware.
// index/ts
const app = express()
app.use(cors({ origin: true }))
;(async () => {
await createConnections()
const server = await getApolloServer()
server.applyMiddleware({ app, cors: false })
app.listen({ port: ENVIRONMENT.port }, () => { console.log(`Server ready`) })
})()
I hope this helps others with the same issues.

timeout async callback testing with sinon jest supertest to simulate error 500 on express api

I am testing an api with all http 500 errors.
Here I try to use sinon.stub to test on a failing server and get a 500 error, but I get a timeOut async callback, or if I use my app a successfull 200 response statusCode as if sinon.stub has no effect. I must miss something and I am stucked...
would you see a horrifying error below ?
thanks for your precious help
process.env.NODE_ENV = "test";
const app = require("../../app");
const request = require("supertest");
const sinon = require("sinon");
// /************************** */
const usersRoute = require("../../routes/Users");
const express = require("express");
const initUsers = () => {
const app = express();
app.use(usersRoute);
return app;
};
describe("all 5xx errors tested with stub", function () {
it("should return a 500 when an error is encountered", async (done) => {
let secondApp;
sinon.stub(usersRoute, "post").throws(
new Error({
response: { status: 500, data: { message: "failed" } },
})
);
secondApp = initUsers(); //==========> Timeout Async Callback
//secondApp = require("../../app"); //==============> gives a 200 instead of 500
const fiveHundredError = await request(secondApp)
.post("/users/oauth?grant_type=client_credentials")
.send({
username: "digitalAccount",
password: "clientSecret",
});
expect(fiveHundredError.statusCode).toBe(500);
//sinon.restore();
done();
});
});
app is using express.Router to get users route :
const express = require("express");
const router = express.Router();
const axios = require("axios");
router.post("/users/oauth", async (req, res) => {
//if (all missing parts)
//else {
try {
if (req.fields) {
const response = await axios.post(
`${base_url}oauth/token?grant_type=${req.query.grant_type}`,
{},
{
auth: {
username: req.fields.username,
password: req.fields.password,
},
}
);
res.json(response.data);
}
} catch (error) {
return res.status(error.response.status).json(error.response.data);
}
}
});
module.exports = router;
See server.js :
const app = require("./app");
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`server starting on port ${port}!`));
and app.js :
// private environment:
require("dotenv").config();
const express = require("express");
const formidableMiddleware = require("express-formidable");
const cors = require("cors");
const app = express();
app.use(formidableMiddleware());
app.use(cors());
const usersRoute = require("./routes/Users");
app.use(usersRoute);
app.get("/", (req, res) => {
res.status(200).send("Welcome to Spark Admin back end!");
});
app.all("*", (req, res) => {
return res.status(404).json({ error: "Web url not found" });
});
module.exports = app;
I finally opted for 'nock' and deleted 'sinon'
const nock = require("nock");
const axios = require("axios");
describe("POST login: all 5xx errors tested with nock", function () {
it("should return a 500 when an error is encountered", async (done) => {
const scope = nock("http://localhost:5000")
.post(
"/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
)
.reply(500, {
response: {
statusCode: 500,
body: { error: "AN ERROR OCCURED" },
},
});
try {
await axios.post(
"http://localhost:5000/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
);
} catch (e) {
expect(e.response.status).toBe(500);
}
done();
});
});

Apollo subscriptions JWT authentication

I am using Robin Wieruch's fullstack boilerplate but it is missing authentication for subscriptions. It uses JWT token for sessions and it is working fine for http but for ws auth is completely missing.
I need to pass user trough context for subscriptions as well, I need session info in subscriptions resolver to be able to decide weather I should fire subscription or not.
I did search Apollo docs, I saw I should use onConnect: (connectionParams, webSocket, context) function, but there is no fully functional fullstack example, I am not sure how to pass JWT from client to be able to get it in webSocket object.
Here is what I have so far:
Server:
import express from 'express';
import {
ApolloServer,
AuthenticationError,
} from 'apollo-server-express';
const app = express();
app.use(cors());
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, process.env.SECRET);
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
const server = new ApolloServer({
introspection: true,
typeDefs: schema,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket, context) => {
console.log(webSocket);
},
},
context: async ({ req, connection }) => {
// subscriptions
if (connection) {
return {
// how to pass me here as well?
models,
};
}
// mutations and queries
if (req) {
const me = await getMe(req);
return {
models,
me,
secret: process.env.SECRET,
};
}
},
});
server.applyMiddleware({ app, path: '/graphql' });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
const isTest = !!process.env.TEST_DATABASE_URL;
const isProduction = process.env.NODE_ENV === 'production';
const port = process.env.PORT || 8000;
httpServer.listen({ port }, () => {
console.log(`Apollo Server on http://localhost:${port}/graphql`);
});
Client:
const httpLink = createUploadLink({
uri: 'http://localhost:8000/graphql',
fetch: customFetch,
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:8000/graphql`,
options: {
reconnect: true,
},
});
const terminatingLink = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return (
kind === 'OperationDefinition' && operation === 'subscription'
);
},
wsLink,
httpLink,
);
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => {
const token = localStorage.getItem('token');
if (token) {
headers = { ...headers, 'x-token': token };
}
return { headers };
});
return forward(operation);
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.log('GraphQL error', message);
if (message === 'UNAUTHENTICATED') {
signOut(client);
}
});
}
if (networkError) {
console.log('Network error', networkError);
if (networkError.statusCode === 401) {
signOut(client);
}
}
});
const link = ApolloLink.from([authLink, errorLink, terminatingLink]);
const cache = new InMemoryCache();
const client = new ApolloClient({
link,
cache,
resolvers,
typeDefs,
});
You need to use connectionParams to set the JWT from the client-side. Below is the code snippet using the angular framework:
const WS_URI = `wss://${environment.HOST}:${environment.PORT}${
environment.WS_PATH
}`;
const wsClient = subscriptionService.getWSClient(WS_URI, {
lazy: true,
// When connectionParams is a function, it gets evaluated before each connection.
connectionParams: () => {
return {
token: `Bearer ${authService.getJwt()}`
};
},
reconnect: true,
reconnectionAttempts: 5,
connectionCallback: (error: Error[]) => {
if (error) {
console.log(error);
}
console.log('connectionCallback');
},
inactivityTimeout: 1000
});
const wsLink = new WebSocketLink(wsClient);
In your server-side, you are correct, using onConnect event handler to handle the JWT. E.g.
const server = new ApolloServer({
typeDefs,
resolvers,
context: contextFunction,
introspection: true,
subscriptions: {
onConnect: (
connectionParams: IWebSocketConnectionParams,
webSocket: WebSocket,
connectionContext: ConnectionContext,
) => {
console.log('websocket connect');
console.log('connectionParams: ', connectionParams);
if (connectionParams.token) {
const token: string = validateToken(connectionParams.token);
const userConnector = new UserConnector<IMemoryDB>(memoryDB);
let user: IUser | undefined;
try {
const userType: UserType = UserType[token];
user = userConnector.findUserByUserType(userType);
} catch (error) {
throw error;
}
const context: ISubscriptionContext = {
// pubsub: postgresPubSub,
pubsub,
subscribeUser: user,
userConnector,
locationConnector: new LocationConnector<IMemoryDB>(memoryDB),
};
return context;
}
throw new Error('Missing auth token!');
},
onDisconnect: (webSocket: WebSocket, connectionContext: ConnectionContext) => {
console.log('websocket disconnect');
},
},
});
server-side: https://github.com/mrdulin/apollo-graphql-tutorial/blob/master/src/subscriptions/server.ts#L72
client-side: https://github.com/mrdulin/angular-apollo-starter/blob/master/src/app/graphql/graphql.module.ts#L38

nuxt axios.post request with empty body

I'm trying to get nuxt with axios working.
I do the following post request (store/index.js):
login({ commit }, { username, password }) {
const data = JSON.stringify({
password
, username
})
console.log('store login ', username, password)
this.$axios.post('http://localhost:3000/api/login', data)
.then(res => {
commit('SET_USER', res.data)
})
.catch(error => {
console.log(error.response)
})
}
My backend looks like this (api/auth.js):
const express = require('express')
const session = require('express-session')
const body_parser = require('body-parser')
const app = express()
const router = express.Router()
app.use(body_parser.json())
app.use(body_parser.urlencoded({ extended: true }))
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}))
router.post('/login', (req, res) => {
console.log('try to login ', req.body)
if (!req.body)
return res.status(401).json({ message: 'No body' })
if (req.body.username === 'demo' && req.body.password === 'demo') {
req.session.auth_user = { username: 'demo' }
console.log('logged in')
return res.json({ username: 'demo' })
}
res.status(401).json({ message: 'Bad credentials' })
})
My Problem is, that the req.body is always undefined. But in the error message I can see, that the params are sent. (config.data)
Does anybody has an idea what I'm missing? I use body-parser and checked everything 100 times. I guess it's something total obvious, but I can't see it.
My nuxt.config.js:
module.exports = {
...
modules: [
'#nuxtjs/axios'
, '#nuxtjs/auth'
],
router: {
middleware: ['auth']
},
serverMiddleware: ['#api/auth']
...
}
Probably you take this error also. This is caused by cors policy.
Access to XMLHttpRequest at 'http://localhost:3000/.......' from
origin 'http://127.0.0.1:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
same problem occured on my project.
in server index.js change line
const host = process.env.HOST || 'localhost'
and nuxt.config.js
axios: {
baseURL: 'http://localhost:YOUR_PORT'
},
You must define bodyParser in router.
router.use(express.json())