What is the best way to handle the react GQL configurations with all query and mutation policies and for file upload? - gql

Need to know the best practice to maintain the react GQL configurations settings where we set all query, mutation, or other policies, and the better way to handle file upload setting?

There are many approaches with different use cases. The following code might be helpful in your case.
import {
ApolloClient,
ApolloLink,
DefaultOptions,
InMemoryCache,
} from "#apollo/client";
import { onError } from "#apollo/client/link/error";
import { createUploadLink } from "apollo-upload-client";
const authMiddleware = new ApolloLink((operation: any, forward: any) => {
const token = localStorage.getItem("token") || null;
operation.setContext({
headers: {
authorization: `Bearer ${token}`,
},
});
return forward(operation);
});
const defaultOptions: DefaultOptions = {
watchQuery: {
fetchPolicy: "cache-and-network",
errorPolicy: "ignore",
},
query: {
fetchPolicy: "network-only",
errorPolicy: "all",
},
mutate: {
errorPolicy: "all",
},
};
const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }: any) => {
// You can modify do something with the errors
}
);
const client = new ApolloClient({
cache: new InMemoryCache(),
connectToDevTools: true,
link: ApolloLink.from([
errorLink,
authMiddleware,
createUploadLink({
uri: process.env.REACT_APP_GRAPHQL_URI || "http://localhost:3001/graphql",
}),
]),
defaultOptions: defaultOptions,
});
export default client;

Related

Nest JS retryAttempts on undefined error driving me nuts

I keep getting this error upon starting my nestjs application:
TypeError: Cannot read property 'retryAttempts' of undefined
The error is originating from my common config file, which was loaded into my configurations in app.module. I had tried call an await function in it and it gave me the retryattempts undefined error.
However, after commenting out that line, the error is resolved.
Context: I am trying to load variables from a pcf config server in place of env variables.
getEnvfrompcfconfig
import { getPcfServerClientCredentials } from '#common/configurations/pcf-credentials.config';
import axios, { AxiosResponse } from 'axios';
import * as client from 'cloud-config-client';
import { getAppEnv } from 'cfenv';
export const getEnvFromPcfConfigServer = async () => {
const appEnv = getAppEnv();
const pcfCredentials = getPcfServerClientCredentials();
const { client_secret, client_id, access_token_uri, uri } = pcfCredentials;
const accessToken: string = await axios
.post(
access_token_uri,
new URLSearchParams({
grant_type: 'client_credentials', //gave the values directly for testing
client_id,
client_secret,
}),
)
.then((res: AxiosResponse) => {
return res.data.access_token;
})
.catch((e) => console.log('cannot get access token', e));
const options: client.Options = {
name: 'application',
rejectUnauthorized: false,
endpoint: uri,
profiles: [appEnv.app.space_name || 'dev'],
headers: { authorization: `bearer ${accessToken}` },
};
const config = await client.load(options);
const url = config.get('baseurl');
return url;
};
config file
import { ScheduleLog as ScheduleLogEntity } from '#scheduleLog/entities/schedule-log.entity';
import { CustomNamingStrategy } from '#common/helpers/database/database.helper';
import { Schedule as ScheduleEntity } from 'src/schedule/entities/schedule.entity';
import { ICommonConfiguration } from '#common/interfaces/configurations/common-configuration';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { getCreds } from '#common/configurations/credhub.config';
export const configuration = async () => {
const {
APP_PORT,
ENVIRONMENT,
MARIADB_HOST,
MARIADB_PORT,
MARIADB_USERNAME,
MARIADB_PASSWORD,
MARIADB_DATABASE,
AUTH_PASSWORD,
EUREKA_AWS_URL,
EUREKA_UAA,
EUREKA_PCF,
EUREKA_DATALOADER,
EUREKA_COMMS,
EUREKA_PROXY_HOST,
EUREKA_PROXY_PORT,
EUREKA_FULFILMENT,
} = process.env;
const creds = getCreds();
const thing = await getEnvFromPcfConfigServer(); //<-- line that is causing problem
console.log('thing', thing);
return {
APP: {
PORT: Number(APP_PORT),
ENVIRONMENT,
},
UAAPASSWORD: creds.uaaPassword || AUTH_PASSWORD,
DATABASE: {
type: 'mysql',
host: MARIADB_HOST,
port: Number(MARIADB_PORT),
username: MARIADB_USERNAME,
password: MARIADB_PASSWORD || creds.mariaDbPassword,
database: MARIADB_DATABASE,
entities: [ScheduleEntity, ScheduleLogEntity],
synchronize: false,
namingStrategy: new CustomNamingStrategy(),
// autoLoadEntities: true,
},
HTTPMODULE: {
BASEURL: {
AWS: EUREKA_AWS_URL,
UAA: EUREKA_UAA,
PCF: EUREKA_PCF,
DATALOADER: 'hi',
COMMS: EUREKA_COMMS,
FULFILMENT: EUREKA_FULFILMENT,
},
PROXYAGENT: new HttpsProxyAgent({
host: EUREKA_PROXY_HOST,
port: EUREKA_PROXY_PORT,
}),
},
};
};
App.module
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
envFilePath: `.env.stage.${appEnv.app.space_name || 'local'}`,
}),
I find this very weird. Anyone able to help?

Dispatch RTK Query mutation inside redux-observable epic

I'm using redux toolkit to create slices:
import { createSlice } from '#reduxjs/toolkit';
const thingSlice = createSlice({
...
reducers: {
setThing(state, action) {}
}
});
export const { setThing } = thingSlice.actions;
I'm using RTK Query to build mutations:
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react';
const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '' }),
endpoints: (build) => ({
postThing: build.mutation({
query: () => ({ url: `/api/thing`, method: 'POST', body: 'thing' }),
})
})
}}
I'm using redux-observable epics for complex actions logic.
How do I return a mutation action inside an epic?
import { setThing } from './thingSlice';
const somethingEpic = (actions$, state$) =>
actions$.pipe(
filter(setThing.match),
mergeMap(() => {
// how to dispatch "postSomething" here?
})
);

Context is empty in GraphQL middleware

I'm sending from frontend authorization token in headers and then I want to check validity of this token in some endpoints using middleware and context, but context is always empty.
I'm using type-graphql.
Frontend code (I check request in 'Network' tab and I can see my additional header):
private async mutate<T>(
mutation: DocumentNode,
data: unknown,
token?: string
) {
const response = await apolloClient.mutate<T>({
mutation: mutation,
context: {
headers: {
'auth-token': token || '',
},
},
variables: {
data: data,
},
});
return response.data;
}
Resolver code:
#Mutation(() => Token)
#UseMiddleware(authMiddleware)
async login(#Ctx() ctx: unknown, #Arg('data') data: LoginInput) {
console.log(ctx);
...
}
Middleware code:
export const authMiddleware: MiddlewareFn = ({ context }, next) => {
console.log(context);
try {
return next();
} catch (error) {
return next();
}
};
console.log is always equal to {}
I found the cause.
In declaration of ApollorServer the context was missing.
const server = new ApolloServer({
schema,
context: ({ req }) => {
const context = {
req,
};
return context;
},
cors: {
origin: '*',
credentials: true,
},
});

How to set cookies using apollo server express

I am using apollo server express on my backend and next js on frontend.
I have been trying to set cookies in backend using (response) from context but unfortunately it is not working. i would like your help.
here is my backend code.
import http from 'http';
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import cors from 'cors';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
import { execute, subscribe } from 'graphql';
import {
SubscriptionServer,
ConnectionParams,
ConnectionContext,
} from 'subscriptions-transport-ws';
import { connect } from './Config';
import { typeDefs } from './TypeDefs';
import { resolvers } from './Resolvers';
import { isAuthForSubscription } from './Helpers';
import cookieParser from 'cookie-parser';
const startServer = async () => {
const app = express();
app.use(
cors({
credentials: true,
origin: 'http://localhost:3000',
})
);
app.use(cookieParser());
const httpServer = http.createServer(app);
const subscriptionServer = SubscriptionServer.create(
{
schema: makeExecutableSchema({ typeDefs, resolvers }),
execute,
subscribe,
onConnect: async (
connectionParams: ConnectionParams,
_websocket: any,
context: ConnectionContext
) => {
let user = null;
if (connectionParams.Authorization) {
user = await isAuthForSubscription(connectionParams.Authorization);
}
return { context, user };
},
},
{
server: httpServer,
path: '/graphql',
}
);
const server = new ApolloServer({
schema: makeExecutableSchema({ typeDefs, resolvers }),
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
serverWillStart: async () => {
return {
drainServer: async () => {
subscriptionServer.close();
},
};
},
},
],
context: ({ req, res }) => ({ req, res }),
});
await server.start();
server.applyMiddleware({ app });
await new Promise<void>(resolve =>
httpServer.listen({ port: 4000 }, resolve)
);
await connect();
console.log(
`server started on port http://localhost:4000${server.graphqlPath}`
);
return { server, app };
};
startServer();
// and my resolver is this one
import { ApolloError } from 'apollo-server-express';
import {
TCreateAccountArgs,
TLoginArgs,
TContext,
CookieName,
} from '../../__generated__';
import { generate } from '../../Helpers';
import { userModel } from '../../Models';
import { CookieOptions } from 'express';
export const login = async(_: any, args: TLoginArgs, { res, req }: TContext) => {
try {
const find = await userModel.findOne({ username: args.username });
if (!find) {
return new ApolloError('Account not found');
}
const comparePassword = generate.comparePassword(
find.password,
args.password
);
if (!comparePassword) {
return new ApolloError('Incorrect password or username');
}
const generateToken = generate.generateToken({ _id: find._id });
const cookieOptions: CookieOptions = {
httpOnly: true,
maxAge: 1 * 60 * 60 * 24 * 1000,
secure: true
};
res.cookie(CookieName.token, generateToken, cookieOptions);
return {
token: generateToken,
data: {
username: find.username,
_id: find._id,
email: find.email,
profilePicture: find.profilePicture,
createdAt: find.createdAt,
updatedAt: find.updatedAt,
},
};
} catch (error: any) {
return new ApolloError(
`Unable to login due to internal server error ${error.message}`
);
}
};
on frontend I am receiving this error message. Cannot read property 'cookie' of undefined
This question has been there from several months ago.
Just in case you haven't been able to fix it yet, the reason why you're not seeing the cookies in the frontend is that you're using the option httpOnly, those cookies are not supposed to be readable by the browser (they are useful to handle authentication in the server while protecting you from cross site scripting).
If you really need to read these cookies in the browser, you should set the httpOnly option to false.

Is it good to use vuex-persistedstate to get user information after refreshing the page?

I use nodeJS on the backend and Vue with Vuex on the frontend. I have the logic for logging the user and everything works great, I put the JWT token in the Cookie and from there I read it and decode and log the user. When I refresh the page, my data is lost, so I'm wondering if it's a good practice to use vuex-persistedstate and save the entire state after refreshing?
This is my Vuex Store:
import { createApp } from 'vue';
import { createStore } from 'vuex';
import router from './router';
import createPersistedState from 'vuex-persistedstate';
import Cookies from 'js-cookie';
import './bootstrap.min.css';
import App from './App.vue';
import API from './API';
const store = createStore({
state: {
user: {},
userLoading: null,
userError: null,
},
plugins: [
createPersistedState({
storage: {
getItem: key => Cookies.get(key),
setItem: (key, value) =>
Cookies.set(key, value, { expires: 3, secure: true }),
removeItem: key => Cookies.remove(key),
},
}),
],
getters: {},
mutations: {
SET_USER(state, payload) {
state.user = payload;
},
SET_USER_LOADING(state, payload) {
state.userLoading = payload;
},
SET_USER_STATUS_FAIL(state, payload) {
state.userError = payload;
},
},
actions: {
async login({ commit }, User) {
commit('SET_USER_LOADING', true);
commit('SET_USER', []);
try {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const { data } = await API.post(`/api/v1/users/login`, User, config);
commit('SET_USER', data);
commit('SET_USER_LOADING', false);
} catch (error) {
commit('SET_USER_LOADING', false);
commit(
'SET_USER_STATUS_FAIL',
error.response && error.response.data.message
? error.response.data.message
: error.message
);
}
},
},
});

Resources