How do you get the graphql context in a gqlgen subgraph that has been passed down from the federated graphql gateway?
You would need to add a buildService function in your gateway that json stringifies the context in the gateway and then injects that into a header that will be sent to the subgraph and then in the subgraph you will need to parse that header and put that into the context yourself so your resolves can access it. Something like this:
{
gateway: {
buildService({ url }) {
return new RemoteGraphQLDataSource({
url,
willSendRequest({ request, context }) {
request.http.headers.set('X-GQL-Context', JSON.stringify(context))
},
})
},
},
}
Related
I have a yoga graphQL server set up in between API Gateway and Lambda
so when making a post call to my-api-gateway.aws.com/graphql, this makes a query and each query is mapped to lambda function
I learned that context is where I can access headers but I'm specifically looking for x-amzn-requestid value which is an unique request id sent from api gateway, but it doesn't seem to exist in context.request.headers or context.req.rawHeaders I could only access x-amzn-trace-id
const resolvers = {
Query: {
hello: async (_, args, context) => {
return {
context: {
request: context.request.headers.get('x-amzn-requestid'),
req2: context.request.headers.get('X-Amzn-Trace-Id'),
req3: context.req.rawHeaders,
req4: Object.keys(context.req),
}}}}
does anyone know where x-amzn-requestid is located inside context argument?
I'm trying to implement Auth0 in Apollo Federation, I was able to implement it in its individual services (https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-authorization/#Set-Up-API-Authorization) but if I'm trying to access the APIs thru the gateway, the header/payload is not being passed down to the services, hence its always unauthorized.
if the API is accessed thru individual services, the payload is being received and properly decoded from the header and works fine but if thru the gateway, its not being cascaded to the services that needs it.
Currently using a code-first implementation for it. I've also tried mirroring the module used in the services but it still doesn't work.
sample payload in individual service
{
iss: 'issuer url here',
sub: 'google-oauth2',
aud: ['audience link'],
iat: ,
exp: ,
azp: '',
scope: '',
permissions: [ 'sample:permission' ]
}
imports in the gateway
imports: [
ConfigModule.forRoot(),
GraphQLGatewayModule.forRoot({
server: {
cors: true,
},
gateway: {
serviceHealthCheck: true,
serviceList: [
{
name: 'service',
url: `${process.env.SERVICE_URL}/graphql`,
},
],
},
}),
]
You can customize header that's being used in internal request by using buildService option:
server.ts
const gateway = new ApolloGateway({
buildService: ({ url }) => new RequestHandler({ url }),
serviceList,
})
where RequestHandler class extends RemoteGraphQLDataSource:
import { RemoteGraphQLDataSource } from '#apollo/gateway'
import type { GraphQLRequest } from 'apollo-server-core'
import type express from 'express'
export class RequestHandler extends RemoteGraphQLDataSource {
willSendRequest({ context, request }: { context: { req: express.Request }, request: GraphQLRequest }) {
request.http?.headers.set('somethingFromOriginalRequestOrSomethingCustom', context.req.headers['something'])
}
}
Good morning,
I have an Angular WebApp that uses GraphQL codegen with (apollo-angular plugin and all the typescript plugins). Everything works fine but I want to handle Hasura Roles and Hasura User ID. From Hasura Console everything is configured correctly and working.
Only thing I am missing is how to handle this on the front end. I need to add X-Hasura-Role and X-Hasura-User-Id headers to every request sent to Hasura.
Is there a way to do this with graphql-codegen?
What is the right way to do this?
I know I can add the headers section on the codegen.yml, but obviously the role and userid are dynamic so I cannot hardcode anything there.
Should I use maybe a customFetch component? This component, thought, should only intercept every request sent to Hasura and add the headers needed. I have no idea how to do this so I hope you can help me (I also hope there is a better solution)
Best regards
When you create your Apollo client instance in the Angular application you can set it up to pass along the Authorization header which should contain the user's id and their roles.
There are examples of this in the Angular Apollo docs. Eg:
import { NgModule } from '#angular/core';
import { HttpClientModule } from '#angular/common/http';
import { Apollo, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { InMemoryCache,ApolloLink } from '#apollo/client/core';
import { setContext } from '#apollo/client/link/context';
const uri = '/graphql';
export function createApollo(httpLink: HttpLink) {
const basic = setContext((operation, context) => ({
headers: {
Accept: 'charset=utf-8'
}
}));
const auth = setContext((operation, context) => {
const token = localStorage.getItem('token');
if (token === null) {
return {};
} else {
return {
headers: {
Authorization: `JWT ${token}`
}
};
}
});
const link = ApolloLink.from([basic, auth, httpLink.create({ uri })]);
const cache = new InMemoryCache();
return {
link,
cache
}
}
#NgModule({
exports: [
HttpClientModule,
],
providers: [{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink]
}]
})
export class GraphQLModule {}
It is up to you to ensure that the JWT token that will be passed along with your request is available in the front end. Ultimately you're going to have to implement some kind of authentication approach to allow the user to sign in and pass the token to your front end application.
More information is available in the Hasura Docs for Authentication
There are also a number of tutorials and guides for integrating with different third party auth providers
Apollo is not storing the header from the query dynamically.
pages/index.vue
methods: {
fetchCars() {
const token = Cookies.get('XSRF-TOKEN')
console.log(token) // 🟢 Token is shown in console
this.$apollo.query({
query: gql`
query {
cars {
uuid
name
}
}
`,
headers: {
'X-XSRF-TOKEN': token, // â• Fetch without header
},
})
},
},
Is there a way to set the header value new for every Apollo request?
I have a separate Frontend and Backend. For the Frontend I am using Nuxt.js with Apollo. I want to have a session based communication with my server. For this reason I need to send the CSRF-Token with every Request.
Now the problem: On the first load of the page there is no Cookie set on the browser. I do a GET-Request on every initialization of my Nuxt application.
plugins/csrf.js
fetch('http://127.0.0.1:8000/api/csrf-cookie', {
credentials: 'include',
})
Now I have a valid Cookie set on my side and want to communicate with the GraphQL Server but my header is not set dynamically in the query. Does anyone know how I can solve this?
My Laravel Backend is throwing now a 419 Token Mismatch Exception because I did not send a CSRF-Token with my request.
Link to the repository: https://github.com/SuddenlyRust/session-based-auth
[SOLVED] Working solution: https://github.com/SuddenlyRust/session-based-auth/commit/de8fb9c18b00e58655f154f8d0c95a677d9b685b Thanks to the help of kofh in the Nuxt Apollo discord channel 🎉
In order to accomplish this, we need to access the code that gets run every time a fetch happens. This code lives inside your Apollo client's HttpLink. While the #nuxtjs/apollo module gives us many options, we can't quite configure this at such a high level.
Step 1: Creating a client plugin
As noted in the setup section of the Apollo module's docs, we can supply a path to a plugin that will define a clientConfig:
// nuxt.config.js
{
apollo: {
clientConfigs: {
default: '~/plugins/apollo-client.js'
}
}
}
This plugin should export a function which receives the nuxt context. It should return the configuration to be passed to the vue-cli-plugin-apollo's createApolloClient utility. You don't need to worry about that file, but it is how #nuxtjs/apollo creates the client internally.
Step 2: Creating the custom httpLink
In createApolloClient's options, we see we can disable defaultHttpLink and instead supply our own link. link needs to be the output of Apollo's official createHttpLink utility, docs for which can be found here. The option we're most interested in is the fetch option which as the docs state, is
a fetch compatible API for making a request
This boils down to meaning a function that takes uri and options parameters and returns a Promise that represents the network interaction.
Step 3: Creating the custom fetch method
As stated above, we need a function that takes uri and options and returns a promise. This function will be a simple passthrough to the standard fetch method (you may need to add isomorphic-fetch to your dependencies and import it here depending on your setup).
We'll extract your cookie the same as you did in your question, and then set it as a header. The fetch function should look like this:
(uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
Putting it all together
Ultimately, your ~/plugins/apollo-client.js file should look something like this:
import { createHttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-fetch'
export default function(context) {
return {
defaultHttpLink: false,
link: createHttpLink({
uri: '/graphql',
credentials: 'include',
fetch: (uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
})
}
}
I have a Graph QL server running (Apollo Server 2) and the API behind it requires every request to include a token.
Currently the token comes from HTTP Request Cookie. This was simple enough to work. When the request comes in, grab the cookie from the header and pass it along to the HTTP request to be sent to the API server through the resolvers.
I'd like to make it so a GraphQL client can pass this token along through the POST query itself.
Basically wondering if I can define a global GQL variable of some sort. "All queries, this variable is required."
I had a similar implementation in Typescript, and in order to achieve something like this, I've define an object:
const globalInput = {
token: {
type: GraphQLString;
}
}
And then use it in your GraphQLObjectType:
const Query = new GraphQLObjectType({
name: 'Query',
fields: () => ({
myObject: {
type: MyTypeObject,
args: { ...globalInput },
resolve: (source: any, args: any) => {
// global input values can be access in args
// ex: args.token
return {}
}
}
})
})
The problem is that I need to extend it(...globalInput) it in every object type.
But it does the job.