How do I debug server-side errors on MERN? - node.js

I have this front-end code:
export const CreatePage = () => {
const auth = useContext(AuthContext)
const {request} = useHttp()
const [content, setContent] = useState('')
const [title, setTitle] = useState('')
const [lead, setLead] = useState('')
useEffect(() => {
window.M.updateTextFields()
},[])
const postHandler = async () => {
try {
const data = await request('/api/post/generate', 'POST', {title: title, lead: lead, content: content}, {
Authorization: `Bearer ${auth.token}`
})
console.log(data)
} catch (e) {}
}
And this back-end code:
router.post('/generate', auth, async (req, res) => {
try {
const baseURL = config.get('baseURL')
const {title, lead, content} = req.body
// if (!title || !lead || !content) {
// return res.status(422).json({error: 'Please, input ALL fields'})
// }
const Post = new Post({
title, lead, content, owner: req.body.user.userId // req.user.userId
})
await Post.save()
res.status(201).json({Post})
} catch (e) {
res.status(500).json({message: 'Something went wrong'})
}})
I've tried a lot of things, but I still get this error. I know this is a server-side error, but that's all I have been able to figure out.
P.S. If there are any questions about the code, I will add it later.
UPD: By the way, could it be a problem's reason? Console log:
[1] Proxy error: Could not proxy request /api/post/generate from localhost:3000 to http://localhost:5000.

Probably, it's because of cors, you just can't send request from different url's. Try to install cors and configure it:
const cors = require("cors");
app.use("/", require('./src/routes'));
app.use(cors({
origin: '*'
}))

Related

Clickjacking Issue In Shopify Nextjs App?

I am facing an issue regarding clickjacking prevention. I have implemented authentication and custom headers that contain headers that are requested by Shopify.
ctx.set('Content-Security-Policy', `frame-ancestors https://${ctx.query.shop} https://admin.shopify.com`);
ctx.res.setHeader(
"Content-Security-Policy",
`frame-ancestors https://${ctx.query.shop} https://admin.shopify.com;`
)
local environment headers:
[1]: https://i.stack.imgur.com/XBkW4.png
Production Environment response headers:
[2]: https://i.stack.imgur.com/YkfnA.png
It is working fine in my development environment but is not working in production.
my server.js file
/* eslint-disable #typescript-eslint/no-var-requires */
const dotenv = require('dotenv')
const Koa = require('koa')
const next = require('next')
const { default: createShopifyAuth } = require('#shopify/koa-shopify-auth')
const { verifyRequest } = require('#shopify/koa-shopify-auth')
const { default: Shopify, ApiVersion } = require('#shopify/shopify-api')
const { withSentry } = require('#sentry/nextjs')
const proxy = require('koa-proxy')
const Router = require('#koa/router')
const { checkForNewThemeSupport } = require('./checkfordawntheme')
dotenv.config()
const { PORT } = process.env
const { NODE_ENV } = process.env
const port = parseInt(PORT, 10) || 3000
const dev = NODE_ENV !== 'production'
const app = next({ dev })
const handle = withSentry(app.getRequestHandler())
if (!process.env.NEXT_PUBLIC_SHOPIFY_API_KEY || !process.env.SHOPIFY_API_SECRET_KEY) {
console.error('Missing api keys')
}
const SCOPES = [
'read_products',
'write_products',
'unauthenticated_read_product_listings',
'read_orders',
'read_script_tags',
'write_script_tags',
'read_themes'
]
Shopify.Context.initialize({
API_KEY: process.env.NEXT_PUBLIC_SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET_KEY,
SCOPES,
HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/https:\/\//, ''),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage()
})
// TODO replace this with something serious
const ACTIVE_SHOPIFY_SHOPS = {}
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res)
ctx.set('Content-Security-Policy', `frame-ancestors https://${ctx.query.shop} https://admin.shopify.com`);
ctx.res.setHeader(
"Content-Security-Policy",
`frame-ancestors https://${ctx.query.shop} https://admin.shopify.com;`
)
console.log("************* frame ancestor ********* ")
ctx.respond = false
ctx.res.statusCode = 200
}
app.prepare().then(() => {
const server = new Koa()
const router = new Router()
server.keys = [Shopify.Context.API_SECRET_KEY]
// online auth for app/user request
server.use(createShopifyAuth({
accessMode: 'online',
afterAuth(ctx) {
// Online access mode access token and shop available in ctx.state.shopify
const { shop } = ctx.state.shopify
const { host } = ctx.query
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`)
}
}))
// offline auth for background tasks
server.use(createShopifyAuth({
accessMode: 'offline',
prefix: '/offline',
async afterAuth(ctx) {
const { shop, accessToken } = ctx.state.shopify
ACTIVE_SHOPIFY_SHOPS[shop] = true
// APP_UNINSTALLED webhook to make sure merchants go through OAuth if they reinstall it
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: '/webhooks',
topic: 'APP_UNINSTALLED',
webhookHandler: async (topic, shop) => delete ACTIVE_SHOPIFY_SHOPS[shop]
})
if (!response.success) {
console.error(`Failed to register APP_UNINSTALLED webhook: ${response.result}`)
}
ctx.redirect(`/auth?shop=${shop}`)
}
}))
router.get('/', async (ctx) => {
const { shop } = ctx.query
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/offline/auth?shop=${shop}`)
} else {
await handleRequest(ctx)
}
})
router.get('(/_next/static/.*)', handleRequest)
router.get('/_next/webpack-hmr', handleRequest)
router.get('/(.*).js', handleRequest)
router.get('/login', verifyRequest(), handleRequest)
router.get('/register', verifyRequest(), handleRequest)
router.post(
'/webhooks',
async (ctx) => {
try {
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res)
} catch (error) {
console.error(`Failed to process webhook: ${error}`)
}
}
)
router.post(
'/graphql',
verifyRequest({ returnHeader: true }),
async (ctx) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res)
}
)
router.get(
'/checkfor20',
verifyRequest(),
async (ctx) => {
try {
const hasCartAppBlock = await checkForNewThemeSupport(ctx)
ctx.body = JSON.stringify({ hasCartAppBlock })
ctx.status = 200
} catch (error) {
console.error(`Failed to check for theme: ${error}`)
}
}
)
server.use(router.allowedMethods())
server.use(router.routes())
server.use(proxy({
host: process.env.NEXT_PUBLIC_TREEPOINTS_API_URL,
match: /^\/********-api\/\w*/,
map: (path) => path?.split(process.env.NEXT_PUBLIC_TREEPOINTS_API_PROXY_URL)?.[1] || path
}))
// eslint-disable-next-line no-console
server.listen(port, () => console.log(`> Ready on http://localhost:${port}`))
})
App development framework: nextjs
deployment server: Heroku
Any help would be appreciated.

how do I get req.query nextjs api?

I have this API, and I am trying to get the query I pass in the URL (such as products?page=1&limit=10) but I keep getting an empty object
const handler = nc().use(Cors());
handler.get(async (req, res) => {
await db.connect();
console.log(req.query)
const products = await Product.paginate({}, { page: 1, limit: 30 });
res.send(products);
});
export default handler;
console.log(req.query.page, req.query.limit)
You can pass any props to your api
const desiredId = 'xyz'
'https://yourAPI/getSomething?any_name_you_want='+ desiredId
//in your api
console.log(req.query.any_name_you_want)
Well, I have somehow managed to do it even though it probably isn't the best way. It works though and my main focus is not the backend, so I guess it's good enough
In redux toolkit query:
endpoints: (builder) => ({
getAllProducts: builder.query({
query: (page = 1) => `${baseUrl}?page=${page}`,
}),
In my index.js
const router = useRouter();
const [page, setPage] = useState(1);
const { data, isLoading, error } = useGetAllProductsQuery(page);
const handlePaginationChange = (e, value) => {
e.preventDefault();
setPage(value);
router.push(`/products?page=${value}`);
};
and my API route:
const handler = nc().use(Cors());
handler.get(async (req, res) => {
await db.connect();
const products = await Product.paginate({}, { page: req.query.page, limit: 9 });
res.send(products);
});

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

Fetch API failed to Fetch during authentication, alongside CORS error

I have a button that lauches a fetch to my API that uses KOA and JWT. The javascript for the fetch initiated on click is:
<script>
function loginButton(user, pass) {
fetch('http://localhost:5454/api/login', {
method: "post",
headers: {
'Content-Type': "application/json"
},
body: JSON.stringify({
username: user,
password: pass
})
})
.then( (response) => {
console.log("Success")
})
.catch(e => console.log(e));
}
</script>
The code for my Authentication is:
router.post(`${BASE_URL}/login`, async (ctx) => {
const reqUsername = ctx.request.body.username
const reqPassword = ctx.request.body.password
const unauthorized = (ctx) => {
ctx.status = 401
ctx.body = {
error: 'Invalid username or password'
}
}
let attemptingUser
try {
attemptingUser = await Employee.findOne({ where: { username: reqUsername }})
if (attemptingUser != null && attemptingUser.password === reqPassword) {
ctx.status = 200
ctx.body = {
username: attemptingUser.username,
given_name: attemptingUser.given_name,
role: attemptingUser.role,
created_at: attemptingUser.createdAt,
updated_at: attemptingUser.updatedAt,
}
const token = jwt.sign({ username: attemptingUser.username, role: attemptingUser.role }, SECRET)
ctx.set("X-Auth", token)
} else {
unauthorized(ctx)
}
} catch(err) {
console.error(err)
console.error(`Failed to find username: ${reqUsername}`)
unauthorized(ctx)
}
})
The code for my KOA initiation is:
require('dotenv').config()
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const baseRoutes = require('./routes')
const cors = require('#koa/cors');
const PORT = process.env.PORT || 8080
const app = new Koa()
app.use(bodyParser())
app.use(baseRoutes.routes())
app.use(cors());
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`)
})
Im using Port 8080 for my http-server and port 5454 for my npm server. I am getting a Failed to Fetch in the catch of the Fetch, as well as a CORS error related to not having a Access-Control-Allow-Origin header in the response header. I've tried a couple things and am ready to have a new set of eyes look at it, any tips?
Edit: I am successfully receiving the token in the X-Auth header, but for some reason it’s still throwing errors and I’d like to get them resolved before it spirals out of control.

Nock and google maps client

I'm trying to test a service that uses the #google/maps client for getting directions data.
Here is a simplified version of the service:
'use strict'
const dotenv = require('dotenv')
const GoogleMaps = require('#google/maps')
dotenv.config()
const {GOOGLE_API_KEY: key} = process.env
const client = GoogleMaps.createClient({key, Promise})
const getData = exports.getData = async function({origin, destination}) {
try {
const options = {
origin,
destination,
mode: 'transit',
transit_mode: ['bus', 'rail']
}
const res = await client.directions(options).asPromise()
return res
} catch (err) {
throw err
}
}
And here is a test file to show the case:
'use strict'
const dotenv = require('dotenv')
const nock = require('nock')
const gdService = require('./gd.service')
dotenv.config()
const {GOOGLE_API_KEY: key} = process.env
const response = {json: {name: 'custom'}}
const origin = {lat: 51.5187516, lng: -0.0836314}
const destination = {lat: 51.52018, lng: -0.0998361}
const opts = {origin, destination}
nock('https://maps.googleapis.com')
.get('/maps/api/directions/json')
.query({
origin: `${origin.lat},${origin.lng}`,
destination: `${destination.lat},${destination.lng}`,
mode: 'transit',
transit_mode: 'bus|rail',
key
})
.reply(200, response)
gdService.getData(opts)
.then(res => {
console.log(res.json) // it's undefined!
})
.catch(err => {
console.error(err)
})
What I expect is to get the defined response as a response of the service method invocation. But I get undefined. Why is that?
After reading the source of #google/maps client, I figured out that I had to provide nock with the following reply header:
...
nock('https://maps.googleapis.com')
.defaultReplyHeaders({
'Content-Type': 'application/json; charset=UTF-8'
})
.get('/maps/api/directions/json')
...

Resources