🚀🚀 A Shopify embedded app starter template, with updated dependencies, session storage, app context and examples for basic functionalities.

Overview

Shopify Node App Starter

This is a starter template for embedded shopify apps based on the shopify cli node app.

Contributions to create the perfect shopify app boilerplate are very welcome! 🤩

Changes to the default Shopify node app starter

  • Updated dependencies (@apollo/client instead of react-apollo, react 17, next.js 12, polaris 7)
  • MongoDB session storage already set up (full credit goes to Harshdeep Singh Hura)
  • App context set up. Can be used to store data, that only needs to be fetched once, but is needed in multiple places
  • Link component to convert <a> tags to Next Links for relative paths
  • Routepropagation set up
  • Example API with verifyRequest() is set up
  • Examples for creating, displaying and canceling of app subscriptions
  • Loading screen while the app context is loading

Boilerplate to create an embedded Shopify app made with Node, Next.js, Shopify-koa-auth, Polaris and App Bridge React.

Installation

  • Fork and clone repo
  • Create an app in the shopify partner dashboard
  • Run shopify app connect to connect the app to shopify
  • Add ENCRYTION_STRING and MONGO_URL to your .env file (.env example is in the root directory)
  • Run shopify app serve to start dev enviroment

Requirements

Usage

Same usage as apps created with the Shopify CLI

Important

API routes have to start with /api/. All relative paths starting with /api/ will be converted to absolute paths by a next.js rewrite

License

This respository is available as open source under the terms of the MIT License.

Comments
  • Abort [Error]: The CLIKitStore instance hasn't been initialized & AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided

    Abort [Error]: The CLIKitStore instance hasn't been initialized & AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided

    Hey guys. Unfortunately, it is not possible to set up the project due to two errors. Been trying to get the project to run for hours.

    Error 1: Abort [Error]: The CLIKitStore instance hasn't been initialized - The CLIKitStore instance hasn't been initialized in line 35 @ next.config.mjs before session.ensureAuthenticatedPartners(); is called. I couldn't find any documentation on @shopify/cli-kit and trying await store.initializeCliKitStore(); in line 34 doesn't seem to work either. How do I initialize the store before the session is authenticated?

    Error 2: AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided - Whether I comment out the host in line 62 @ next.config.mjs or not, the host seems to be non-resolvable. Do I need to define an additional host in the dotenv? HOST=https://localhost does not seem to work for me, even tho I set it as URL in the Shopify Plugin. Is there a solution for this?

    I would be very grateful for any solutions and advice and thank you in advance.

    Have a nice evening everyone :)

    opened by testuser4334 6
  • I think I broke the app dev :D

    I think I broke the app dev :D

    Hello! Thanks for the awesome work, in case you know what's up, I tried to add cli3 inside the app and then I completely destroyed it.

    When running the npm run dev I get , Variable $applicationUrl of type Url! was provided invalid value: {"response":{"errors":[{"message":"Variable $applicationUrl of type Url! was provided

    Its around this line ShopifyCli.mjs file const result = await api.partners.request(query, token, variables);

    Although all the strings are looking ok it expects a type of URL

    I removed everything and the folders, cloned again etc, I installed everything again with force and npm audit fix but no luck. Let me know if you have any idea! Thanks

    opened by alexandrosk 3
  • Error when running npm run dev

    Error when running npm run dev

    Hi! After following all the steps, when running npm run dev, the software runs into an error. Output is:

    ready - started server on 0.0.0.0:3000, url: http://localhost:3000
    info  - Loaded env from [my-path]\.env
    Abort [Error]: The CLIKitStore instance hasn't been initialized
        at cliKitStore (file:///[MY-APP-PATH]/node_modules/@shopify/cli-kit/dist/store.js:28:15)
        ......
    

    Ani ideas why that is?

    opened by supa-freak 3
  • CustomProperties deprecated + Hydration failure

    CustomProperties deprecated + Hydration failure

    Deprecation: The CustomProperties component has been deprecated. See the v10 migration guide for replacing dark color scheme styles. https://github.com/Shopify/polaris/blob/main/documentation/guides/migrating-from-v9-to-v10.md

    Error: Hydration failed because the initial UI does not match what was rendered on the server. See more info here: https://nextjs.org/docs/messages/react-hydration-error

    How do I fix this?

    opened by aimproxy 3
  • Perform fewer HTTP calls at the Callback Endpoint (Solution)

    Perform fewer HTTP calls at the Callback Endpoint (Solution)

    I've been playing around with Shopify OAuth a little bit again and I might discover a way of not doing too many requests into the callback endpoints.

    I realised if I tweak a little bit the code of the session storage file, at the callback endpoint we would have to persist a second time the shop into Redis!

    There are only two cases during Shopify OAuth that need to be handled! We know that Shopify uses session.id from the SessionInterface, the ID can vary between a uuidv4 string and the shop URL with a suffixed number like this: .myshopify.com_123456789.

    Suffice to say, that we only need to handle the storeCallback and the loadCallback to make this work, let's see some code:

    
    const SHOPIFY_DOMAIN = ".myshopify.com";
    
    const storeCallback = async (session: SessionInterface): Promise<boolean> => {
      const result = await redis.set(
        session.id.includes(SHOPIFY_DOMAIN) ? session.shop : session.id,
        JSON.stringify(session),
        session.id.includes("offline") ? {} : { ex: 36000 }
      );
    
      return result === "OK";
    };
    
    const getShopDomainIfPresent = (shopId: string): string | null => {
      if (shopId.includes(SHOPIFY_DOMAIN)) {
        return shopId.substring(0, shopId.indexOf("_"));
      }
    
      return null;
    };
    
    const loadCallback = async (shopId: string): Promise<SessionInterface | { [key: string]: unknown } | undefined> => {
      const result = await redis.get<Promise<SessionInterface | null>>(getShopDomainIfPresent(shopId) || shopId);
    
      return result ?? undefined;
    };
    
    const deleteCallback = async (shopId: string): Promise<boolean> => {
      const result = await redis.del(getShopDomainIfPresent(shopId) || shopId);
    
      return result > 0;
    };
    

    By doing this, this piece of code is now obsolete benefiting from fewer calls at the callback endpoint!

          const redis = new Redis({
            url: process.env.UPSTASH_REDIS_REST_URL as string,
            token: process.env.UPSTASH_REDIS_REST_TOKEN as string
          });
    
          const redisShop = await redis.get(session.shop);
          if (!redisShop || redisShop !== process.env.SCOPES) {
            await redis.set(session.shop, session);
          }
    
    opened by aimproxy 2
  • "cliKitStore.initializeCliKitStore is not a function" error

    Received this error after running pnpm install and npm run on a new clone of the project. Using version 3.0.25 of @shopify/cli-kit.

    TypeError: cliKitStore.initializeCliKitStore is not a function
        at setEnvironmentAndReturnHost (file:///Users/user/app/next.config.mjs:37:21)
        at config (file:///Users/user/app/next.config.mjs:66:18)
        at Object.normalizeConfig (/Users/user/app/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/server/config-shared.ts:556:14)
        at Object.loadConfig [as default] (/Users/user/app/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/server/config.ts:799:6)
        at NextServer.prepare (/Users/user/app/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/server/next.ts:110:20)
        at /Users/user/app/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/cli/next-dev.ts:107:
    

    Console log of cliKitStore:

    cliKitStore [Object: null prototype] {
      createConf: [Function: createConf],
      remove: [Function: remove$2],
      getAppInfo: [Function: getAppInfo],
      setAppInfo: [Function: setAppInfo],
      clearAppInfo: [Function: clearAppInfo],
      getTheme: [Function: getTheme],
      setTheme: [Function: setTheme],
      getSession: [Function: getSession],
      setSession: [Function: setSession],
      removeSession: [Function: removeSession]
    }
    
    opened by yngwz 2
  • Cookies gone after app is embedded (without offline endpoints)

    Cookies gone after app is embedded (without offline endpoints)

    I removed the offline access tokens endpoint as my app won't need to contact Shopify API without the user consentiment.

    So my middleware ends up like this:

        const urlParams = new URLSearchParams(req.url?.split('?')[1]);
        const {shop} = Object.fromEntries(urlParams);
    
        if (
            req.nextUrl.pathname.startsWith('/api/auth') ||
            req.nextUrl.pathname.startsWith('/api/webhooks')
        ) {
            return NextResponse.next();
        }
    
        if (req.nextUrl.pathname.startsWith('/api')) {
            return await withShopifyMiddleware(req, shop);
        }
    
        if (shop) {
            const shopSession = await findShopByShopDomain(shop)
            console.info(shopSession)
    
            if (!shopSession) {
                return NextResponse.redirect(
                    `${process.env.HOST}/api/auth/?shop=${shop}`
                );
            }
    
            return NextResponse.next({
                headers: {
                    'Content-Security-Policy': `frame-ancestors https://${shop} https://admin.shopify.com;`,
                },
            });
        }
    
        return NextResponse.next();
    

    I also changed a few things here and there and now Cookies are gone after the app rich is embedded state and

       const bearerPresent = req.headers.get('authorization')?.match(/Bearer (.*)/);
    

    is always undefined!

    @carstenlebek Any thoughts on why this might append?

    opened by aimproxy 2
  • Persist host in window object

    Persist host in window object

    Changing the strategy to persist the host from session storage to storing it in the window object. The reasoning for this is, that the access to session storage is denied in incognito modes of browsers.

    opened by carstenlebek 1
  • return-top-level-redirection.ts:15 points to clebek shop.

    return-top-level-redirection.ts:15 points to clebek shop.

    Hi, The helper returnToLevelRedirection function (line 15) points to ${process.env.HOST}/api/auth?shop=clebek.myshopify.com. Should it be redirectUrl instead?

    Thanks!!!

    opened by hfoffani 1
  • Add full URL to Next config to work with Heroku deployment

    Add full URL to Next config to work with Heroku deployment

    Fix for this error when deploying to Heroku:

    destination does not start with /, http://, or https:// for route {"source":"/api/:path*","destination":"${host}/api/:path*"}

    opened by uglyhott 1
  • How to integrate in app-extensions with Shopify

    How to integrate in app-extensions with Shopify

    Hi there,

    Thanks for making this amazing template, really helps to make shopify app dev much easier. I'm currently hoping to add in some extensions, in particular the checkout button ( Something like this : https://shopify.dev/apps/checkout/delivery-instructions )

    How do you suggest doing so? Not too sure how i might go about doing so since the layout of the folder is a bit different as compared to the scaffolded one by shopify-cli

    opened by ivanleomk 3
Owner
Carsten Lebek
Carsten Lebek
Forked from hayes0724/shopify-packer Modern development tool for Shopify using Webpack 5. Easy to extend and customize, zero build config, compatible with Slate and existing websites.

Shopify Packer Modern development tool for Shopify using Webpack 5. Easy to extend and customize, zero build config, comes with starter themes and com

Web & Mobile | eCommerce | Full-Stack Developer 4 Nov 24, 2022
An unofficial, simplified version of the @Shopify/koa-shopify-auth middleware library.

simple-koa-shopify-auth https://www.npmjs.com/package/simple-koa-shopify-auth NOTE: This package is not maintained by or affiliated with Shopify. Desc

David 20 Nov 7, 2022
Shopify Landing (Open source landing page shopify application)

SHOPIFY Open source landing page shopify application Configuration and Setup Key Features Technologies used ?? Screenshots Author License Configuratio

Gilbert Hutapea 8 May 10, 2023
Emem Ekpo 7 Sep 9, 2022
Richard Chileya 5 Nov 11, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Awesome books is a vanilla Javascript which offers CRUD functionalities allowing you to add, remove edit boks info and store it to the local storage.

Awesome Books Awesome books is a simple project that displays new books when a user updates them. Built With HTML-5 CSS3 Javacript Linters Live Demo L

Nemwel Boniface 23 Aug 6, 2022
A "Basic-to-Lisp" compiler. But Basic is not real Basic, and Lisp is not real Lisp.

Basic2Lisp A "Basic-to-Lisp" compiler. But Basic is not real Basic, and Lisp is not real Lisp. Syntax Print-Sth Put some-value to standard output. PRI

Hana Yabuki 5 Jul 10, 2022
Node-cli-starter - Basic starter kit for building Node CLI applications with TypeScript.

node-cli-starter Minimal starter kit for building Node CLI applications with TypeScript. Getting Started To get started clone repo locally and run npm

Cory Rylan 7 May 17, 2022
Finance-Tracker - A basic finance-tracker application built using Next.js, React Hooks and Context API

Next.js + Tailwind CSS Example This example shows how to use Tailwind CSS (v3.0) with Next.js. It follows the steps outlined in the official Tailwind

Osemwengie Benjamin 1 Jan 2, 2022
A weather dashboard that features dynamically updated HTML and CSS using OpenWeather API data.

Weather Dashboard A weather dashboard that features dynamically updated HTML and CSS using OpenWeather API data. User Story AS A traveler I WANT to se

Benjamin Eidum 1 Apr 19, 2022
C2Ladders - a reconstruction of old a2oj Ladders with new and updated problemset

C2Ladders is rating wise list of Codeforces problems which were solved by many people who have had stable rating increase. It's a reconstruction of old a2oj Ladders with new and updated problemset.

Prince Gupta 45 Dec 11, 2022
Aggregate quality learning resources, useful tools, interesting projects in github... Updated every Monday.

Github University Aggregate quality learning resources, useful tools, interesting projects in github... Updated every Monday. 汇总 github 中优质的学习资源,好用的工具

荣顶 9 Dec 14, 2022
A continuously updated collection of Tesla superchargers + prices

tesla-superchargers This is a daily updating repo containing a list of all Tesla Superchargers and the current prices for both members (Tesla owners a

Niek van der Maas 15 Dec 21, 2022