Caching #nestjs/graphql resolver response - caching

I have graphql config:
export const graphQLConfig: GqlModuleAsyncOptions = {
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
autoSchemaFile: 'schema.graphql',
cacheControl: {
defaultMaxAge: 60,
stripFormattedExtensions: false,
calculateHttpHeaders: true.
},
...
}),
inject: [ConfigService],
}
And now I want to cache my response from resolver sending large amount of data:
#Query(() => [ConvertRateType])
async getRates(
#Args('input') input: GetConvertRatesInput,
): Promise<ConvertRateType[]> {
const rates = await this._ratesService.getRates(input)
return rates
}
How can I do this using #nestjs/graphql? I'm using redis as a storage.

Related

Apollo Server Context Request Property Does Not Exist

Here's my Apollo Server definition.
const server = new ApolloServer({
schema,
context: (async ({ req }) => {
console.log(req);
return {};
}),
csrfPrevention: true,
cache: 'bounded',
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});
As we can see that auth property does exists in the req object but somehow when I tried to get the value, an error thrown Property 'auth' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' How can I retrieve the auth property?
<ref *2> IncomingMessage {
...,
auth: { sub: '1234567890', name: 'John Doe', iat: 1516239022 },
body: { query: 'query Query() {\n }\n}\n' },
_body: true,
length: undefined,
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
}
after a little bit try and error, I can solve this issue by cast the context request to express-jwt.Request type as the code shown below,
const server = new ApolloServer({
schema,
context: (({ req }: { req: Request }) => {
console.log(req.auth);
return {};
}),
csrfPrevention: true,
cache: 'bounded',
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});

How to use RTK Query in createSlice?

I want to process the data that I get from the request in the slice.
Because not all slices are async (but work with the same data), transformResponse is not suitable.
Is there anything you can suggest?
My code example:
Some RTK Query
export const currencyApi = createApi({
reducerPath: 'currencyApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.apilayer.com/exchangerates_data' }),
endpoints: (build) => ({
fetchCurrencyRates: build.query<IApiResponse, string>({
query: (currency) => ({
url: '/latest',
params: {
base: currency
},
headers: {
apikey: *SomeApiKey*
}
})
})
})
})
Slice where I want to use data from RTK requests
const initialState: ICurrencyState = {
currencyRates: {},
availableCurrencyOptions: [],
fromCurrency: '',
toCurrency: '',
exchangeRate: 0,
error: null
}
export const currencySlice = createSlice({
name: 'currency',
initialState,
reducers: {
//
}
})
Use Hooks in Components
You can send the received data to the slice via useEffect. Something like this:
const { data } = useFetchCurrencyRatesQuery();
useEffect(() => {
if (data !== undefined) {
dispatch(...)
}
}, [data])

Apollo studio CORS error unable to reach server

I'm trying to setup apollo server in my NextJS project, I'm using apollo-server-micro and I ran into these issues:
the apollo studio sandbox is unable to reach the server due to CORS.
This is in pages/api/graphql
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "./schemas";
import { resolvers } from "./resolvers/index";
import { createContext } from "./db/context";
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: ({ res, req }) => createContext(res, req),
uploads: false,
introspection: true,
formatError: (error) => {
console.log(error);
return error;
},
});
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};
module.exports = apolloServer
.start()
.then(() => apolloServer.createHandler({ path: "/api/graphql" }));
then I tried to enable cors but without success because I get this error:
API resolved without sending a response for /api/graphql, this may result in stalled requests.
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "./schemas";
import { resolvers } from "./resolvers/index";
import { createContext } from "./db/context";
import { send } from "micro";
import cors from "micro-cors";
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: ({ res, req }) => createContext(res, req),
uploads: false,
introspection: true,
formatError: (error) => {
console.log(error);
return error;
},
});
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};
module.exports = apolloServer.start().then(() => {
const handler = apolloServer.createHandler({ path: "/api/graphql" });
return cors((req, res) => {
console.log(res);
return req.method === "OPTIONS" ? send(res, 200, 'ok') : handler(req, res);
});
});

Can't set context to resolvers in apollo server

Hello I'm new to GraphQl and to Apollo Server.
I would like to implement authentication on my project.
But
For some reason, I can't seem to set context on my resolvers in apollo server.
Here's my index
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const userId = jwtDecode(req.headers.authorization)
return userId.sub
}
})
And my query
Query: {
users: async (parent, args, context) => {
try {
console.log(context)
return await getUsers(context)
} catch (err) {
console.log(err)
throw new Error(err.message)
}
}
When I try to output the context the result is always like this...
{ injector:
Injector {
options:
{ name: 'index.ts_8346047369535445_SESSION',
injectorScope: 'SESSION',
hooks: [Array],
children: [] },
_classMap: Map {},
_factoryMap: Map {},
_applicationScopeInstanceMap:
Map {
Symbol(ModuleConfig.index.ts_8346047369535445) => undefined,
[Function] => undefined },
_sessionScopeInstanceMap: Map { [Function: ModuleSessionInfo] => [ModuleSessionInfo] },
_applicationScopeServiceIdentifiers:
[ Symbol(ModuleConfig.index.ts_8346047369535445), [Function] ],
_requestScopeServiceIdentifiers: [],
_sessionScopeServiceIdentifiers: [ [Function: ModuleSessionInfo] ],
_hookServiceIdentifiersMap: Map {},
_name: 'index.ts_8346047369535445_SESSION',
_injectorScope: 'SESSION',
_defaultProviderScope: 'SESSION',
........
What's returned inside the context function should always be an object. So you would do something like
context: ({ req }) => {
const { sub } = jwtDecode(req.headers.authorization)
return {
sub,
}
}
and then access the value inside the resolver by calling context.sub.
However, if you're using GraphQL Modules to create your schema, you should follow the library's documentation for configuring your context on a per-module basis.

Apollo 2.0.0 Graphql cookie session

Can someone help me on this, My setup was as follows prior to Apollo 2.0, I had a server.js in which i used express and graphql-server-express
I had a http only cookie session, when a user logs in I set the jwt token as a cookie and it is set in browser as http only.
On subsequent request I validate the cookie that the browser passes back. It was all working fine and I could access
the token from req.session.token in any other resolver and validate the jwt token saved in the cookie session.
server.js
import express from 'express';
import { graphqlExpress, graphiqlExpress } from 'graphql-server-express';
import { ApolloEngine } from 'apollo-engine';
import bodyParser from 'body-parser';
import cors from 'cors';
import cookieSession from 'cookie-session';
import schema from './schema/';
​
const server = express();
​
server.use(
cookieSession({
name: 'session',
keys: 'k1,k2',
maxAge: 30 * 60 * 1000,
domain: '.mydomain.com',
path: '/',
}),
);
​
const corsOptions = {
origin: 'http://local.mydomain.com:3000',
credentials: true,
methods: ['GET', 'PUT', 'POST', 'OPTIONS'],
};
​
server.use(cors(corsOptions));
​
server.use(
'/graphql',
bodyParser.json(),
graphqlExpress(req => ({
schema,
tracing: true,
context: { req },
})),
);
​
if (process.env.NODE_ENV !== 'production') {
server.use('/graphiql',graphiqlExpress({endpointURL: '/graphql'}));
}
​
const engine = new ApolloEngine({
apiKey: engineConfig.apiKey,
});
​
engine.listen(
{
port: 3000,
graphqlPaths: ['/graphql'],
expressApp: server,
},
() => {console.log('GraphiQL is now running');},
);
authenticateResolver.js
const authenticateResolver = {
Query: {
authenticate: async (root, args, context) => {
const { req } = context;
​
const auth = `Basic ${Buffer.from(`${args.username}:${args.password}`).toString('base64')}`;
​
const axiosResponse = await axios.post("localhot:8080/login, 'true',
{
headers: {
Authorization: auth,
},
});
​
if (axiosResponse.status === 200 && axiosResponse.data.token) {
req.session.token = axiosResponse.data.token;
}
return {
status: 200,
};
},
But when I upgraded to Apollo 2.0 my server.js code changed, authenticateResolver was as is.
I am now unable to access req.session.token in any subsequent requests since the cookie session is not getting set.
When I open Developer tools in chrome I cannot see the cookie being set when Authentication is called.
What am I doing wrong here ?
server.js # After Apollo 2.0 upgrade
​
import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';
import cors from 'cors';
import cookieSession from 'cookie-session';
import { mergedTypes, resolvers } from './schema/';
​
const server = express();
​
server.use(
cookieSession({
name: 'session',
keys: 'k1,k2',
maxAge: 30 * 60 * 1000,
domain: '.mydomain.com',
path: '/',
}),
);
​
const corsOptions = {
origin: 'http://local.mydomain.com:3000',
credentials: true,
methods: ['GET', 'PUT', 'POST', 'OPTIONS'],
};
​
server.use(cors(corsOptions));
​
server.listen({ port: 3000 }, () => {
console.log('Server ready');
console.log('Try your health check at: .well-known/apollo/app-health');
});
​
const apollo = new ApolloServer({
typeDefs: gql`
${mergedTypes}
`,
resolvers,
engine: false,
context: ({ req }) => ({ req }),
});
​
apollo.applyMiddleware({
server
});
Yes, If you look at the graphql playground there is a settings option if you click on that you can observe few settings, one of them being
"request.credentials": "omit" just change it to
"request.credentials": "include" and save settings and it should now work.
My code looks as follows as well:
const app = express()
app.use(
cookieSession({
name: 'session',
keys: corsConfig.cookieSecret.split(','),
maxAge: 60 * 60 * 1000,
domain: corsConfig.cookieDomain,
path: '/',
})
)
const corsOptions = {
origin: corsConfig.corsWhitelist.split(','),
credentials: true,
methods: ['GET', 'PUT', 'POST', 'OPTIONS'],
}
app.use(cors(corsOptions))
const apollo = new ApolloServer({
typeDefs: gql`
${mergedTypes}
`,
resolvers,
engine: false,
context: ({ req }) => ({ req }),
tracing: true,
debug: !process.env.PRODUCTION,
introspection: !process.env.PRODUCTION,
})
apollo.applyMiddleware({
app,
path: '/',
cors: corsOptions,
})
app.listen({ port: engineConfig.port }, () => {
console.log('🚀 - Server ready')
console.log('Try your health check at: .well-known/apollo/app-health')
})

Resources