Apollo server context initialization function parameter? - node.js

In Apollo server docs, Apollo server constructor run with some configurations, one of them is a context initialization function that is called with every request, and according to the docs, this function parameter is an object that get the request (req) automatically as one field of that object.
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
authScope: getScope(req.headers.authorization)
}),
});
This is ok for me, but in one project i got from Github that creates a what's app clone, the parameter object is different which i couldn't relate, something i searched for and i couldn't find anything can relate
export const server = new ApolloServer({
schema: rootModule.schema,
context: (session: any) => {
if (session.connection) {
const req = session.connection.context.session.request;
const cookies = req.headers.cookie;
if (cookies) {
req.cookies = cookie.parse(cookies);
}
}
return rootModule.context(session);
},
});
The context function parameter is a session object, and the developer need to get the request from that session object using that verbose code:
const req = session.connection.context.session.request;
I searched about that session parameter but couldn't find anything in the docs or anywhere else
I am confused about this inconsistency, or am i missing something?

In general, the actual context object can change dependending on the apollo server configuration (see: https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server#middleware-specific-context-fields)
My guess in the "WhatsApp-clone", the parameter comes from the subscription support of Apollo Server (here as in the github repo from Version 2): https://www.apollographql.com/docs/apollo-server/v2/data/subscriptions/#operation-context
For whatever reason the argument in the example project is called session it is propably that object described in the documentation.
The subscription support is added here to the example server: https://github.com/Urigo/WhatsApp-Clone-Server/blob/master/index.ts#L13

Related

NextJS middleware cookies issue

I wanted to check if user has a valid token inside the cookie before accessing /api routes on my NextJS app so I created a middleware which would check that.
Somehow I am not able to extract the value from req.cookies in my middleware... I followed NextJS official docs (https://nextjs.org/docs/advanced-features/middleware). First of all TypeScript is already throwing error: Error [TypeError]: request.cookies.getAll is not a function and also Property 'value' does not exist on type 'string'. Did you mean 'valueOf'?ts(2551)
export function middleware(request: NextRequest) {
const cookie = request.cookies.get("token")?.value;
console.log(cookie);
const allCookies = request.cookies.getAll();
console.log(allCookies);
const response = NextResponse.next();
return response;
}
// See "Matching Paths" below to learn more
export const config = {
matcher: "/api/:path*",
};
You cannot read in middleware the cookie value. You can read the cookie value only server side e.g. getserversideprop

How to check the content of Koa's ctx.query?

For a Koa-based API server, I want to check what parameters the URL query contains.
My setup looks as simple as this:
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
router = new Router()
router.get('/', ctx => {
console.log(ctx.query)
})
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
It seems that ctx.query has a structure like an object but doesn't work as one.
Methods like ctx.query.hasOwnProperty() or ctx.query.toString() result in an error saying that it is not a function.
Though, Object.keys(ctx.query) gives an array of the keys—which is confusing to me because it apparently is an object and should have above methods.
What is ctx.query exactly? How can I make the failing methods from above working?
ctx.query is the return from Node.js' querystring.parse() method. From the documentation:
The object returned by the querystring.parse() method does not
prototypically inherit from the JavaScript Object. This means that
typical Object methods such as obj.toString(), obj.hasOwnProperty(),
and others are not defined and will not work.
You can check Koa's request implementation.

How to remove authentication for introspection query in Graphql

so may be this is very basic question so please bear with me. Let me explain what I am doing and what I really need.
EXPLANATION
I have created a graphql server by using ApolloGraphql (apollo-server-express npm module).
Here is the code snippet to give you an idea.
api.js
import express from 'express'
import rootSchema from './root-schema'
.... // some extra code
app = express.router()
app.use(jwtaAuthenticator) // --> this code authenticates Authorization header
.... // some more middleware's added
const graphQLServer = new ApolloServer({
schema: rootSchema, // --> this is root schema object
context: context => context,
introspection: true,
})
graphQLServer.applyMiddleware({ app, path: '/graphql' })
server.js
import http from 'http'
import express from 'express'
import apiRouter from './api' // --> the above file
const app = express()
app.use([some middlewares])
app.use('/', apiRouter)
....
....
export async function init () {
try {
const httpServer = http.createServer(app)
httpServer
.listen(PORT)
.on('error', (err) => { setTimeout(() => process.exit(1), 5000) })
} catch (err) {
setTimeout(() => process.exit(1), 5000)
}
console.log('Server started --- ', PORT)
}
export default app
index.js
require('babel-core')
require('babel-polyfill')
require = require('esm')(module/* , options */)
const server = require('./server.js') // --> the above file
server.init()
PROBLEM STATEMENT
I am using node index.js to start the app. So, the app is expecting Authorization header (JWT token) to be present all the times, even for the introspection query. But this is not what I want, I want that introspection query will be resolvable even without the token. So that anyone can see the documentation.
Please shed some light and please guide what is the best approach to do so. Happy coding :)
.startsWith('query Introspection') is insecure because any query can be named Introspection.
The better approach is to check the whole query.
First import graphql and prepare introspection query string:
const { parse, print, getIntrospectionQuery } = require('graphql');
// format introspection query same way as apollo tooling do
const introspectionQuery = print(parse(getIntrospectionQuery()));
Then in Apollo Server configuration check query:
context: ({ req }) => {
// allow introspection query
if (req.body.query === introspectionQuery) {
return {};
}
// continue
}
There's a ton of different ways to handle authorization in GraphQL, as illustrated in the docs:
Adding middleware for express (or some other framework like hapi or koa)
Checking for authorization inside individual resolvers
Checking for authorization inside your data models
Utilizing custom directives
Adding express middleware is great for preventing unauthorized access to your entire schema. If you want to allow unauthenticated access to some fields but not others, it's generally recommended you move your authorization logic from the framework layer to the GraphQL or data model layer using one of the methods above.
So finally I found the solution and here is what I did.
Let me first tell you that there were 2 middle-wares added on base path. Like this:
app //--> this is express.Router()
.use(jwtMw) // ---> these are middlewares
.use(otherMw)
The jwtMw is the one that checks the authentication of the user, and since even introspection query comes under this MW, it used to authenticate that as well. So, after some research I found this solution:
jwtMw.js
function addJWTMeta (req, res, next) {
// we can check for null OR undefined and all, then check for query Introspection, with better condition like with ignore case
if (req.body.query.trim().startsWith('query Introspection')) {
req.isIntrospection = true
return next()
}
...
...
// ---> extra code to do authentication of the USER based on the Authorization header
}
export default addJWTMeta
otherMw.js
function otherMw (req, res, next) {
if (req.isIntrospection) return next()
...
...
// ---> extra code to do some other context creation
}
export default otherMw
So here in jwtMw.js we are checking that if the query is Introspection just add a variable in req object and move forward, and in next middleware after the jwtMw.js whosoever wants to check for introspection query just check for that variable (isIntrospection, in this case) and if it is present and is true, please move on. We can add this code and scale to every middleware that if req.isIntrospection is there just carry on or do the actual processing otherwise.
Happy coding :)

handling Postbacks From other website to My website

Im using SuperRewards (SR) To make Coin transaction for users in my website,maybe you are familiar with SR.
Whenever a transaction happens SuperRewards sends a postback (Post Request) to my server containing information about the transaction and coins etc...
So my question is how to handle Postbacks or Post request ( i really dont know the diffrence ) from other website to my server USing Nodejs Express ?
Picture 1 App testing
picture 2 app Testing
Code
You would handle it like any other request coming to your Express app. Since it's a GET request, you would have a GET route defined.
According to section Fields and Formats, you get back quite a few query strings. Knowing that we can do the following:
app.get('/super-rewards', async (req, res) => {
// `new` is a reserved keyword, so we can't use `new` as a variable name.
const newCurrency = req.query['new']
const {
id,
uid,
oid,
total,
sig
} = req.query
})
Additionally, the documentation states that sig should match the MD5 hash of your secret key, if I'm understanding that correctly. So a full example would be something like:
const crypto = require('crypto')
const express = require('express')
const app = express()
app.get('/super-rewards', async (req, res) => {
// `new` is a reserved keyword, so we can't use `new` as a variable name.
const newCurrency = req.query['new']
const {
id,
uid,
oid,
total,
sig
} = req.query
const secretHash = crypto.createHash('md5').update(process.env.SECRET_KEY).digest('hex')
if (secretHash !== sig) {
throw new Error('Invalid transaction')
}
})
Aside, it is a GET request because the documentation clearly states that:
Postbacks are sent from our server to yours using a HTTP GET request (...)
I contacted the support team and they told me i must use a public domain not Local host that s why it wasnt working so the problem is solved and thank you for your time :)

Is it possible to execute a mutation or query locally at server side with Apollo Server

I would like to know if it is possible to execute a mutation or query locally at server side with Apollo Server.
Example:
1- Endpoint GET /createNew is reached
2- I run a mutation I defined like:
apolloserver.mutation(mutation_query).then((response)=>{res.status(200)})
3- A new entry is added to the database and a JSON string is returned to client
Does something like that exist? Is a apolloserver object available at runtime?
I'm using NodeJS + Express
I got it working
First we start the objects:
import express from 'express';
import { graphql } from 'graphql';
const context = require('./context'); //this is the object to be passed to each resolver function
const schema = require('./schema'); //this is the object returned by makeExecutableSchema({typeDefs, resolvers})
const app = express();
Then, if we want, for example, to run a query on endpoint GET /execute
app.get('/execute', function(req, res){
var query = `
query myQueryTitle{
queryName{
_id,
...
}
}
`;
graphql(schema, query, null, context)
.then((result) => {
res.json(result.data.queryName);
});
})
So anytime we want to run a query or a mutation, we call graphql passing schema, the requestString/query and the context object
And this may coexist with an instance of graphqlExpress

Resources