Right way to have Authentication and PubSub in GraphQLServer - node.js

I'm trying to use subscriptions on my GraphQL server. The problem I'm facing is that I can't have both a middleware to extract my JWT and a PubSub when initializing the GraphQLServer.
This is what I have:
const server = new GraphQLServer({
schema,
context: ({request}) => extractJWT(request),
});
And it works just fine with this:
#Mutation(returns => User)
public async findUser(
#Ctx() context: IContext,
#PubSub() pubsub: PubSubEngine,
) {
const user = await User.findById(context.tokenData.userId)
pubsub.publish('user', { user })
return user
}
But if I register the PubSub on the GraphQLServer initialization context and change my code to the one below, I can't any longer access what I have in my #Ctx decorators.
const pubSub = new PubSub();
const server = new GraphQLServer({
schema,
context: {
ctx: ({request}) => extractJWT(request),
pubSub
},
});
What is the proper way to initialize my GraphQLServer using both JWT extraction middleware and the PubSub?

Related

How to add gates to Objection in NodeJS (AWS Lambda)?

I have an API developed with Serverless and serverless-offline which is deployed to AWS Lambda. Objection/Knex is used for DB, Firebase for authentication, and Middy to manage the requests.
For the logged-in users, I'm trying to:
Automatically add the company ID on inserting to DB
Exit if a user tries to update or delete an entry that belongs to another company
As of now, this is manually checked. I'm trying to figure out if this can be built into the Model so that it can be eliminated from the main code.
The company ID is available in the event and it's possible to exit the process from the static beforeInsert method. Is it possible to pass data from the event to the model?
Handler:
const baseHandler = async (event) => {
const companyId = event?.requestContext?.authorizer?.claims?.companyId;
await event.models.Client.query().insert({
name: event?.body?.name || "",
companyId // This is the current method. I need to eliminate this
});
await event.models.Client.query().where('id', 'uuid').where('companyId', companyId).update({
name: event?.body?.name || "",
}); // Need to remove the check for companyId from here, and add it to the Model
};
const endpoint = middy(baseHandler)
.use(jsonBodyParser())
.use(validator({ eventSchema }))
.use(httpErrorHandler())
.use(useModels());
Model:
module.exports = class Client extends Model {
async $beforeInsert(queryContext) {
this.companyId = '' // Set the company ID from the logged in user
}
static beforeInsert(args) {
if (!belongsToUser) { // This is what I was trying
args.cancelQuery();
}
}
static get tableName() {
return "clients";
}
};

How to override url for RTK query

I'm writing pact integration tests which require to perform actual call to specific mock server during running tests.
I found that I cannot find a way to change RTK query baseUrl after initialisation of api.
it('works with rtk', async () => {
// ... setup pact expectations
const reducer = {
[rtkApi.reducerPath]: rtkApi.reducer,
};
// proxy call to configureStore()
const { store } = setupStoreAndPersistor({
enableLog: true,
rootReducer: reducer,
isProduction: false,
});
// eslint-disable-next-line #typescript-eslint/no-explicit-any
const dispatch = store.dispatch as any;
dispatch(rtkApi.endpoints.GetModules.initiate();
// sleep for 1 second
await new Promise((resolve) => setTimeout(resolve, 1000));
const data = store.getState().api;
expect(data.queries['GetModules(undefined)']).toEqual({modules: []});
});
Base api
import { createApi } from '#reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from '#rtk-query/graphql-request-base-query';
import { GraphQLClient } from 'graphql-request';
export const client = new GraphQLClient('http://localhost:12355/graphql');
export const api = createApi({
baseQuery: graphqlRequestBaseQuery({ client }),
endpoints: () => ({}),
});
query is very basic
query GetModules {
modules {
name
}
}
I tried digging into customizing baseQuery but were not able to get it working.

How to add query params to context of apollo-server-express

I'm using apollo-server-express as so right now:
import { ApolloServer } from 'apollo-server-express';
import { WebApp } from 'meteor/webapp';
import { getUser } from 'meteor/apollo';
import schema from './api';
const server = new ApolloServer({
schema,
context: async ({ req }) => {
const user = await getUser(req.headers.authorization);
return {
user,
};
},
playground: process.env.NODE_ENV === 'development',
introspection: true,
uploads: false,
});
server.applyMiddleware({
app: WebApp.connectHandlers,
path: '/graphql',
});
I'd like to add another field to the context which has all the query params from the URL my user is visiting.
It seems that the req object passed to the context function by apollo-server-express is of type express.Request which ought to have a req.query object. However, when I try to access that like so:
context: async ({ req }) => {
console.log('### query', req.query);
console.log('### params', req.params);
const user = await getUser(req.headers.authorization);
return {
user,
};
},
and visit my app at http://localhost:3000/u/3q2PcjRwyiqR2ywHM/BK2CiG7fN3P7Z5xvy?editToken=qdj3RRYjCuMxFNRnz (note the ?editToken=...)
I see the following log lines:
I20220417-15:08:54.477(-5)? ### query {}
I20220417-15:08:54.478(-5)? ### params undefined
I20220417-15:08:54.570(-5)? ### query {}
I20220417-15:08:54.571(-5)? ### params undefine
What is the correct way to access URL query params when creating the context for apollo-server-express? I'm specifically trying to add the editToken to the context.

GraphQL Subscriptions using Express-GraphQL

Can anyone tell me how to implement GraphQL Subscriptions using Express-GraphQL in Node?
I have also run into the same problem. I wasn't able to find a clear solution to this in the documentation. So i have just switched to graphql-yoga instead. But i did find this thread so do check it out
I've been researching this same issue.
I've read the GitHub issues for express-graphql subscriptions and a member of that repo suggested using graphql-ws on the closing comment.
Here's a link to my GitHub project shammelburg/express-graphql-api, you can npm start load grapiql to test queries and mutation.
To test subscriptions, I've created an Angular project which implements graphql-ws's observables example. shammelburg/graphql-rxjs-angular
The Angular project also uses graphql-request for queries and mutations.
This is a very lightweight solution and works perfectly.
They've added the doc fragment mentioning Subscription Support with an example implementation in Nov 2020.
But unfortunately that never got released, there's an issue here mentioning that.
My workaround for now's been switching over to Express Playground for the subscriptions-transport-ws socket (Playground doesn't support graphql-ws yet) and Apollo Sandbox for the graphql-ws.
Then my subscription creation options are the following.
Where createScopedPermissionWrapper is just an execute wrapper with #graphql-authz and createGraphqlContext a factory function validating auth and creating a custom context for my resolvers.
import { Server } from 'http'
import { useServer } from 'graphql-ws/lib/use/ws' // subscription with graphql-ws
import { SubscriptionServer } from 'subscriptions-transport-ws' // subscription with subscriptions-transport-ws
export const createSubscriptionsTransportWs = (server: Server) => {
const wsServer = new SubscriptionServer(
{
schema,
execute: createScopedPermissionWrapper(),
subscribe,
onConnect: (args: { authentication?: string }) =>
createGraphqlContext({
authentication: args.authentication,
}),
},
{ server, path }
)
const wsAddress = wsServer.server.address() as AddressInfo
depClients.logger.success(
`Graphql subscription socket up on ${wsAddress.address}:${wsAddress.port}${path}`
)
}
export const createGraphqlWS = (server: Server) => {
const wsServer = new ws.Server({ server, path })
useServer(
{
schema,
execute: createScopedPermissionWrapper(),
subscribe,
context: (args: { connectionParams: { authentication?: string } }) =>
createGraphqlContext({
authentication: args.connectionParams.authentication,
}),
},
wsServer
)
const wsAddress = wsServer.address() as AddressInfo
depClients.logger.success(
`Graphql subscription socket up on ${wsAddress.address}:${wsAddress.port}${path}`
)
}
See Authentication and Express Middleware
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
ip: String
}
`);
const loggingMiddleware = (req, res, next) => {
console.log('ip:', req.ip);
next();
}
var root = {
ip: function (args, request) {
return request.ip;
}
};
var app = express();
app.use(loggingMiddleware);
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

How to retrieve the PR number in nodejs github probot listening on `pull_request` event

I have created a GitHub probot app using nodejs and typescript. I am listening on pull_request event. How do I retrieve pr_number from the probot context object?
following is the code in intex.ts
export = (app: Application) => {
app.on('pull_request', async (context) => {
})
}
The field that you're interested in is context.payload inside the callback:
export = (app: Application) => {
app.on('pull_request', async (context) => {
const payload = context.payload
// ...
})
}
This matches the payloads listed in the GitHub Webhook Events page: https://developer.github.com/webhooks/#events
You're interested in the pull_request payload which can be found here: https://developer.github.com/v3/activity/events/types/#pullrequestevent
And pull_request.number is your relevant piece of information you need:
export = (app: Application) => {
app.on('pull_request', async (context) => {
const payload = context.payload
const number = payload.pull_request.number
})
}

Resources