I have followed Apollo docs but seem to still be having issues connecting to subscriptions. Here is my code: On the frontend, I can see it trying to connect but is logging:
WebSocket connection to 'ws://localhost:4000/subscriptions' failed:
I have been following this: https://www.apollographql.com/docs/apollo-server/data/subscriptions/ but it seems like the documentation is slightly behind so I may be missing something.
Client:
import ReactDOM from 'react-dom';
import './index.css';
import Routes from './routes';
import 'semantic-ui-css/semantic.min.css';
import { setContext } from '#apollo/client/link/context';
import { WebSocketLink } from '#apollo/client/link/ws';
import { getMainDefinition } from '#apollo/client/utilities';
import {
ApolloProvider,
ApolloClient,
HttpLink,
InMemoryCache,
split,
} from '#apollo/client';
// Http link
const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql' });
// Websocket link
const wsLink = new WebSocketLink({
uri: 'ws://localhost:4000/subscriptions',
options: {
reconnect: true
}
});
// Attach auth headers to requests
const middlewareLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
const refreshToken = localStorage.getItem('refreshToken');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
x_token: token ? `${token}` : "",
x_refresh_token: refreshToken ? `${refreshToken}`: ""
}
}
});
// Combine
const httpLinkWithMiddleware = middlewareLink.concat(httpLink);
// Split link - either http or ws depending on graphql
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLinkWithMiddleware,
);
// Create client with link
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
// Provide client
const App = (
<ApolloProvider client={client}>
<Routes />
</ApolloProvider>
)
// Render
ReactDOM.render(
App,
document.getElementById('root')
);
Server:
import express from 'express';
import path from 'path';
import { fileLoader, mergeTypes, mergeResolvers } from 'merge-graphql-schemas';
import { ApolloServer } from 'apollo-server-express';
import { refreshTokens } from './auth';
import models from './models';
import cors from 'cors';
import jwt from 'jsonwebtoken';
const SECRET = "";
const SECRET2 = "";
const typeDefs = mergeTypes(fileLoader(path.join(__dirname, './schema')));
const resolvers = mergeResolvers(fileLoader(path.join(__dirname, './resolvers')));
const PORT = 4000;
const app = express();
// Cors
app.use(cors('*'));
// Add tokens
const addUser = async (req, res, next) => {
const token = req.headers['x_token'];
if (token) {
try {
const { user } = jwt.verify(token, SECRET);
req.user = user;
} catch (err) {
const refreshToken = req.headers['x_refresh_token'];
const newTokens = await refreshTokens(token, refreshToken, models, SECRET, SECRET2);
if (newTokens.token && newTokens.refreshToken) {
res.set('Access-Control-Expose-Headers', 'x_token', 'x_refresh_token');
res.set('x_token', newTokens.token);
res.set('x_refresh_token', newTokens.refreshToken);
}
req.user = newTokens.user;
}
}
next();
};
app.use(addUser);
// Create server
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
path: '/subscriptions'
},
context: ({req, res, connection}) => {
const user = req.user;
return { models, SECRET, SECRET2, user };
},
});
// Apply middleware
server.applyMiddleware({ app });
// Sync and listen
models.sequelize.sync({force: true}).then(x => {
app.listen({ port: PORT }, () => {
console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`);
console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`);
}
);
});
Any help would be appreciated...
I can't see in the server declaring a websocket creation. Something like:
const WebSocket = require('ws');
const graphQLWebSocket = new WebSocket.Server({noServer: true});
Then also put in:
server.installSubscriptionHandlers(graphQLWebSocket);
After the row with server.applyMiddleware({ app });
More info here.
Related
i'm trying to use createUploadLink and GraphQLWsLink in react is there any way
import * as dotenv from 'dotenv'
dotenv.config()
import { getUser, userProtect ,adminProtect,getAdmin} from "./Users/user.utilities.js";
import { typeDefs, resolvers } from './schema.js';
import { ApolloServer } from '#apollo/server';
import { createServer } from 'http';
import { expressMiddleware } from '#apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '#apollo/server/plugin/drainHttpServer';
import { makeExecutableSchema } from '#graphql-tools/schema';
import bodyParser from 'body-parser';
import express from 'express';
import { graphqlUploadExpress,GraphQLUpload} from "graphql-upload";
import * as path from 'path';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { PubSub } from 'graphql-subscriptions';
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();
const httpServer = createServer(app);
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql'
});
const wsServerCleanup = useServer({schema}, wsServer);
const startApolloServer = async () => {
app.use(graphqlUploadExpress())
app.use('/images', express.static(path.join(__dirname, '/uploads/images')))
const context = async ({ req }) => {
return {
LogedInUser: await getUser(req.headers.token),
LogedInAdmin: await getAdmin(req.headers.admintoken),
userProtect,
adminProtect
}
}
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await wsServerCleanup.dispose();
}
}
}
}
],
})
await server.start();
app.use('/graphql', bodyParser.json(), expressMiddleware(server,{ context }));
}
startApolloServer()
httpServer.listen(8000, () => { console.log(`🚀 Server ready at http://localhost:4000$`) })
;
i've tried this but is not working
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:8000/graphql',
}));
const uploadLink = createUploadLink({
uri: "http://localhost:8000/graphql" });
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const authLink = setContext((_,{headers })=>{
return{
headers:{
...headers,
token:localStorage.getItem(TOKEN),
admintoken:localStorage.getItem(ADMINTOKE)
}
}
})
export const client = new ApolloClient({
link:authLink.concat(splitLink) ,
cache: new InMemoryCache(),
});
i'm trying to use createUploadLink and GraphQLWsLink in react is there any way
i'm trying to use createUploadLink and GraphQLWsLink in react is there any wayi'm trying to use createUploadLink and GraphQLWsLink in react is there any way
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.
I'm relatively new to next-auth & next in general. We have an application which we are transitioning to using AWS Cognito for authentication. It's a server side rendered application & I know this as under pages/_app.tsx is getInitialProps(). The code being:
import React from "react";
import App from "next/app";
import Router from "next/router";
import { Provider } from "react-redux";
import withRedux from "next-redux-wrapper";
import ReduxToastr from "react-redux-toastr";
import NProgress from "nprogress";
import * as Sentry from "#sentry/node";
// import { setTokens } from "../util/tokens";
import jwtDecode from "jwt-decode";
import { SessionProvider, getSession } from "next-auth/react";
import createStore from "../redux/store";
import { setSession } from "../redux/session";
import "semantic-ui-css/semantic.min.css";
import "react-redux-toastr/lib/css/react-redux-toastr.min.css";
import "../styles/nprogress.css";
import "../styles/overrides.scss";
import "../styles/util.css";
import axios from "axios";
import { IdToken, CognitoTokens } from "../interfaces";
Router.events.on("routeChangeStart", () => {
NProgress.start();
});
Router.events.on("routeChangeComplete", () => NProgress.done());
Router.events.on("routeChangeError", () => NProgress.done());
NProgress.configure({ showSpinner: false });
type tProps = {
passport?: {};
};
class MyApp extends App {
static async getInitialProps({ Component, ctx }: any) {
debugger;
let pageProps: tProps = {};
const session = await getSession({
req: ctx.req,
}); // this calls '/api/auth/session'
// let tokens: AuthTokens = {};
if (ctx.isServer && ctx.query?.code) {
const {
AUTH_DOMAIN,
COGNITO_CLIENT_ID,
COGNITO_CLIENT_SECRET,
BASE_URL,
} = process.env;
const { data }: { data: CognitoTokens } = await axios.post(
`https://${AUTH_DOMAIN}/oauth2/token?client_id=${COGNITO_CLIENT_ID}&client_secret=${COGNITO_CLIENT_SECRET}&grant_type=authorization_code&redirect_uri=${BASE_URL}&code=${ctx.query.code}`,
);
}
return { pageProps };
}
render() {
debugger
const { Component, pageProps, store }: any = this.props;
return (
<SessionProvider session={pageProps.session}>
<Provider store={store}>
<Component {...pageProps} />
<ReduxToastr
preventDuplicates
position="bottom-right"
transitionIn="fadeIn"
transitionOut="fadeOut"
timeOut={5000}
progressBar
/>
</Provider>
</SessionProvider>
);
}
}
export default withRedux(createStore)(MyApp);
pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import CognitoProvider from "next-auth/providers/cognito";
export default NextAuth({
providers: [
CognitoProvider({
clientId: process.env.COGNITO_CLIENT_ID,
clientSecret: process.env.COGNITO_CLIENT_SECRET,
issuer: process.env.COGNITO_ISSUER,
}),
],
debug: process.env.NODE_ENV === "development" ? true : false,
});
There is authentication middleware defined for the API. The main reason for this is that we have a custom permissions attribute defined on a user in Cognito which defines what CRUD operations they can do in specific environments
server/index.ts
import express from "express";
import next from "next";
import bodyParser from "body-parser";
import session from "express-session";
import connectMongodbSession from "connect-mongodb-session";
// Config
import { /* auth0Strategy, */ getSessionConfig } from "./config";
const port = parseInt(process.env.PORT || "3000", 10);
const dev = process.env.NODE_ENV !== "production";
// API
import authAPI from "./api/auth";
import accountsAPI from "./api/accounts";
import usersAPI from "./api/users";
import jobsAPI from "./api/jobs";
import awsAPI from "./api/aws";
import completedJobsAPI from "./api/completedJobs";
// middleware
import { restrictAccess, checkPermissions } from "./middleware/auth";
// Server
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
const MongoDBStore = connectMongodbSession(session);
const store = new MongoDBStore({
uri: process.env.MONGO_PROD_URL,
collection: "godfatherSessions",
});
server.use(bodyParser.json());
server.use(session(getSessionConfig(store)));
server.get("/healthcheck", async (req: any, res, next) => {
const { db } = req.session.req.sessionStore;
try {
await db.command({
ping: 1,
});
res.status(200).end();
} catch (err) {
next(err);
}
});
// Middleware
server.use(checkPermissions);
// API
server.use(authAPI);
server.use(accountsAPI, restrictAccess);
server.use(usersAPI, restrictAccess);
server.use(jobsAPI, restrictAccess);
server.use(awsAPI, restrictAccess);
server.use(completedJobsAPI, restrictAccess);
// Protected Page Routes
server.get("/accounts", restrictAccess, (req: any, res) =>
app.render(req, res, "/accounts", req.query),
);
server.get("/users", restrictAccess, (req: any, res) =>
app.render(req, res, "/users", req.query),
);
server.get("/jobs", restrictAccess, (req: any, res) =>
app.render(req, res, "/jobs", req.query),
);
server.get("/completedJobs", restrictAccess, (req: any, res) => {
app.render(req, res, "/completedJobs", req.query);
});
server.get("/debug-sentry", function mainHandler() {
throw new Error("My first Sentry error!");
});
// Fallback Routes
server.all("*", handle as any);
// The error handler must be before any other error middleware and after all controllers
// server.use(Sentry.Handlers.errorHandler());
server.listen(port, (err?: Error) => {
if (err) throw err;
console.log(
`> Server listening at http://localhost:${port} as ${
dev ? "development" : process.env.NODE_ENV
}`,
);
});
});
server/middleware/auth.ts
/* eslint-disable #typescript-eslint/camelcase */
import "cross-fetch/polyfill";
import { Request, Response, NextFunction } from "express";
import { ForbiddenError } from "../../util/errors";
import { Environments } from "../../interfaces";
import jwt_decode from "jwt-decode";
import { CognitoUser, CognitoUserPool } from "amazon-cognito-identity-js";
import { getSession } from "next-auth/react";
export async function checkPermissions(
req: CustomRequest,
res: Response,
next: NextFunction,
) {
const { path, method } = req;
const { authorization, env } = req.headers;
const session = await getSession({ req });
debugger;
if (
!req.url.includes("/api/") ||
["/api/auth/callback/cognito", "/api/auth/session"].includes(req.path)
) {
return next();
}
if (req.headers.env === "development") {
return next();
}
console.log(session);
if (!authorization) {
return res.status(401).redirect("/login");
}
const decoded: any = jwt_decode(authorization);
const invalidAuth = isInvalidAuth(decoded, path);
if (!invalidAuth) {
const userPool = new CognitoUserPool({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
ClientId: process.env.COGNITO_CLIENT_ID,
});
const username = decoded.username || decoded["cognito:username"];
const cognitoUser = new CognitoUser({
Username: username,
Pool: userPool,
});
const userAttributes: any = await getUserAttributes(cognitoUser);
const permissions = userAttributes.permissions
.split(",")
.map(perm => `"${perm}"`);
const hasPermission = getHasPermission(
method,
permissions,
env as Environments,
);
if (!hasPermission) {
return res
.status(403)
.send(
new ForbiddenError(
"You do not have permission to perform this action.",
).toErrorObject(),
);
}
return next();
}
return next(invalidAuth);
}
The callback URL defined in Cognito app client is "http://localhost:3000/api/auth/callback/cognito" (for development purposes - as is highlighted in the example).
When running the application & logging in via the Cognito hosted UI, the 1st breakpoint hit is the debugger on L5 in checkPermissions. The session is null at this point & the path is /api/auth/session.
The issue comes when I step through past that breakpoint to return next() where I get the error(s) shown in the picture below. So these are ECONNRESET errors & if I let it run freely, these errors occur over & over again resulting in an EMFILE error every now & again until eventually the application crashes. No other breakpoints are hit.
Versions:
next: v9.3.5
next-auth: v4.1.0
Even though, by default, next-auth will use localhost:3000, I kept getting an ENOTFOUND error when I didn't define the URL in the .env so I set it to:
NEXTAUTH_URL=http://127.0.0.1:3000
After reading all docs & examples & several tutorials on how to do it (no one has done a tutorial on v4 - just v3) I'm still stumped
For me helped to remove .next/ and add these lines to .env
NODE_TLS_REJECT_UNAUTHORIZED="0"
NEXTAUTH_URL="http://127.0.0.1:3000"
NEXTAUTH_URL_INTERNAL="http://127.0.0.1:3000"
It seems to work very fine.
Greetings I have a problem every time when I want to make an Admin REST API call to Shopify I get this problem "Error: Failed to parse session token '****' jwt expired" I see some code examples on the net I have my own custom session storage for accessToken and shop but every time when I try to call my own route from front-end and get more details about the shop I get this problem here is code example can anyone help me?
server.js
import "#babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "#shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
const helmet = require("koa-helmet");
const compress = require("koa-compress");
const cors = require("koa-cors");
const logger = require("koa-logger");
const bodyParser = require("koa-bodyparser");
import axios from "axios";
import { storeCallback, loadCallback, deleteCallback } from "./custom-session";
const sequelize = require("./database/database");
const { Shopify_custom_session_storage } = require("./../models/sequelizeModels");
// import apiRouter from "./../routers/apiRouter";
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(storeCallback, loadCallback, deleteCallback)
});
sequelize.sync()
.then(() => {
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
// Getting users data from database and saving it to variable //
try {
await Shopify_custom_session_storage.findAll({
raw: true,
where:{
shop: shop
},
limit:1
});
} catch(err) {
console.log(err);
throw err;
}
// End of Getting users data from database and saving it to variable //
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>{
return Shopify_custom_session_storage.destroy({
where: {
shop: shop
}
})
.then(result => {
return true;
})
.catch(err => {
if(err) throw err;
return false;
});
}
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.post("/webhooks", async (ctx) => {
try {
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
}
});
router.post("/graphql", verifyRequest({ returnHeader: true }), async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
// Our Routes //
router.get("/getProducts", verifyRequest({ returnHeader: true }), async (ctx) => {
try{
const session = await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res);
const client = new Shopify.Clients.Rest(session.shop, session.accessToken);
console.log(session);
}catch(err) {
console.log(err);
throw new Error(err);
}
});
// End of Our Routes //
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
try {
let user = await Shopify_custom_session_storage.findAll({
raw: true,
where:{
shop: shop
},
limit:1
});
// This shop hasn't been seen yet, go through OAuth to create a session
if (user[0].shop == undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
} catch(err) {
console.log(err);
throw err;
}
});
server.use(router.allowedMethods());
server.use(router.routes());
// Setting our installed dependecies //
server.use(bodyParser());
server.use(helmet());
server.use(cors());
server.use(compress());
server.use(logger());
// End of Setting our installed dependecies //
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
})
.catch((err) => {
if(err) throw err;
return process.exit(1);
})
_app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "#shopify/polaris";
import { Provider, useAppBridge } from "#shopify/app-bridge-react";
import { authenticatedFetch, getSessionToken } from "#shopify/app-bridge-utils";
import { Redirect } from "#shopify/app-bridge/actions";
import "#shopify/polaris/dist/styles.css";
import translations from "#shopify/polaris/locales/en.json";
import axios from 'axios';
function userLoggedInFetch(app) {
const fetchFunction = authenticatedFetch(app);
return async (uri, options) => {
const response = await fetchFunction(uri, options);
if (
response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1"
) {
const authUrlHeader = response.headers.get(
"X-Shopify-API-Request-Failure-Reauthorize-Url"
);
const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
return null;
}
return response;
};
}
function MyProvider(props) {
const app = useAppBridge();
const client = new ApolloClient({
fetch: userLoggedInFetch(app),
fetchOptions: {
credentials: "include",
},
});
const axios_instance = axios.create();
// Intercept all requests on this Axios instance
axios_instance.interceptors.request.use(function (config) {
return getSessionToken(app) // requires a Shopify App Bridge instance
.then((token) => {
// Append your request headers with an authenticated token
config.headers["Authorization"] = `Bearer ${token}`;
return config;
});
});
const Component = props.Component;
return (
<ApolloProvider client={client}>
<Component {...props} axios_instance={axios_instance}/>
</ApolloProvider>
);
}
class MyApp extends App {
render() {
const { Component, pageProps, host } = this.props;
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
host: host,
forceRedirect: true,
}}
>
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
host: ctx.query.host,
};
};
export default MyApp;
index.js
import { Heading, Page, Button } from "#shopify/polaris";
function Index(props){
async function getProducts(){
const res = await props.axios_instance.get("/products");
return res;
}
async function handleClick() {
const result = await getProducts();
console.log(result);
}
return (
<Page>
<Heading>Shopify app with Node and React </Heading>
<Button onClick={handleClick}>Get Products</Button>
</Page>
);
}
export default Index;
I found the solution for "Error: Failed to parse session token '******' jwt expired" the problem was Computer Time was not synchronized, check the computer time and synchronized it, for my example, I'm on Kali Linux and I search it how to synchronize time on Kali Linux and follow that tutorial when you finally synchronize your time restart your application server and try again. That's it so dump I lost 4 days on this.
I've created simple backend api using apollo-server-express and postgresql that has two mutations: register and login. When the login mutation is used, the api creates an auth token and a refresh token and then sends them as cookies using res.cookie(...). In the frontend, I'm using apollo-client to access my GraphQL api. When I call the login mutation, everything works as expected, I receive the set-cookie header along with the requested user-details. However, the cookies don't show up in the Application tab under Cookies. Can someone please help me. I'm using the following code:
server/index.ts
import "dotenv/config";
import "reflect-metadata";
import * as express from "express";
import * as cors from "cors";
import {
ConnectionOptions,
createConnection,
getConnectionOptions
} from "typeorm";
import { buildSchema } from "type-graphql";
import { GraphQLSchema } from "graphql";
import { ApolloServer } from "apollo-server-express";
import * as path from "path";
import * as cookieParser from "cookie-parser";
import {
UserResolver,
DepartmentResolver,
CourseResolver,
SubjectResolver,
DocumentResolver
} from "./modules";
import { customAuthChecker, formatError, GQLRuntimeContext } from "./utils";
const getOptions = async () => {
console.log(`NODE_ENV: ${process.env.NODE_ENV}`);
let connectionOptions: ConnectionOptions;
connectionOptions = {
type: "postgres",
synchronize: true,
logging: false,
entities: [path.join(__dirname, "./modules/**/*entity.*")]
};
if (process.env.DATABASE_URL) {
Object.assign(connectionOptions, { url: process.env.DATABASE_URL });
} else {
// gets your default configuration
// you could get a specific config by name getConnectionOptions('production')
// or getConnectionOptions(process.env.NODE_ENV)
connectionOptions = await getConnectionOptions();
}
return connectionOptions;
};
const connect = async (): Promise<void> => {
const typeormconfig = await getOptions();
console.log(`\n`);
console.log(`\tconfiguration: `);
console.log(typeormconfig);
console.log(`\n`);
await createConnection(typeormconfig);
};
connect().then(async () => {
const schema: GraphQLSchema = await buildSchema({
resolvers: [
UserResolver,
DepartmentResolver,
CourseResolver,
SubjectResolver,
DocumentResolver
],
authChecker: customAuthChecker
});
const app: express.Application = express();
app.use(cookieParser());
const server: ApolloServer = new ApolloServer({
schema,
formatError,
context: ({ req, res }: GQLRuntimeContext): GQLRuntimeContext => ({
req,
res
})
});
server.applyMiddleware({
app,
path: "/",
cors: { origin: "*", credentials: true }
});
app.listen({ port: process.env.PORT || 4000 }, () => {
console.log(
`🚀 Server ready at http://localhost:${process.env.PORT || 4000}${
server.graphqlPath
}`
);
});
});
client/index.tsx:
import React from "react";
import ReactDOM from "react-dom";
import {
ApolloClient,
ApolloProvider,
NormalizedCacheObject,
InMemoryCache
} from "#apollo/client";
import { createHttpLink } from "apollo-link-http";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
const link: any = createHttpLink({
uri:
/*process.env.NODE_ENV === "production"
? "https://the-notebook-graph.herokuapp.com/graphql"
: */ "http://localhost:4000/graphql",
credentials: "include"
});
const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
link,
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client={client}>
<React.StrictMode>
<App />
</React.StrictMode>
</ApolloProvider>,
document.getElementById("root")
);
serviceWorker.register();
Response headers in ReactJS application
Cookies after receiving the response
I get the following error when I try to use {...credentials: "include"...} in the frontend:
Unhandled Rejection (Error): Failed to fetch