Access a community of over 900,000 Shopify Merchants and Partners and engage in meaningful conversations with your peers.
Hi all,
I've been following the Node and React Shopify app tutorial, and have managed to get basically everything working. I can access the GraphQL API no problem, and have written a few of my own queries and mutations. However, some of the data I need is only available through the Shopify REST API.
I've read through the Shopify OAuth guide and I'm able to retrieve a valid access_token. I'm using that to send a GET request to retrieve a list of collections, but I get a 401 Unauthorized response. I've tried using Postman to test the exact request, and it works fine.
Is there anything special I need to do? Is it possible that some of the GraphQL code from the demo is interfering? I've posted my code below, in case it's helpful.
fetch("https://my-fake-demo-store.myshopify.com/admin/api/2019-04/custom_collections.json", { mode: 'no-cors', method: 'GET', headers: { "X-Shopify-Access-Token": accessToken, "Content-type": "application/json", }, }).then(response => { console.log(response) return response; }).then(json => { console.log(json) });
Solved! Go to the solution
This is an accepted solution.
I managed to figure this out, and wanted to share an update in case anyone runs into the same issue. My problem was that my API calls were coming from the frontend instead of the backend and they were being rejected. My solution was to create my own backend routes and call them from my frontend.
1. Add koa-router to server.js
const Router = require('koa-router'); const router = new Router();
2. Add custom backend routes to make API calls to Shopify:
router.get('/api/:object', async (ctx) => { try { const results = await fetch("https://" + ctx.cookies.get('shopOrigin') + "/admin/api/2019-04/" + ctx.params.object + ".json", { headers: { "X-Shopify-Access-Token": ctx.cookies.get('accessToken'), }, }) .then(response => response.json()) .then(json => { return json; }); ctx.body = { status: 'success', data: results }; } catch (err) { console.log(err) } })
3. Call backend routes from React frontend:
getCollections = () => { var fetchUrl = "/api/custom_collections"; var method = "GET"; fetch(fetchUrl, { method: method }) .then(response => response.json()) .then(json => console.log(json)) }
I'm not sure if this is the most effective solution, so feel free to chime in if you have suggestions to improve this.
Would you be able to provide an X-Request-Id response header after replicating? That would give me a look at the logs to see what might be happening.
Cheers.
Alex | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
@Alex sure thing, I just added the X-Request-Id with a value of "thresher_collections_rest_api_test" to my header. Thanks for your response!
Sorry for the confusion! I meant could you capture a response header which we provide called x-request-id. This will be a unique value I can use to track down logs.
Cheers!
Alex | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
@Alex apologies, wasn't 100% sure what you were asking. I attached an image showing the response I get back from the above fetch request. The response.headers seem to be empty. Does my code above look correct, or am I missing something?
So after doing some more digging, it looks like I can't query the REST API because the requests are coming directly from my app frontend instead of backend. Does that make sense?
Are there examples of how to connect to the REST API via Node JS server + React app?
Edit: it appears that is the case. I'm able to access the REST API from my server.js file with the following code:
fetch("https://" + shop + "/admin/api/2019-04/custom_collections.json", { headers: { "X-Shopify-Access-Token": accessToken, }, }) .then(response => response.json()) .then(json => console.log(json))
I'm not sure how to set this up so I can call the API from my React front end though.
This is an accepted solution.
I managed to figure this out, and wanted to share an update in case anyone runs into the same issue. My problem was that my API calls were coming from the frontend instead of the backend and they were being rejected. My solution was to create my own backend routes and call them from my frontend.
1. Add koa-router to server.js
const Router = require('koa-router'); const router = new Router();
2. Add custom backend routes to make API calls to Shopify:
router.get('/api/:object', async (ctx) => { try { const results = await fetch("https://" + ctx.cookies.get('shopOrigin') + "/admin/api/2019-04/" + ctx.params.object + ".json", { headers: { "X-Shopify-Access-Token": ctx.cookies.get('accessToken'), }, }) .then(response => response.json()) .then(json => { return json; }); ctx.body = { status: 'success', data: results }; } catch (err) { console.log(err) } })
3. Call backend routes from React frontend:
getCollections = () => { var fetchUrl = "/api/custom_collections"; var method = "GET"; fetch(fetchUrl, { method: method }) .then(response => response.json()) .then(json => console.log(json)) }
I'm not sure if this is the most effective solution, so feel free to chime in if you have suggestions to improve this.
Hi Thresher, tried your solution, still getting 401. Where did you add the custom backend route in server.js? I have no backend experience, and I'm really confused...
app.prepare().then(()=>{ const server = new Koa(); server.use(session(server)); server.keys = [SHOPIFY_API_SECRET_KEY]; server.use( createShopifyAuth({ apiKey: SHOPIFY_API_KEY, secret: SHOPIFY_API_SECRET_KEY, scopes: ['read_products', 'read_orders', 'write_products'], afterAuth(ctx) { const { shop, accessToken } = ctx.session; ctx.cookies.set('shopOrigin', shop, { httpOnly: false }); ctx.redirect('/'); }, }), ); server.use(graphQLProxy({version: ApiVersion.April19})); server.use(verifyRequest()); server.use(async (ctx) => { await handle(ctx.req, ctx.res); ctx.respond = false; ctx.res.statusCode = 200; return }); router.get('/api/:object', async (ctx) => { try { const results = await fetch("https://" + ctx.cookies.get('shopOrigin') + "/admin/api/2019-04/" + ctx.params.object + ".json", { headers: { "X-Shopify-Access-Token": ctx.cookies.get('accessToken'), }, }) .then(response => response.json()) .then(json => { return json; }); ctx.body = { status: 'success', data: results }; } catch (err) { console.log(err) } }) server.listen(port, () => { console.log(`> Ready on http://localhost:${port}`); });
@Ireneludi make sure that you include the code from step 1 at the top of server.js. You're missing a line below where you set the shopOrigin cookie:
ctx.cookies.set('accessToken', accessToken);
Let me know if that works!
@Thresher got this error GET https://bd73afd9.ngrok.io/api/orders 404 (Not Found)
and my frontend fetch function is like
const fetchUrl = '/api/orders'; const method = "GET"; fetch(fetchUrl, {method: method,}) .then(response=>response.json()).then(json => console.log(json))
then I tried to change the fetchUrl
const fetchUrl = 'https://getordertest.myshopify.com/api/orders.json'; const method = "GET"; fetch(fetchUrl, {method: method,}) .then(response=>response.json()).then(json => console.log(json))
and I got : Access to fetch at 'https://getordertest.myshopify.com/api/orders.json' from origin 'https://bd73afd9.ngrok.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And I add {mode: 'no-cors'}
and I just got an opaque response...
@Ireneludi ahh, did you install the koa router npm package?
npm install --save koa-router
If not, stop your server, run the above command, and start your server again.
@Thresher Yes, I installed at very first.
QAQ I have been stuck with this API problem for the whole week....
Finally figure it out. The issue was related with chrome security, after I disable chrome cors, everything works fine.
Hello @Thresher, isn't a risky approach to store the accessToken into a cookie ?
Wouldn't be better to:
1) verify request is coming from your domain and avoid extra jobs if not
2) get the stored access token from your DB using the shop name
3) make the Shopify request
4) return data
cheers
I don't think disabling cross origin security in the browser is the solution.
Sorry for digging an old thread but it has part of the solution I need, and it's in the exact same context, query the API from the backend.
const fetchUrl = 'https://getordertest.myshopify.com/api/orders.json'; const method = "GET"; fetch(fetchUrl, {method: method,}) .then(response=>response.json()).then(json => console.log(json))
The code above, is calling the orders api, which is what I need. But I have no idea where to insert this snippet.
Should I wrap it in a server.use? or maybe in a router.get? How do I make it trigger?
Even if its just for a test as soon as the server launches would do for me to get moving. I'm stuck trying to understand how to call the APIs for a long time.
I used in router.get(`insert the code snippet here`)
Basically what I do is, from client, call my backend, and in backend router, call Shopify API
Your best bet is to use the shopify-api-node package so that proper headers are sent for auth without having to manually configure them. All of this should happen on your server and not on the client so as not to expose your accessToken.
@Thresher thanks for this post.
I implemented it almost exactly as you all have outlined but when I call `/api/products` or anything else I get a 404 error. Any idea what's up? Here is the relevant server code. TBH I dont see where the server is using the routes based on this code.
app.prepare().then(() => { const server = new Koa(); server.use(session(server)); server.keys = [SHOPIFY_API_SECRET_KEY]; server.use( createShopifyAuth({ apiKey: SHOPIFY_API_KEY, secret: SHOPIFY_API_SECRET_KEY, scopes: ['read_products'], afterAuth(ctx) { const { shop, accessToken } = ctx.session; console.log('cookie', shop) ctx.cookies.set('shopOrigin', shop, { httpOnly: false }); ctx.cookies.set('accessToken', accessToken, { httpOnly: false }); ctx.redirect('/'); }, }), ); server.use(verifyRequest()); server.use(async (ctx) => { await handle(ctx.req, ctx.res); ctx.respond = false; ctx.res.statusCode = 200; return }); router.get('/api/:object', async (ctx) => { try { const results = await fetch("https://" + ctx.cookies.get('shopOrigin') + "/admin/api/2019-10/" + ctx.params.object + ".json", { headers: { "X-Shopify-Access-Token": ctx.cookies.get('accessToken'), }, }) .then(response => response.json()) .then(json => { console.log('json', json) return json; }); ctx.body = { status: 'success', data: results }; } catch (err) { console.log(err) } }) server.listen(port, () => { console.log(`> Ready on http://localhost:${port}`); }); });
User | RANK |
---|---|
6 | |
5 | |
5 | |
4 | |
4 |