Using loopback4 and graphQL together - node.js

import {Lb4Application} from './application';
import {ApplicationConfig} from '#loopback/core';
import graphqlHTTP from 'express-graphql';
import {createGraphQLSchema} from 'openapi-to-graphql';
import {Oas3} from 'openapi-to-graphql/lib/types/oas3';
export {Lb4Application};
export async function main(options: ApplicationConfig = {}) {
const app = new Lb4Application(options);
await app.boot();
await app.start();
const url: string = <string>app.restServer.url;
console.log(`REST API Server is running at ${url}`);
const graphqlPath = '/graphql';
const oas: Oas3 = <Oas3>(<unknown>app.restServer.getApiSpec());
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: false,
baseUrl: url,
headers: {
'X-Origin': 'GraphQL',
},
tokenJSONpath: '$.jwt',
});
const handler: graphqlHTTP.Middleware = graphqlHTTP(
(request, response, graphQLParams) => ({
schema,
pretty: true,
graphiql: true,
context: {jwt: getJwt(request)},
}),
);
// Get the jwt from the Authorization header and place in context.jwt, which is then referenced in tokenJSONpath
function getJwt(req: any) {
if (req.headers && req.headers.authorization) {
return req.headers.authorization.replace(/^Bearer /, '');
}
}
app.mountExpressRouter(graphqlPath, handler);
console.log(`Graphql API: ${url}${graphqlPath}`);
return app;
}
I have taken this code from this github issue, and I still cannot seem to get it to run.
The error is get is
Error: Invalid specification provided
When i just use an express server, and run npx openapi-to-graphql --port=3001 http://localhost:3000/openapi.json --fillEmptyResponses The graphql is served correctly.
I need to get the example code running, as it seems to be the only way to pass JWT token headers correctly when using loopback4 and graphql together

This is how i solved it for anyone that needs help:
/* eslint-disable #typescript-eslint/no-explicit-any */
import {Lb4GraphqlPocApplication} from './application';
import {ApplicationConfig} from '#loopback/core';
const graphqlHTTP = require('express-graphql');
const {createGraphQLSchema} = require('openapi-to-graphql');
const fetch = require('node-fetch');
export {Lb4GraphqlPocApplication};
export async function main(options: ApplicationConfig = {}) {
console.log('hello world!')
const app = new Lb4GraphqlPocApplication(options);
await app.boot();
await app.start();
const url = app.restServer.url;
const graphqlPath = '/graphql';
console.log(`REST Server is running at ${url}`);
console.log(`Try ${url}/ping`);
// replace with process.env.{active-environment} once deployments setup
const openApiSchema = 'http://localhost:3000/openapi.json';
const oas = await fetch(openApiSchema)
.then((res: any) => {
console.log(`JSON schema loaded successfully from ${openApiSchema}`);
return res.json();
})
.catch((err: any) => {
console.error('ERROR: ', err);
throw err;
});
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: true,
baseUrl: url,
headers: {
'X-Origin': 'GraphQL',
},
tokenJSONpath: '$.jwt',
});
const handler = graphqlHTTP(
(request: any, response: any, graphQLParams: any) => ({
schema,
pretty: true,
graphiql: true,
context: {jwt: getJwt(request)},
}),
);
// Get the jwt from the Authorization header and place in context.jwt, which is then referenced in tokenJSONpath
function getJwt(req: any) {
if (req.headers && req.headers.authorization) {
return req.headers.authorization.replace(/^Bearer /, '');
}
}
app.mountExpressRouter(graphqlPath, handler);
console.log(`Graphql API: ${url}${graphqlPath}`);
return app;
}

Related

I can't get a data from backend/data file

please can someone help me to get the data of products from the backend/data by using Axios.get method to retrieve with the helping of redux store.I do all the steps to get the contain of products but the console send me the initial state empty object so there are all my files :
productActions.js :
import Axios from 'axios';
const PRODUCT_LIST_REQUEST = "PRODUCT_LIST_REQUEST";
const PRODUCT_LIST_SUCCESS = "PRODUCT_LIST_REQUEST";
const PRODUCT_LIST_FAIL = "PRODUCT_LIST_FAIL";
export const listProducts =
() => async (dispatch) => {
dispatch({
type: PRODUCT_LIST_REQUEST
});
try {
const {data} = await Axios.get('/api/products');
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
} catch(error) {
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
ProductReducers.js:
const {PRODUCT_LIST_REQUEST} = require('../actions/productActions.js');
const {PRODUCT_LIST_SUCCESS} = require('../actions/productActions.js');
const {PRODUCT_LIST_FAIL} = require('../actions/productActions.js')
export const productListReducer = (state = {loading: true, products: {}}, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return ({loading: false, products: action.payload});
case PRODUCT_LIST_FAIL:
return ({loading: false, error: action.payload});
default:
return state;
}
};
this store.js:
import {createStore, compose, applyMiddleware, combineReducers} from 'redux';
import thunk from 'redux-thunk';
import {productListReducer} from './reducers/productReducers';
const initialState = {};
const reducer = combineReducers({
productList: productListReducer
})
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
server.js :
import express from 'express';
import {data} from './data.js';
const app = express();
app.get('/api/products', (req, res) => {
res.send(data.categories)
});
app.get('/', (req, res) => {
res.send('server is ready')
});
const port = process.env.port || 5001;
app.listen(port, ()=> {
console.log(`server is runing at http://localhost:${port}`);
})
I am not sure but, I think you must return in your switch cases like this:
return {...state, loading: false, products: action.payload};
Just try all of them like this. And try to console logging the data you get from axios.get. I am pretty sure that you must use JSON.parse(data) before setting it to your payload.

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.

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.

Trouble connecting to Graphql subscriptions?

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.

How to retrieve products from Shopify API using CLI

Run the Shopify CLI but still is very difficult to do anything.
Would like to retrieve 10 articles from Shopify between those id's but I am getting an error that URLSearchParams is not recognized.
Pretty sure that is something really easy.
The full code below is where I am at the moment.
index.js
import { Heading, Page } from "#shopify/polaris";
const Index = () => (
<Page
title='Trustpilot Aggreggation Uploader'
primaryAction={{
content: 'Update Metafields',
onAction: () =>
{
console.log('appliying products');
var limit = 10;
var sinceId = '0,921728736';
const product = async (limit, sinceId) => {
const res = await fetch(
"/products?" +
new URLSearchParams({
limit,
since_id: sinceId,
})
);
return await res.json();
};
}
}}
/>
);
export default Index;
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";
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:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
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;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
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}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.get("/products", async (ctx) => {
try {
const { shop, accessToken } = ctx.session;
const res = await fetch( 'https://${SHOPIFY_API_KEY}:${accessToken}#${shop}/admin/api/2020-10/products.json?${new URLSearchParams(
ctx.request.query
)}'
);
ctx.body = await res.json();
ctx.status = 200;
} catch (error) {
console.log('Failed to process products: ${error}');
}
});
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", verifyRequest(), handleRequest); // Everything else must have sessions
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
Please beware that URLSearchParams class was added as a Global Object only on Node v10.
On older versions of Node you have to import it first:
import { URLSearchParams } from 'url';
global.URLSearchParams = URLSearchParams

Resources