Giter Club home page Giter Club logo

shopify-express's Introduction

Deprecation

This project is deprecated. This means Shopify will not be maintaining it going forward. If you are interested in building a Shopify app using first party tools then check out our other libraries:

These are all used internally and written against technologies we use for our own applications. Of course, if you wish to continue using Express, feel free to fork this codebase and continue it as you wish.

shopify-express

A small set of abstractions that will help you quickly build an Express.js app that consumes the Shopify API.

Example

const express = require('express');
const shopifyExpress = require('@shopify/shopify-express');
const session = require('express-session');

const app = express();

const {
  SHOPIFY_APP_KEY,
  SHOPIFY_APP_HOST,
  SHOPIFY_APP_SECRET,
  NODE_ENV,
} = process.env;

// session is necessary for api proxy and auth verification
app.use(session({secret: SHOPIFY_APP_SECRET}));

const {routes, withShop} = shopifyExpress({
  host: SHOPIFY_APP_HOST,
  apiKey: SHOPIFY_APP_KEY,
  secret: SHOPIFY_APP_SECRET,
  scope: ['write_orders, write_products'],
  accessMode: 'offline',
  afterAuth(request, response) {
    const { session: { accessToken, shop } } = request;
    // install webhooks or hook into your own app here
    return response.redirect('/');
  },
});

// mounts '/auth' and '/api' off of '/shopify'
app.use('/shopify', routes);

// shields myAppMiddleware from being accessed without session
app.use('/myApp', withShop({authBaseUrl: '/shopify'}), myAppMiddleware)

Shopify routes

  const {routes} = shopifyExpress(config);
  app.use('/', routes);

Provides mountable routes for authentication and API proxying. The authentication endpoint also handles shop session storage using a configurable storage strategy (defaults to SQLite).

/auth/shopify

Serves a login endpoint so merchants can access your app with a shop session.

/api

Proxies requests to the api for the currently logged in shop. Useful to securely use api endpoints from a client application without having to worry about CORS.

shopStore

shopifyExpress's config takes an optional shopStore key, You can use this to define a strategy for how the module will store your persistent data for user sessions.

Strategies

By default the package comes with MemoryStrategy, RedisStrategy, and SqliteStrategy. If none are specified, the default is MemoryStrategy.

MemoryStrategy

Simple javascript object based memory store for development purposes. Do not use this in production!

const shopifyExpress = require('@shopify/shopify-express');
const {MemoryStrategy} = require('@shopify/shopify-express/strategies');

const shopify = shopifyExpress({
  shopStore: new MemoryStrategy(redisConfig),
  ...restOfConfig,
});

RedisStrategy

Uses redis under the hood, so you can pass it any configuration that's valid for the library.

const shopifyExpress = require('@shopify/shopify-express');
const {RedisStrategy} = require('@shopify/shopify-express/strategies');

const redisConfig = {
  // your config here
};

const shopify = shopifyExpress({
  shopStore: new RedisStrategy(redisConfig),
  ...restOfConfig,
});

SQLStrategy

Uses knex under the hood, so you can pass it any configuration that's valid for the library. By default it uses sqlite3 so you'll need to run yarn add sqlite3 to use it. Knex also supports postgreSQL and mySQL.

const shopifyExpress = require('@shopify/shopify-express');
const {SQLStrategy} = require('@shopify/shopify-express/strategies');

// uses sqlite3 if no settings are specified
const knexConfig = {
  // your config here
};

const shopify = shopifyExpress({
  shopStore: new SQLStrategy(knexConfig),
  ...restOfConfig,
});

SQLStrategy expects a table named shops with a primary key id, and string fields for shopify_domain and access_token. It's recommended you index shopify_domain since it is used to look up tokens.

If you do not have a table already created for your store, you can generate one with new SQLStrategy(myConfig).initialize(). This returns a promise so you can finish setting up your app after it if you like, but we suggest you make a separate db initialization script, or keep track of your schema yourself.

Custom Strategy

shopifyExpress accepts any javascript class matching the following interface:

  class Strategy {
    // shop refers to the shop's domain name
    getShop({ shop }): Promise<{accessToken: string}>
    // shop refers to the shop's domain name
    storeShop({ shop, accessToken }): Promise<{accessToken: string}>
  }

Helper middleware

const {middleware: {withShop, withWebhook}} = shopifyExpress(config);

withShop

app.use('/someProtectedPath', withShop({authBaseUrl: '/shopify'}), someHandler);

Express middleware that validates the presence of your shop session. The parameter you pass to it should match the base URL for where you've mounted the shopify routes.

withWebhook

app.use('/someProtectedPath', withWebhook, someHandler);

Express middleware that validates the presence of a valid HMAC signature to allow webhook requests from shopify to your app.

Example app

You can look at shopify-node-app for a complete working example.

Gotchas

Install route

For the moment the app expects you to mount your install route at /install. See shopify-node-app for details.

Express Session

This library expects express-session or a compatible library to be installed and set up for much of it's functionality. Api Proxy and auth verification functions won't work without something putting a session key on request.

It is possible to use auth without a session key on your request, but not recommended.

Body Parser

This library handles body parsing on it's own for webhooks. If you're using webhooks you should make sure to follow express best-practices by only adding your body parsing middleware to specific routes that need it.

Good

  app.use('/some-route', bodyParser.json(), myHandler);

  app.use('/webhook', withWebhook(myWebhookHandler));
  app.use('/', shopifyExpress.routes);

Bad

  app.use(bodyParser.json());
  app.use('/some-route', myHandler);

  app.use('/webhook', withWebhook(myWebhookHandler));
  app.use('/', shopifyExpress.routes);

Contributing

Contributions are welcome. Please refer to the contributing guide for more details.

shopify-express's People

Contributors

awaselnuk avatar extrablind avatar jamiemtdwyer avatar kaelig avatar kevinhughes27 avatar marekweb avatar marutypes avatar ragalie avatar tomsouthall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shopify-express's Issues

SHOPIFY_APP_SECRET undefined for withWebhook middleware

Hello;

I opened an issue on shopify-node-app that seems related to shopify-express.
Shopify/shopify-node-app#70

Quick summary: on a clean clone of shopify-node-app, using ngrok, everything's fine except from webhooks, which return 404 error when tested (POST request from store is fine and working). On further inspection results that withWebhook has lost access to SHOPIFY_APP_SECRET constant.

ReferenceError: SHOPIFY_APP_SECRET is not defined
    at withWebhook (/Users/idiazroncero/Localdev/otros/shopify-node-app/node_modules/@shopify/shopify-express/middleware/webhooks.js:9:17)

So the route skips and fallbacks to error handlers.

The code at the beginning hasn't been touched, is the original from shopify-node-app. The .env is filled with the correct values and, as a proof of it, auth and everything else is working.

const {
  SHOPIFY_APP_KEY,
  SHOPIFY_APP_HOST,
  SHOPIFY_APP_SECRET,
  NODE_ENV,
} = process.env;

Move to node 8.9 LTS

Node 8.9 has entered LTS, so we should move to that version in our package.json asap.

We can also start using async / await freely.

Wrong callback_url is being generated when the configured route prefix isn't "/"

My setup:

function configureShopifyExpress(app: INestApplication) {
    const shopModule: INestApplicationContext = app.select<ShopModule>(ShopModule);
    const strategy: ShopStoreStrategyInterface = shopModule.get(ShopStoreTypeOrmService);
    const {routes}: {routes: any[]} = shopifyExpress({
        host: process.env.APP_HOST,
        apiKey: process.env.SHOPIFY_API_KEY,
        secret: process.env.SHOPIFY_API_SECRET,
        scope: ['write_orders, read_orders'],
        shopStore: strategy,
        afterAuth(request, response) {
            const { session: { accessToken, shop } } = request;
            console.log(accessToken, shop);
            return response.redirect('/');
        },
    });
    app.use('/shopify/', routes);
}

I map the routes to /shopify/ because I don't want this module to mess with my existing /api/ endpoints.
The install works halfway and then fails after getting the authentication token from shopify.

Generated URL:
http://my.domain/auth/shopify/callback?code=xxxxx&hmac=xxxxx&shop=xxxxx.myshopify.com&timestamp=1518025765

Expected URL:
http://my.domain/shopify/auth/shopify/callback?code=xxxxx&hmac=xxxxx&shop=xxxxx.myshopify.com&timestamp=1518025765

ReferenceError: fetch is not defined

During the oAuth process an error occurs because you are using "fetch" but not importing it before you're using it.

Platform info:

ts-node v4.0.2
node v9.5.0
typescript v2.7.1

I'm using the latest npm release.

ReferenceError: fetch is not defined
    at router.get (/home/benni/myproject/node_modules/@shopify/shopify-express/routes/shopifyAuth.js:72:5)
    at Layer.handle [as handle_request] (/home/benni/myproject/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/benni/myproject/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/home/benni/myproject/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/home/benni/myproject/node_modules/express/lib/router/layer.js:95:5)
    at /home/benni/myproject/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/home/benni/myproject/node_modules/express/lib/router/index.js:335:12)
    at next (/home/benni/myproject/node_modules/express/lib/router/index.js:275:10)
    at Function.handle (/home/benni/myproject/node_modules/express/lib/router/index.js:174:3)
    at router (/home/benni/myproject/node_modules/express/lib/router/index.js:47:12)
    at Layer.handle [as handle_request] (/home/benni/myproject/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/home/benni/myproject/node_modules/express/lib/router/index.js:317:13)
    at /home/benni/myproject/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/home/benni/myproject/node_modules/express/lib/router/index.js:335:12)
    at next (/home/benni/myproject/node_modules/express/lib/router/index.js:275:10)
    at Function.handle (/home/benni/myproject/node_modules/express/lib/router/index.js:174:3)

I fixed it locally by doing this:

global.fetch = require('node-fetch');

But that is obviously a terrible solution.

Add CI

We should set up CI for this project once there are a non-zero number of tests

Allow custom storage strategies

We should allow users to pass in their own storage strategies, and make ours available as classes they can instantiate and pass in if needed.

Incompatibility with AWS Lambda

AWS Lambda is currently on node 6.10, and is a very good candidate for hosting shopify apps. However the oldest supported node version for shopify-express is 8.1. Are there plans to support 6 in the future? I guess I could always fork and try to babel back to 6.1

Getting 403 on Heroku Redis only on Carrier Services endpoint

Deployed and launched to Heroku successfully, works good with all endpoints but
Getting 403 when trying to access carrier_services.json or fulfillment endpoints

What can be the problem? Tested locally with ngrok, the same issue

p.s.
scope is correct

Reconcile hidden dependencies

Right now we need the user to set up bodyParser and express session implicitly. In both these cases we should either automatically hook them up or find a way to communicate clearly that these need to be there and error if they aren't.

BadRequestError: request aborted // Shopify 499

Shopify Webhook into Nginx -> proxy_pass to nodeJS

"POST /webhook/shop_update HTTP/1.1" 499 0 "-" "Ruby"

And catching the error:

{ BadRequestError: request aborted
    at IncomingMessage.onAborted (MYAPP/node_modules/raw-body/index.js:231:10)
    at IncomingMessage.emit (events.js:182:13)
    at abortIncoming (_http_server.js:444:9)
    at socketOnEnd (_http_server.js:460:5)
    at Socket.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1090:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)

I believe this issue stream-utils/raw-body#57 is the "problem"

Likely down to my globalisation of bodyParser.json in error

app.use(bodyParser.json());

But probably worth to be able to support/handle bodyParser.jsonbeing globalised?

In my example, 90% of the routes in my application expect a JSON to be POST/PUT'ed so yes I globalised

Online access mode

Hi, I was wondering if there were any plans of adding the online access oauth type? Do you know if there's a way to retrieve the per-user scope with the offline access token? Thanks in advance!

Test webhook middleware

We have some tests in place for auth and api proxy, but not for the webhook middleware. We should rectify this.

enhancement: the API white list on shopifyApiProxy should be extended

For now, only the following endpoints are listed in the white list:
ALLOWED_URLS = ['/products', '/orders'];

We should add all the api endpoints, as these endpoints are secured and you can't access them without a proper authorization from the shop owner.

Am I mistaken here ?

;)
Greg

Correct way to make backend API calls with token?

Hey Guys,

A bit of a node N00B and trying to set up a simple carrier service that will provide different shipping rates to our wholesale customers.

To accomplish this, I need to loop through the items in the cart and check to see if see if their tags array contains a specific tag. I've been really struggling with making API calls within a new post route I've set up, mainly because I'm not sure where to grab the access token and it's not super clear in the docs (or maybe just my n00bness).

What's the correct way to set up a backend API properly call using the shop credentials? Currently doing something like this, but both shopDomain and accessToken are undefined. Have also tried different flavors of grabbing it from the session without luck. Any help would be appreciated!

request.session.accessToken
req.session.accessToken

app.post('/rates', jsonParser, function (request, res) {

  const shopify = new ShopifyAPIClient({ shopName: shopDomain, accessToken: accessToken });
  shopify.product.get(1234)

  // Do more stufff

});

SQliteStrategy -> SQLStrategy

It should be simple to update the sqliteStrategy to work with other sql databases either through environment variables or manual arguments.

This would allow one strategy to cover postgresql, mysql, etc.

'scope' is a one-item array, and scopes are a comma separated string

Noticed in 1.0.0-alpha.7.

Had to change my code from a multi-item array to a one-item, comma separated string to get installs to work again in this version. I believe I upgraded from either alpha 3 or 5.

It's fine, because it works, but its weird. Why not a multi-item array?

Add test suite

As of right now the project has no tests to speak of. We should rectify this to facilitate faster iteration.

afterAuth always redirects to "/" route

Issue summary

Let's say we have Express app with these routes:

app.get("/", withShop({ authBaseUrl: "/shopify" }), (req, res) => {
  var { session: { shop, accessToken } } = req;
  return res.send("Home");
});

app.get("/about", withShop({ authBaseUrl: "/shopify" }), (req, res) => {
  var { session: { shop, accessToken } } = req;
  return res.send("About");
});

If we install the app, go through the authentication process and navigate to https://app-domain.com/about?shop=shop-domain.myshopify.com, delete the session cookies and refresh, the re-authentication process will begin again and redirects to the / route when it should redirect to the /about route.

Expected behavior

The route should persist through the re-authentication process and redirect to the /about route.

Actual behavior

The route is forgotten and redirects to the / route.

Steps to reproduce the problem

  1. Install the app/navigate to the authenticated app
  2. Navigate to https://app-domain.com/about?shop=shop-domain.myshopify.com
  3. Delete the session cookies and refresh

Workaround

I figured out a workaround without changing anything in /node_modules/@shopify/shopify-express. I don't use the default withShop function, I made a few changes and use this instead:

module.exports = function withShop({ authBaseUrl } = {}) {
  return function verifyRequest(request, response, next) {
    const { query: { shop }, session, baseUrl } = request;

    if (session && session.accessToken) {
      next();
      return;
    }

    if (shop) {
      response.redirect(`${authBaseUrl || baseUrl}/auth?shop=${shop}&url=${encodeURIComponent(request.url)}`);
      return;
    }

    response.redirect('/install');
    return;
  };
};

I then change the shopifyExpress.afterAuth call to:

afterAuth(request, response) {
  const { session: { accessToken, shop } } = request;
  // install webhooks or hook into your own app here

  var path = "/";

  if( req.headers.referer ) {
    var query = req.headers.referer.split("?")[1];
    var queryParsed = queryString.parse(query);
    path = queryParsed.url ? queryParsed.url : path;
  }

  return response.redirect(path);
}

It'd be preferable to get this in the shopfiy-express core.

Error getting accessToken

Hi, I wonder why the accessToken could be retrieved when running locally but Heroku.
Here's what the error said:

GET /auth/shopify?shop=lskdfj.myshopify.com 304 2.288 ms - - 2017-12-13T15:57:27.999826+00:00 app[web.1]: (node:20) UnhandledPromiseRejectionWarning: TypeError: Cannot set property 'accessToken' of undefined 2017-12-13T15:57:27.999840+00:00 app[web.1]: at shopStore.storeShop (/app/node_modules/@shopify/shopify-express/routes/shopifyAuth.js:89:39) 2017-12-13T15:57:27.999841+00:00 app[web.1]: at MemoryStrategy.storeShop (/app/node_modules/@shopify/shopify-express/strategies/MemoryStrategy.js:13:12) 2017-12-13T15:57:27.999842+00:00 app[web.1]: at fetch.then.then.responseBody (/app/node_modules/@shopify/shopify-express/routes/shopifyAuth.js:84:19) 2017-12-13T15:57:27.999843+00:00 app[web.1]: at <anonymous> 2017-12-13T15:57:27.999844+00:00 app[web.1]: at process._tickCallback (internal/process/next_tick.js:160:7) 2017-12-13T15:57:28.006774+00:00 app[web.1]: (node:20) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) 2017-12-13T15:57:28.006860+00:00 app[web.1]: (node:20) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Add release.md

We should add instructions for how to release a new version of the package.

Double // in oAuth Process (invalid_request: The redirect_uri is not whitelisted)

Hi,

When I try to use this lib or this boilerplate: https://github.com/Shopify/shopify-node-app I'm getting an error when try to install app

You can checkout error here: http://prntscr.com/jamn44

Returned url: https://leonantest.myshopify.com/admin/oauth/authorize?baseUrl=%2Fshopify%2Fauth&scope=write_orders%2C%20write_products&client_id=c5277d3b1910bdb2d43ec919e756698b&redirect_uri=http%3A%2F%2F868de5a3.ngrok.io%2F%2Fshopify%2Fauth%2Fcallback

Note here: redirect_uri=http%3A%2F%2F868de5a3.ngrok.io**%2F%2F**shopify%2Fauth%2Fcallback

It has // instead of /

shopify-express oAuth process breaks when sessions are not configured in express

🔴 Error storing shop access token TypeError: Cannot set property 'accessToken' of undefined
    at shopStore.storeShop (/home/benni/myproject/node_modules/@shopify/shopify-express/routes/shopifyAuth.js:89:39)
    at ShopStoreTypeOrmService.<anonymous> (/home/benni/myproject/src/shop/services/shop-store-type-orm.service.ts:49:13)
    at Generator.next (<anonymous>)
    at fulfilled (/home/benni/myproject/src/shop/services/shop-store-type-orm.service.ts:16:58)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)
(node:18153) UnhandledPromiseRejectionWarning: TypeError: Cannot set property 'accessToken' of undefined
    at shopStore.storeShop (/home/benni/myproject/node_modules/@shopify/shopify-express/routes/shopifyAuth.js:89:39)
    at ShopStoreTypeOrmService.<anonymous> (/home/benni/myproject/src/shop/services/shop-store-type-orm.service.ts:51:13)
    at Generator.next (<anonymous>)
    at fulfilled (/home/benni/myproject/src/shop/services/shop-store-type-orm.service.ts:16:58)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)
(node:18153) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:18153) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I don't need or want session support in my app.

The problem is that in shopifyAuth.js in the function that handles "/callback" doesn't actually check if a session is available and just blindly tries to save stuff into the session.

request.session.accessToken = accessToken;
request.session.shop = shop;

Execution fails when I install this package.

/node_modules/@shopify/shopify-express/routes/index.js:19
);
^
SyntaxError: Unexpected token )
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
....

The syntax error is because of the extra comma on the previous line.
That is actually valid with new versions of javascript. I wonder why is this an error.

Redis Strategy causing infinite load

Hello,
Think I made a mistake wanted to use redisStrategy instead of Memory one. I cannot access my app, I have an infinite scroll without error

import session from 'express-session';
const RedisStore = require('connect-redis')(session);
etc

const shopifyConfig = {
  host: SHOPIFY_APP_HOST,
  apiKey: SHOPIFY_APP_KEY,
  secret: SHOPIFY_APP_SECRET,
  scope: ['read_script_tags','write_script_tags','read_orders','write_orders','read_fulfillments','write_fulfillments'],
  shopStore: new RedisStrategy(REDIS_URL),
  afterAuth(req, res) {
    const { session: { accessToken, shop } } = req;

    return res.redirect('/');
  },
};

app.use(
  session({
    store: new RedisStore(),
    secret: SHOPIFY_APP_SECRET,
    resave: true,
    saveUninitialized: false
  })
);

REDIS_URL is the redis to go url

It was working with memory strategy but now I have just infinite scroll (I have before an error with accessToken that was saved)

Thank you for your help

SQLStrategy.storeShop not working with Postgres

I noticed that the storeShop method fails when using the SQLStrategy with Postgres. After some debugging I found this was due to the knex SQL using IGNORE which Postgres does not support. I was able to implement the following hot fix which checks if the client in the supplied config is pg and using ON CONFLICT (shopify_domain) DO NOTHING instead.

Hotfix:

constructor(config = defaultConfig) {
    this.dbConfig = config;
    this.knex = Knex(config);
}

...

storeShop({ shop, accessToken, data = {} }, done) {
    const baseQuery = `INTO shops (shopify_domain, access_token) VALUES ('${shop}', '${accessToken}')`;
    const dbQuery = (this.dbConfig.hasOwnProperty('client') && this.dbConfig.client === 'pg')? 
    `INSERT ${baseQuery} ON CONFLICT (shopify_domain) DO NOTHING` : `INSERT OR IGNORE ${baseQuery}`;
    this.knex
      .raw(dbQuery)
      .then(result => {
        return done(null, accessToken);
      });
}

Usage of fetch in shopifyApiProxy

Hey there,

I've been browsing through the code of this repo as we are looking to build something similar for our APIs at my current job. While browsing, I stumbled over [this|https://github.com/Shopify/shopify-express/blob/master/routes/shopifyApiProxy.js#L58].

function fetchWithParams(url, fetchOpts, query) {
  const parsedUrl = new URL(url);

  Object.entries(query).forEach(([key, value]) => {
    parsedUrl.searchParams.append(key, value);
  });

  return fetch(parsedUrl.href, fetchOpts);
}

I can't find fetch being imported anywhere and it being a browser API, I would have thought this cannot work in node. Couldn't call fetch in the node REPL either. I'm just curious here, if you've found a way to do straight forward (i.e. no use of http) HTTP requests in node without third-party libraries.

Thanks for the great inspiration.

withShop Middleware --- /install route paramaterization

Forcing the base url of with shop to be /install is a bit of a burden. Imagine having multiple App credentials doing different but interrelated tasks on the same instance of a server. Both requiring separate install routes.

This is my case and it would be nice if

response.redirect('/install');

could instead be

response.redirect('${authBaseUrl || baseUrl}/install');

or something similar. Maybe even a new parameter instead of forcing /install

Improve Error Handling

Right now errors are pretty hit or miss in this middleware. it would be preferable to just emit a standard set of them and give users the tools to handle what to present to users.

Support mounting Install routes

I think we should consider exporting a route that renders an install route, with the user passing in a template to display.

Express-session throws error in production build

This is error I get when I try to run my shopify app in production build.

Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.

I am not able to use session in production build.
I tried to put this only on dev environment but then shopify auth doesnt work. It says

"Session not present on request, please install a session middleware."

Wrong session behavior when using multiple shopify shops in one browser

There is a problem when I use the same shopify app in multiple shopify shops in one browser. The first shopify shop will set the session with accessToken and shop. But for the following shopify shops my shopify-express server thinks, that the request comes from the same (first used) shopify shop.

This issue is already reported for https://github.com/Shopify/shopify-node-app (see Shopify/shopify-node-app#88).

The problem lies in the file https://github.com/Shopify/shopify-express/blob/master/middleware/withShop.js: It checks only, if any accessToken is set on the session. But not, if the shop that performs this request is the same as in the session. So we should also check, if the shop and the session.shop are identical:

if (session && session.accessToken && session.shop && session.shop === shop) {

Now we have another problem: shop is not always set on the request.query.
A possible solution could be to retrieve the shop either via the query or the referer header:

const shop = request.query.shop || request.get('referer').match(/shop=([^&]+)/)[1];

The result would be:

    // ...
    const { session } = request;
    const shop = request.query.shop || request.get('referer').match(/shop=([^&]+)/)[1];

    if (session && session.accessToken && session.shop && session.shop === shop) {
      return next();
    }
   // ...

Should I prepare a pull request with this change? Or does it break something I am not aware of?

Async/Await or Promise in afterAuth

Hi,

Thanks for this great package!
I'm working in an app, and I'm trying to save the store data (the store domain and the access token of the user) in a database.

I thought the best way to do this is in afterAuth, because you want to do this as early as possible in the process. The problem is that the database APIS are Promise/Async-Await based, and it seems like it's not possible to wait for afterAuth to return the redirection.

Perhaps my design is bad, or perhaps there's a way to combine afterAuth with an async middleware? Or is this something which is not supported?

EASDK auth flow is at risk of breaking in future versions of Chrome

Logging this now while it's fresh in my mind

https://github.com/Shopify/shopify-express/blob/master/routes/shopifyAuth.js#L37-L47

This code is at risk of breaking in a future version (65?) of Chrome, where Chrome will prevent redirects in the parent window.

The solution is to use a postMessage to the Shopify EASDK in order to perform the redirect. See the following example from shopify_app:

https://github.com/Shopify/shopify_app/blob/3fb589d71bc03a11a8bb48bf87e613f6ce0210ea/app/assets/javascripts/shopify_app/redirect.js

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.