Getting user agent access in the resolver in graphql - javascript

I am relativity new to graphQL but this is annoying me. I want to get the user agent from the request body being sent from the client side. I can get access to the user-agent in the middleware however when I call the next function with any parameter to send to the resolver, I don't get any data from it. If I don't pass any parameters into next() then the resolver works as expected however parent, args, User and Session do not contain any information about the request headers. Any help or general tips would be greatly appreciated! Thanks!
app.js
import express from 'express';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import { graphiqlExpress, graphqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
import typeDefs from './Graphql/typeDefs';
import resolvers from './Graphql/resolver';
import { User } from './Mongoose/Schemas/user';
import { Session } from './Mongoose/Schemas/session';
mongoose.connect('mongodb://localhost/test');
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const helperMiddleware = [
bodyParser.json(),
bodyParser.text({ type: 'application/graphql' }),
(req, res, next) => {
if ( req.body ) {
console.log(req.headers['user-agent']);
}
next();
},
];
const PORT = 3009;
const app = express();
app.use('/graphql', ...helperMiddleware, graphqlExpress({ schema, context: { User, Session } }));
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
app.listen(PORT);
console.log(`Running On Port ${PORT}`);
resolver.js
Mutation: {
createUser: async (parent, args, { User, Session }) => {
const user = await new User(args).save();
user._id = user._id.toString();
const session = await new Session({
user_id: user._id,
userAgent: 'Nothing ATM',
ip: 'Nothing ATM',
}).save();
return user;
},

You need to use the callback version of creating the GraphQL server middleware, otherwise you have no way of constructing context based on the current request:
https://www.apollographql.com/docs/apollo-server/setup.html#options-function

Related

request to login to a node server, using react

Here is the situation:
I have a database which contains a user and password registered.
My assignment, for now, is to create a login form, and login with a registered uname and pw.
Uname and pw are registered in the server/database already.
ps: I did not create the server nor database.
Node server code
import express from 'express';
import cors from 'cors';
import http from 'http';
import { Sequelize } from 'sequelize';
import { Data } from './database';
import { router } from './routes/Router';
import { initialData } from './database/someData';
const closeServer = async (
server: http.Server,
sequelize: Sequelize,
signal?: string
) => {
server.close();
await sequelize.close();
process.exit();
};
const runServer = async (): Promise<void> => {
const PORT = process.env.PORT || 8082;
const app = express();
const sequelize = Data.init();
app.use(
cors({
credentials: true,
origin: 'http://localhost:3000',
})
);
app.use('/api', router);
const server = app.listen(PORT, () => {
console.log(`Starting server at ${PORT}`);
});
try {
await sequelize.authenticate();
await sequelize.sync({
force: process.env.SERVER === 'reset',
});
if (process.env.SERVER === 'reset') await initialData();
} catch (e) {
closeServer(server, sequelize);
throw e;
}
};
runServer()
.then(() => {
console.log('Run successfully');
})
.catch((ex: Error) => {
console.log('Unable to run:', ex);
});
I need help on what is that I have to do.
When I input username and pw, on the form, what are the methods to use for sending the info?
And then, when the info reaches the server, i think the username and pw need to be validated with jwt, and then check if the user and pw exists. how do i do that?
What i have understood so far is that i gotta use axios to send info to server, but thats it.
Do i need to use jwt for the login?
What is the normal flow for this kind of mechanism?
I am using react as a framework.
So there are quite few steps here.
First you have to create endpoint on your backend server for issuing jwt tokens. Jwt tokens can be used as a pass for user to login. So in your router you would add something like this:
router.post('/login', (req, res)=> {
const username = req.body.username
const password = req.body.password
// Then you make db call to verify username and password are correct.
if Credentials are valid, you would issue jwt token
jwt.sign({
// here you can save extra information of user. Also remember this information must be public since anyone can see it. Do not put user password here
email: 'email',
userId: 'id',
}, "secret")
})
After this, you need some kind of middleware on backend, so that on each user request, you check and verify this jwt token which is sent from react application. For example you could write isAuth middleware:
const jwt =require("jsonwebtoken");
export const isAuth= (req, res, next) => {
try {
// here we attach request in auth header, with Bearer "jwt token" format. So we extract jwt token and verify it
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: "no token" });
}
const token = authHeader.split(" ")[1];
let decodedToken;
decodedToken = jwt.verify(token, "secret");
if (!decodedToken) {
return res.status(401).json({ message: "Wrong token" });
}
req.userId = decodedToken.userId;
next();
} catch (err) {
console.error(err);
return res.status(401).json({ message: err });
}
};
Now you would be able to have backend endpoints like this:
// This is how you would require login on some routes
router.post("/getMyPrivateInfo", isAuth, QueryPrivatInfo)
Now on React side, you would make request for login like this:
axios.post("/login", {
username: '1',
password: "2"
})
This would return jwt token, now you would save this token in local storage.
After its saved in local storage and you make request with axios for private info you would do following
axios.post("/getMyPrivateInfo", {any request body info neeeded}, {
headers: {
Authorization: "Bearer jwtTokenFromLocalStorage"
}
})
This is how whole flow will work, hope it makes sense

Error On Express Router Setup: Router.use() requires middleware function but got a undefined

I will just say first of all that I am aware of almost all the questions asked on this site under this title.
The solutions there were pretty obvious and already done by me (with no success) or only helped for those specific cases and didn’t really work in my case unfortunately.
Now, for the problem:
I'm trying to create a route that will handle a get request and a post request which are sent to the route 'ellipses'.
These requests should receive and send data from and to an SQL database.
The problem is that for some reason the router is not ready to get these functions and gives me the error in the title:
Router.use () requires middleware function but got an undefined
Here is my code:
This code is from the file dat.js. its porpose is just to access the SQL database.
import { Sequelize } from "sequelize";
export const sequelize = new Sequelize('TheDataBaseName', 'TheUser', 'ThePassword', {
host: 'localhost',
dialect: 'mssql'
});
This code is from the file: controller.js. its porpose is to manage the requests and load the data.
import { sequelize } from "../dat";
export const sendEllipses = async (req, res, next) => {
try {
const ellipses = await getEllipsesFromJson();
return res.send(ellipses);
} catch (e) {
console.log(e);
}
};
export const addNewEllipse = async (req, res, next) => {
const { body: obj } = req;
let newEllipse;
try {
if (Object.keys(obj) !== null) {
logger.info(obj);
newEllipse = await sequelize.query(
`INSERT INTO [armageddon].[dbo].[ellipses] (${Object.keys(
obj
).toString()})
values (${Object.values(obj).toString()})`
);
} else {
console.log("the values are null or are empty");
}
return res.send(newEllipse);
} catch (error) {
console.log(error);
}
};
This code is on the file: routers.js.
its porpose is to define the route
import Router from "express";
import { sendEllipses } from "../ellipses.controller";
import { addNewEllipse } from "../ellipses.controller";
const router = Router();
export default router.route("/ellipses").get(sendEllipses).post(addNewEllipse);
This code is from the file: app.js. This is where everything actually happens.
import { router } from "../routers";
import express from "express";
app.use('/api', router);
app.listen(5000, () => {
console.log("server is runing on port 5000")
});
You need to export the router
const router = Router();
router.route("/ellipses").get(sendEllipses).post(addNewEllipse)
export default router
Now import the router:
import routes from "../router.js";
app.use('/api', routes);
Its also mentioned in the docs: https://expressjs.com/de/guide/routing.html#express-router

Context for GraphQl resolvers undefined

I'm new to graphQL but for whatever reason is seems like my context parameter is undefined in my resolver. Here is how I am setting up the server:
index.js
import express from 'express';
import path from 'path';
import { fileLoader, mergeTypes, mergeResolvers } from 'merge-graphql-schemas';
import { ApolloServer } from 'apollo-server-express';
import models from './models';
const typeDefs = mergeTypes(fileLoader(path.join(__dirname, './schema')));
const resolvers = mergeResolvers(fileLoader(path.join(__dirname, './resolvers')));
const PORT = 4000;
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.applyMiddleware({ app });
models.sequelize.sync({force: true}).then(x => {
app.listen({ port: PORT }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
});
Example Resolver:
export default {
Query: {
getUser: (parent, { id }, { models }) => models.User.findOne({ where: { id } }),
allUsers: (parent, args, { models }) => models.User.findAll(),
},
Mutation: {
createUser: (parent, args, { models }) => models.User.create(args),
},
};
When I try to do the createUser mutation I get the error: Cannot read property 'User' of undefined.
Which means my context object parameter is undefined. Any help is appreciated, GraphQL setup has been a bit confusing for me.

Register route 404 not found

I am building a register page and when I submit the I am getting a 404 not found. I am using react and express and I think the front end is good. I am missing some back end or have the wrong url for my post request: axios.post('http://localhost:3000/auth'. Would appreciate some advice if anyone can see what I have wrong.
const handleSubmit = event => {
event.preventDefault();
const user = {
username: username,
password: password,
}
axios.post('http://localhost:5000/', { user })
.then(res=>{
console.log(res);
console.log(res.data);
})
}
This is the registerPost function for my route.
export const registerPost = async (req, res) => {
const {username, password} = req.body;
const newUser = new User({username, password});
try {
await newUser.save();
console.log(newUser);
res.status(201).json(newUser);
} catch (error) {
res.status(409).json({ message: error.message });
}
}
These are the routes.
import express from 'express';
import { getPosts } from '../controllers/posts.js'
import { createPost, updatePost, deletePost, registerPost } from '../controllers/posts.js'
const router = express.Router();
router.get('/', getPosts);
router.post('/', createPost);
router.patch('/:id', updatePost);
router.delete('/:id', deletePost);
router.post('/auth', registerPost);
export default router;
You're getting a 404 error, which means the url you're trying to reach does not exist. In the React app, you're sending the request at http://localhost:5000/, but in the Express app, you've defined the register route as /auth.
Updating the url in the React app to http://localhost:5000/auth should fix this.
As far as I can see you're passing the following Object from the Frontend:
{ user: { username, password } }
but in the backend you're not looking for the user property, but directly for the username and password.
const {username, password} = req.body
// =>
const { user } = req.body

ApolloServer 2.0 context and public/private parts of the GraphQL API

I'm not a pro in any way but I've started and ApolloServer/Express backend to host a site where I will have public parts and private parts for members. I am generating at JWT token in the login mutation and get's it delivered to the client.
With context I want to check if the token is set or not and based on this handle what GraphQL queries are allowed. My Express/Apollo server looks like this at the moment.
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
// get the user token from the headers
const token = (await req.headers.authorization) || '';
if (token) {
member = await getMember(token);
}
}
});
The problem is that this locks down the GraphQL API from any queries and I want/need to reach signup/login mutations for example.
Could anyone spread some light on this to help me understand what I need to do to get this to work.
the way i am doing it is that i will construct auth middleware even before graphql server as sometimes is needed to have information about authenticated user also in other middlewares not just GraphQL schema. Will add some codes, that you need to get it done
const auth = (req, res, next) => {
if (typeof req.headers.authorization !== 'string') {
return next();
}
const header = req.headers.authorization;
const token = header.replace('Bearer ', '');
try {
const jwtData = jwt.verify(token, JWT_SECRET);
if (jwtData && jwtData.user) {
req.user = jwtData.user;
} else {
console.log('Token was not authorized');
}
} catch (err) {
console.log('Invalid token');
}
return next();
};
This way i am injecting the user into each request if the right token is set. Then in apollo server 2 you can do it as follows.
const initGraphQLserver = () => {
const graphQLConfig = {
context: ({ req, res }) => ({
user: req.user,
}),
rootValue: {},
schema,
};
const apolloServer = new ApolloServer(graphQLConfig);
return apolloServer;
};
This function will initiate ApolloServer and you will apply this middleware in the right place. We need to have auth middleware before applyin apollo server 2
app.use(auth);
initGraphQLserver().applyMiddleware({ app });
assuming the app is
const app = express();
Now you will have user from user jwtData injected into context for each resolver as "user", or in req.user in other middlewares and you can use it for example like this. This is me query for saying which user is authenticated or not
me: {
type: User,
resolve: async (source, args, ctx) => {
const id = get(ctx, 'user.id');
if (!id) return null;
const oneUser = await getOneUser({}, { id, isActive: true });
return oneUser;
},
},
I hope that everything make sense even with fractionized code. Feel free to ask any more questions. There is definitely more complex auth, but this basic example is usually enough for simple app.
Best David

Categories

Resources