Increase body limit with nestjs & fastify - nestjs

My main.ts looks like this :
import { NestFactory } from '#nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '#nestjs/platform-fastify';
import { Logger } from 'nestjs-pino';
import { processRequest } from 'graphql-upload';
import { AppModule } from './app.module';
async function bootstrap() {
const adapter = new FastifyAdapter();
const fastifyInstance = adapter.getInstance();
fastifyInstance.addContentTypeParser('multipart', (request, done) => {
request.isMultipart = true;
done();
});
fastifyInstance.addHook('preValidation', async (request: any, reply) => {
if (!request.raw.isMultipart) {
return;
}
request.body = await processRequest(request.raw, reply.raw);
});
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
adapter,
{ bufferLogs: true },
);
app.useLogger(app.get(Logger));
app.enableCors();
await app.listen(parseInt(process.env.SERVER_PORT || '3000', 10), '0.0.0.0');
}
bootstrap();
According to the fastify doc the body limit is 1MiB by default, however I want it to be larger. So I tried like this :
const adapter = new FastifyAdapter({ bodyLimit: 124857600 }); but I still get the same problem with my payload being too large.

Try to add this when you are creating the app
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ bodyLimit: 10048576 }),

Related

Testing firestore with jest not working as expected

I have the following service which I just need to test that whenever an user is not validated, we throw an error
async payoutPendingRewards(userId: string): Promise<Reward[]> {
const db = admin.firestore();
const userRef = db.collection('UserProfiles').doc(userId);
const doc = await userRef.get();
if (!doc.data() || !doc.data().validated) {
throw Error('User profile has not been validated');
}
const userBankAccount = await this.getUserPayoutAccount(userId);
if (userBankAccount) {
const pendingRewards = await this.getPendingRewards(userId);
const amount = pendingRewards.reduce(
(amount, reward) => amount + reward.amount,
0,
);
if (amount > 0) {
try {
const payoutId = await this.createPayout(
userBankAccount.accountType,
userBankAccount.accountId,
amount,
);
if (payoutId) {
// TODO: add payout ID to Reward
return await this.updatePendingRewards(pendingRewards);
}
} catch (e) {
throw new HttpException(
{
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'There was an error: Payout Not Created',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
When trying to test it using jest, I can't get to mock the firebase-admin functions. I get a firebase error when trying to do it
import { Test, TestingModule } from '#nestjs/testing';
import { HttpModule, HttpService } from '#nestjs/axios';
import { describe, expect, beforeEach, it, jest } from '#jest/globals';
import { INestApplication, forwardRef } from '#nestjs/common';
import { SecretManagerServiceClient } from '#google-cloud/secret-manager';
import * as request from 'supertest';
import { RewardsService } from '../src/rewards/rewards.service';
import { RewardsController } from '../src/rewards/rewards.controller';
import { UsersModule } from '../src/users/users.module';
import { FirebaseAuthGuard } from '../src/auth/firebase-auth.guard';
import { HttpExceptionFilter } from '../src/exceptions/http-exception.filter';
import { TransformInterceptor } from '../src/interceptors/transform.interceptor';
import { firestore } from './setupFirestoreTests';
describe('RewardsService', () => {
let service: RewardsService;
let httpService: HttpService;
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [RewardsService],
controllers: [RewardsController],
imports: [
FirebaseAuthGuard,
HttpExceptionFilter,
TransformInterceptor,
HttpModule,
forwardRef(() => UsersModule),
],
}).compile();
app = module.createNestApplication();
service = module.get<RewardsService>(RewardsService);
httpService = module.get<HttpService>(HttpService);
await app.init();
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('makes a call to firestore', async () => {
service.payoutPendingRewards('123');
expect(firestore().doc().get).toHaveBeenCalledWith('123');
});
});
I need help just mocking the firebase functions. Already tried by creating mock functions on a different file but it didn't work

Tests haphazardly fail when using graphql-request and MSW

I am trying to set up tests to test a Remix loader and noticed that the request function from graphql-request haphazardly fails when using MSW. If I replace this and use a simple fetch for the requests the tests pass.
Is there any configuration I need to change? I have created a sample repo that demonstrates the bug. The bug gets worse the more tests you have that are using the same mocked request.
Sample repo: https://github.com/charklewis/sb9or3
Here is a summary of the code I am using:
//modules/database.server
import { GraphQLClient } from "graphql-request";
const client = new GraphQLClient("http://some-graphql-api.com/api/graphql", {});
export const fetchQuery = async (query: any, variables: any) => {
try {
const response = await client.request(query, variables || {});
return response;
} catch (error) {
return {};
}
};
//routes/index
import type { LoaderFunction } from "remix";
import { json } from "remix";
import { gql } from "graphql-request";
import { fetchQuery } from "~/modules/database.server";
export const loader: LoaderFunction = async () => {
const query = gql`
query MyQuery {
demoQuery {
value
}
}
`;
const response = await fetchQuery(query, {});
return json({ value: response.demoQuery.value });
};
//routes/__tests__/index
import { graphql } from "msw";
import { setupServer } from "msw/node";
import { loader } from "~/routes/index";
const createServer = (handlers: any[] = []) => {
const server = setupServer(...handlers);
beforeAll(() => server.listen({ onUnhandledRequest: "bypass" }));
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
return server;
};
const createDemoQueryHandler = ({ value = true } = {}) => {
return graphql.query("MyQuery", (req, res, ctx) => {
return res(ctx.data({ demoQuery: { value } }));
});
};
createServer([createDemoQueryHandler()]);
test("the loader returns data (round 1)", async () => {
const response = await loader({
request: new Request("/", { method: "GET" }),
params: {},
context: {},
});
const data = await response.json();
expect(data.value).toBe(true);
});
My vitest configuration is:
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./app/test-utils.ts",
testTimeout: 20000
}
});

Call to /api/auth/session gives ECONNRESET - next-auth

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.

NestJs Testing create a single instance of app across all tests

I am running into the issue Error querying the database: db error: FATAL: sorry, too many clients already and I am convinced it is because a new instance of the app is being instantiated for every test suite. I have attempted to break the app creation out into a helper file, and that file looks as follows
import { INestApplication } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { AppModule } from '../../src/app.module';
import { PrismaService } from '../../src/prisma.service';
declare global {
var app: INestApplication | undefined;
}
export const getApp = async () => {
if (global.app) {
return global.app;
}
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
providers: [PrismaService],
}).compile();
const app = moduleFixture.createNestApplication();
await app.init();
global.app = app;
return app;
};
This however does not work, when I add console logs, I can see that app is being instantiated for every test suite.
This is how my typical before hook looks
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
I had the same issue and solved it by using Jest's globalSetup and globalTeardown options. I create one instance of the app and seed the DB before running the tests, and destroy it and run the teardown when they've finished. I use a global variable to reference the app instance.
In my jest-e2e.json:
{
...
"globalSetup": "<rootDir>/test/test-setup.ts",
"globalTeardown": "<rootDir>/test/test-teardown.ts"
}
test-setup.ts:
import * as fs from 'fs';
import { FastifyAdapter, NestFastifyApplication } from '#nestjs/platform-fastify';
import { Test } from '#nestjs/testing';
import { AppModule } from './../src/app.module';
import { WriteDataSource } from './../src/database/database.module';
module.exports = async () => {
const moduleRef = await Test.createTestingModule({
imports: [ AppModule ]
})
.compile();
global.app = moduleRef.createNestApplication<NestFastifyApplication>(
new FastifyAdapter()
);
await global.app.init();
await global.app.getHttpAdapter().getInstance().ready();
const seedQuery = fs.readFileSync(__dirname + '/scripts/database-seed.sql', { encoding: 'utf-8' });
await WriteDataSource.manager.query(seedQuery);
};
test-teardown.ts:
import * as fs from 'fs';
import { WriteDataSource } from '../src/database/database.module';
module.exports = async () => {
const teardownQuery = fs.readFileSync(__dirname + '/scripts/database-teardown.sql', { encoding: 'utf-8' }).replace(/\n/g, '');
await WriteDataSource.query(teardownQuery);
await WriteDataSource.destroy();
await global.app.close();
};
I solved it by exporting the server instance and everything else i needed:
let server: Server
let app: INestApplication
let testService: TestService
export const initServer = async () => {
const module = Test.createTestingModule({
imports: [AppModule, TestModule]
})
const testModule = await module.compile()
app = testModule.createNestApplication()
app.useGlobalPipes(
new ValidationPipe({
whitelist: true
})
)
server = app.getHttpServer()
testService = app.get(TestService)
await app.init()
}
export { app, server, testService }
Here i exported the function to clear all my databases to use where needed:
export const clearAllDatabases = async () => {
models.map(async (model: any) => {
await model.destroy({
where: {},
truncate: true,
cascade: true
})
})
}
import { app, initServer } from '#tests/resources/config/test-server'
import { clearAllDatabases } from '#tests/resources/config/clear-databases'
export const setupTestData = async () => {
await initServer()
await clearAllDatabases()
await userRegister()
await app.close()
}

Shopify Apps with NodeJS problem "Error: Failed to parse session token '******' jwt expired"

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.

Resources