How to create a shopify app - Step by Step
Tags: tutorial, javascript
Published: 2021-11-06 21:47:33

Hi there, i am going to create a shopify app for subscriptions and especially for cancelling those according to new german [law](https://www.mofo.com/resources/insights/211006-new-two-click-cancellation-button.html)
 Therefore I created a personal step by step plan to create an shopify app. 
 I justed copied most of the stuff from different source, hope it helps :)

## Vocabulary:
There are three key areas where apps interact with the Shopify platform:  [Shopify admin](https://shopify.dev/apps/getting-started#shopify-admin),  [online store](https://shopify.dev/apps/getting-started#online-store), and  [checkout](https://shopify.dev/apps/getting-started#checkout).

#### Shopify admin[](https://shopify.dev/apps/getting-started#shopify-admin)

After logging in to Shopify, merchants set up their store, configure settings, and manage their business using the  [Shopify admin](https://help.shopify.com/manual/shopify-admin).

The Shopify admin includes core aspects of the merchant's Shopify business, including orders, products, and customers. Merchants also install apps in the Shopify admin.

#### Online store[](https://shopify.dev/apps/getting-started#online-store)

The  [online store](https://help.shopify.com/manual/online-store)  is an online home for a merchant's business. Merchants use the online store to create webpages, publish a blog, and sell their products.

As an app developer, you can build integrations in all the places where merchants want to sell, and where their customers want to buy.

#### Checkout[](https://shopify.dev/apps/getting-started#checkout)

Merchants use the secure  [Shopify checkout](https://help.shopify.com/manual/checkout-settings)  to accept orders and take payments wherever they sell online. After a customer adds products to a cart, they use Shopify checkout to enter their shipping information and payment details before placing the order.

Apps can integrate with Shopify checkout to offer additional functionality to merchants and customers. For example, merchants can use apps that support post-purchase offers to show a cross-sell or upsell offer to customers at checkout.

## Create your first shopify app
#### Step 1:  Register Shopify Partner Account
Create First an Partner Account here:
https://partners.shopify.com/signup

#### Step 2:  Create an test store
A development store is a free Shopify account that comes with a few limitations. As a  [Shopify Partner](https://help.shopify.com/en/partners/about), you can create an unlimited number of development stores.

You can use a development store to  [test any themes or apps](https://shopify.dev/apps/tools/development-stores#create-a-development-store-for-testing-apps-or-themes)  that you create, or to  [set up a Shopify store for a client](https://shopify.dev/apps/tools/development-stores#create-a-development-store-for-a-client). Setting up a development store and transferring it to a client gives you a  [recurring commission](https://help.shopify.com/partners/how-to-earn#development-store-referrals).
2.1.  Log in to your  [Partner Dashboard](https://partners.shopify.com/current/stores).
2.2.  Click  **Stores**.
2.3.  Click  **Add store**.
2.4.  In the  **Store type**  section, select  **Development store**.
2.5.  In the  **Login information**  section, enter a name for your store and a password that you can use to log in. By default, the email associated with your Partner Dashboard is used as the username, but you can change it if you want to.
2.6.  Optional: Enable a developer preview by checking  **Create a non-transferrable store that uses a developer preview**. Select a developer preview version from the drop-down list.
2.7.  In the  **Store address**  section, enter your address.
2.8.  Optional: In the  **Store purpose**  section, select the reason why you're creating this development store.
2.9.  Click  **Save**.

#### Step 3: Install latest Node.js
https://nodejs.org/en/download/

#### Step 4: Install Shopify CLI
If you want to use Shopify CLI natively on Windows 10, then first make sure that you've installed Ruby+Devkit using  [RubyInstaller for Windows](https://rubyinstaller.org/downloads/)  (version 2.7 or higher).
Alternatively, you can use Shopify CLI using Windows Subsystem for Linux, in which case you need to install the following:

-   [Windows Subsystem for Linux](https://docs.microsoft.com/windows/wsl/install-win10)
-   [Ubuntu VM](https://www.microsoft.com/p/ubuntu/9nblggh4msv6)

After you install the prerequisites, you can install Shopify CLI as a Ruby gem using the [RubyGems.org](https://rubygems.org/) package manager. In a new terminal window, navigate to your home directory and run the following command:

    gem install shopify-cli
  
To verify that Shopify CLI is installed properly, run the following command:

    shopify version

#### Step 5: Login to Shopify with the CLI
Before we create the project with the help of the CLI we need to login to shopify. Therefore we type in in the terminal:

    shopify login

A browser tab will be opened, log in to your partner account. You should see a message in your terminal, after successfull logged in

> Logged into store xxxxxx.myshopify.com in partner organization xxxx

#### Step 6: Create a new project
After you've installed Shopify CLI, you're ready to create a new project.

Navigate to the directory where you want to create your project and run  `shopify app create node`. This command scaffolds a new Node.js app in a subdirectory and creates your app in the Partner Dashboard.

#### Step 7: Start a local development server
After your app is created, you can work with it by navigating to your project directory and running  `shopify node serve`  to start a local development server.

Shopify CLI uses  [ngrok](https://ngrok.com/)  to create a tunnel that allows your app to be accessed using a unique HTTPS URL, which is mandatory when creating an app.

#### Step 8: Install your app on your development store
With the server running, open the URL that your terminal printed out in the previous step. When you open the URL, you're prompted to install the app on your development store.
![enter image description here](https://i.ibb.co/XFLS5fP/Inked-Screenshot-2021-10-27-171015.jpg)

If you follow this link, you should see a message containing your app and the scopes of it. Press install and your good to go further.

#### Step 9: Start building the app
Previously, you  [created a new app](https://shopify.dev/apps/getting-started/create)  with  [Shopify CLI](https://shopify.dev/apps/tools/cli). You're now ready to start building your app.

In this tutorial, you'll accomplish a series of tasks to add some specific functionality to your app. Your final app will be simple, but you’ll learn where to find resources to build more complex features on your own.
After you've finished this tutorial, you'll have accomplished the following:

-   populated products in your development store to test your app against
-   built the beginnings of your user interface with Polaris
-   set up a GraphQL query to retrieve products
-   set up a GraphQL mutation to update the prices of products

#### Step 10 : Populate products

Shopify CLI helps with the process of adding example data to test your app's behavior. You can use Shopify CLI to create records for products, customers, and draft orders.

Because your app needs to interact with product data, start by populating products in your development store:

1.  Open a new terminal window.
2.  Navigate to your project directory.
3.  Run  `shopify populate products`
![GIF showing how to populate data in a development store](https://shopify.dev/assets/apps/populate-data-e8eb3effa099f25f01da223a1fd2aba2c507605cc53662a860cf058d3cc35214.gif)

#### Step 11: Add an empty state
Now that you can run your app in Shopify, you can view and test your frontend components as you build them. You can use  [Polaris](https://polaris.shopify.com/), Shopify’s React component library and design system, to build your user interface.

Use Polaris to add an empty state to your app. The  [Polaris Empty state component](https://polaris.shopify.com/components/structure/empty-state)  helps to communicate the value of your app and its primary action when merchants first add it to their Shopify admin.

1.  In your code editor, navigate to your  `pages/index.js`  file.
2.  Replace the contents of the file with an  `EmptyState`  component:

```typescript
import { Heading, Page, TextStyle, Layout, EmptyState} from "@shopify/polaris";
const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';
const Index = () => (
  <Page>
    <Layout>
      <EmptyState // Empty state component
        heading="Discount your products temporarily"
        action={{
          content: 'Select products',
          onAction: () => this.setState({ open: true }),
        }}
        image={img}
      >
        <p>Select products to change their price temporarily.</p>
      </EmptyState>
    </Layout>
  </Page>
);
export default Index;
```
When you preview your embedded app, it displays the empty state.

![Screenshot showing your app's empty state](https://shopify.dev/assets/apps/app-empty-state-1deceb78b6985ec6f2869b639cc709189b11e93b8c9186f4dccdedf342b83bfb.png)

#### Step 12:  Add a resource picker

Next, add a resource picker so that you can select products from your app. You can use  [App Bridge](https://shopify.dev/apps/tools/app-bridge), Shopify's standalone vanilla JavaScript library, to add a resource picker to your app.

The App Bridge  [`ResourcePicker`](https://shopify.dev/apps/tools/app-bridge/actions/resourcepicker)  action set provides a search-based interface to help you find and select one or more products and then returns the selected resources to your app.

In your  `pages/index.js`  file, add a class that sets a state for the resource picker. Then, add the  `ResourcePicker`  component to the primary action button on the  `EmptyState`  component:
```typescript
import React from 'react';
import { Heading, Page, TextStyle, Layout, EmptyState} from "@shopify/polaris";
import { ResourcePicker, TitleBar } from '@shopify/app-bridge-react';
const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';
// Sets the state for the resource picker
class Index extends React.Component {
  state = { open: false };
  render() {
    return (
      <Page>
        <TitleBar
          primaryAction={{
            content: 'Select products',
            onAction: () => this.setState({ open: true }),
          }}
        />
        <ResourcePicker // Resource picker component
          resourceType="Product"
          showVariants={false}
          open={this.state.open}
          onSelection={(resources) => this.handleSelection(resources)}
          onCancel={() => this.setState({ open: false })}
        />
        <Layout>
          <EmptyState
            heading="Discount your products temporarily"
            action={{
              content: 'Select products',
              onAction: () => this.setState({ open: true }),
            }}
            image={img}
          >
            <p>Select products to change their price temporarily.</p>
          </EmptyState>
        </Layout>
      </Page>
    );
  }
  handleSelection = (resources) => {
    this.setState({ open: false });
    console.log(resources);
  };
}
export default Index;
```
In your embedded app, when you click  **Select products**, the  **Add products**  modal opens.

![GIF showing the Add products modal](https://shopify.dev/assets/apps/select-products-6c166dd1fcbe2a5a74b5102e04e8f380ea3e22d7abda4a21278bbe75028dab26.gif)

#### Step 13:   Add a resource list
Now that you've set up your resource picker, you need a way to retrieve products. You can retrieve products using the  [GraphQL Admin API](https://shopify.dev/api/admin/graphql/reference). Ultimately, you want to display these products in a resource list.

To allow your app to query data with GraphQL, create a new  `ResourceList.js`  file and include the  `graphql-tag`  and  `react-apollo`  imports in the file.

Then, set up a GraphQL query called  `getProducts`  to retrieve a list of products and their prices.

 1.  Run  `npm install store-js`
     
 2.  Create a new  `components`  folder in the  `pages`  folder in your project, and create a new  `ResourceList.js`  file in the folder.
    
 3.  Add imports to your  `ResourceList.js`  file and set up your GraphQL query to retrieve products and their prices:
```typescript
import React from 'react';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
import {
  Card,
  ResourceList,
  Stack,
  TextStyle,
  Thumbnail,
} from '@shopify/polaris';
import store from 'store-js';
import { Redirect } from '@shopify/app-bridge/actions';
import { Context } from '@shopify/app-bridge-react';
// GraphQL query to retrieve products by IDs.
// The price field belongs to the variants object because
// variations of a product can have different prices.
const GET_PRODUCTS_BY_ID = gql`
  query getProducts($ids: [ID!]!) {
    nodes(ids: $ids) {
      ... on Product {
        title
        handle
        descriptionHtml
        id
        images(first: 1) {
          edges {
            node {
              originalSrc
              altText
            }
          }
        }
        variants(first: 1) {
          edges {
            node {
              price
              id
            }
          }
        }
      }
    }
  }
`;
```
In your `ResourceList.js` file, after your GraphQL query, set up a class called `ResourceListWithProducts` that extends the `ResourceList` component and returns products and prices. Then, define your `ResourceList` component:
 ```typescript
class ResourceListWithProducts extends React.Component {
  static contextType = Context;

  render() {
    const app = this.context;

    return (
      // GraphQL query to retrieve products and their prices
      <Query query={GET_PRODUCTS_BY_ID} variables={{ ids: store.get('ids') }}>
        {({ data, loading, error }) => {
          if (loading) return <div>Loading…</div>;
          if (error) return <div>{error.message}</div>;

          return (
            <Card>
              <ResourceList // Defines your resource list component
                showHeader
                resourceName={{ singular: 'Product', plural: 'Products' }}
                items={data.nodes}
                renderItem={item => {
                  const media = (
                    <Thumbnail
                      source={
                        item.images.edges[0]
                          ? item.images.edges[0].node.originalSrc
                          : ''
                      }
                      alt={
                        item.images.edges[0]
                          ? item.images.edges[0].node.altText
                          : ''
                      }
                    />
                  );
                  const price = item.variants.edges[0].node.price;
                  return (
                    <ResourceList.Item
                      id={item.id}
                      media={media}
                      accessibilityLabel={`View details for ${item.title}`}
                      onClick={() => {
                        store.set('item', item);
                      }}
                    >
                      <Stack>
                        <Stack.Item fill>
                          <h3>
                            <TextStyle variation="strong">
                              {item.title}
                            </TextStyle>
                          </h3>
                        </Stack.Item>
                        <Stack.Item>
                          <p>${price}</p>
                        </Stack.Item>
                      </Stack>
                    </ResourceList.Item>
                    );
                  }}
                />
              </Card>
            );
          }}
        </Query>
      );
    }
  }
export default ResourceListWithProducts;
```
In `pages/index.js` file, add your imports and define a constant for
    your app's empty state. Then, update the code that controls the
    layout of your empty state, and specify using your new resource list
    with products:
```typescript
import React from 'react';
import { Page, Layout, EmptyState} from "@shopify/polaris";
import { ResourcePicker, TitleBar } from '@shopify/app-bridge-react';
import store from 'store-js';
import ResourceListWithProducts from './components/ResourceList';
const img = 'https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg';
class Index extends React.Component {
  state = { open: false };
  render() {
    // A constant that defines your app's empty state
    const emptyState = !store.get('ids');
    return (
      <Page>
        <TitleBar
          primaryAction={{
            content: 'Select products',
            onAction: () => this.setState({ open: true }),
          }}
        />
        <ResourcePicker
          resourceType="Product"
          showVariants={false}
          open={this.state.open}
          onSelection={(resources) => this.handleSelection(resources)}
          onCancel={() => this.setState({ open: false })}
        />
        {emptyState ? ( // Controls the layout of your app's empty state
          <Layout>
            <EmptyState
              heading="Discount your products temporarily"
              action={{
                content: 'Select products',
                onAction: () => this.setState({ open: true }),
              }}
              image={img}
            >
              <p>Select products to change their price temporarily.</p>
            </EmptyState>
          </Layout>
        ) : (
          // Uses the new resource list that retrieves products by IDs
          <ResourceListWithProducts />
        )}
      </Page>
    );
  }
  handleSelection = (resources) => {
    const idsFromResources = resources.selection.map((product) => product.id);
    this.setState({ open: false });
    store.set('ids', idsFromResources);
  };
}
export default Index;
```

Now, when you click **Select products**, and add products from the **Add products** modal, a list of products displays.
![GIF showing how to add products from the modal](https://shopify.dev/assets/apps/add-products-to-list-51aaee7527ee4d78a965f724190cdf0c9830b1561abcf478e8326e591b058fde.gif)

#### Step 14:   Update product prices
You've implemented a GraphQL query to read product data, and added the functionality to display the retrieved products in a resource list. Next, you'll use GraphQL to modify product data.

Set up a GraphQL mutation called  `ProductVariantUpdate`  to update the prices of products in your app.

1. Create a new  `ApplyRandomPrices.js`  file in your  `components`  folder.
2.  Add imports to your  `ApplyRandomPrices.js`  file and set up a GraphQL mutation that allows your app to update the prices of products:

 pages/components/ApplyRandomPrices.js
```jsx
import React, { useState } from 'react';
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
import { Layout, Button, Banner, Toast, Stack, Frame } from '@shopify/polaris';
import { Context } from '@shopify/app-bridge-react';
// GraphQL mutation that updates the prices of products
const UPDATE_PRICE = gql`
  mutation productVariantUpdate($input: ProductVariantInput!) {
    productVariantUpdate(input: $input) {
      product {
        title
      }
      productVariant {
        id
        price
      }
    }
  }
`;
```
4.  After your mutation in  `ApplyRandomPrices.js`, set up a class called  `ApplyRandomPrices`  which takes your mutation's input and applies a random price to the selected product:
   
    pages/components/ApplyRandomPrices.js

   ````jsx
  class ApplyRandomPrices extends React.Component {
  static contextType = Context;

  render() {
    return ( // Uses mutation's input to update product prices
      <Mutation mutation={UPDATE_PRICE}>
        {(handleSubmit, {error, data}) => {
          const [hasResults, setHasResults] = useState(false);

          const showError = error && (
            <Banner status="critical">{error.message}</Banner>
          );

          const showToast = hasResults && (
            <Toast
              content="Successfully updated"
              onDismiss={() => setHasResults(false)}
            />
          );

          return (
            <Frame>
              {showToast}
              <Layout.Section>
                {showError}
              </Layout.Section>

              <Layout.Section>
                <Stack distribution={"center"}>
                  <Button
                    primary
                    textAlign={"center"}
                    onClick={() => {
                      let promise = new Promise((resolve) => resolve());
                      for (const variantId in this.props.selectedItems) {
                        const price = Math.random().toPrecision(3) * 10;
                        const productVariableInput = {
                          id: this.props.selectedItems[variantId].variants.edges[0].node.id,
                          price: price,
                        };

                        promise = promise.then(() => handleSubmit({ variables: { input: productVariableInput }}));
                      }

                      if (promise) {
                        promise.then(() => this.props.onUpdate().then(() => setHasResults(true)));
                    }}
                  }
                  >
                    Randomize prices
                  </Button>
                </Stack>
              </Layout.Section>
            </Frame>
          );
        }}
      </Mutation>
    );
  }
}
export default ApplyRandomPrices;
````
5.  Update your  `pages/index.js`  file to include the following imports:
````typescript
import React from 'react';
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
import { Page, Layout, EmptyState, Button, Card } from "@shopify/polaris";
import { ResourcePicker, TitleBar } from '@shopify/app-bridge-react';
import store from 'store-js';
import ResourceListWithProducts from './components/ResourceList';
````

6.  In  `ResourceList.js`, add the  `ApplyRandomPrices`  import. Implement a constructor on your  `ResourceListWithProducts`  class and update your GraphQL query to enable refetching products by ID. Finally, update your  `ResourceList`  component:
    
    pages/components/ResourceList.js
  ````typescript
 import React from 'react';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
import {
  Card,
  ResourceList,
  Stack,
  TextStyle,
  Thumbnail,
} from '@shopify/polaris';
import store from 'store-js';
import { Redirect } from '@shopify/app-bridge/actions';
import { Context } from '@shopify/app-bridge-react';
import ApplyRandomPrices from './ApplyRandomPrices';
// GraphQL query that retrieves products by ID
const GET_PRODUCTS_BY_ID = gql`
  query getProducts($ids: [ID!]!) {
    nodes(ids: $ids) {
      ... on Product {
        title
        handle
        descriptionHtml
        id
        images(first: 1) {
          edges {
            node {
              originalSrc
              altText
            }
          }
        }
        variants(first: 1) {
          edges {
            node {
              price
              id
            }
          }
        }
      }
    }
  }
`;
class ResourceListWithProducts extends React.Component {
  static contextType = Context;

  // A constructor that defines selected items and nodes
  constructor(props) {
    super(props);
    this.state = {
      selectedItems: [],
      selectedNodes: {},
    };
  }

  render() {
    const app = this.context;

    // Returns products by ID
    return (
        <Query query={GET_PRODUCTS_BY_ID} variables={{ ids: store.get('ids') }}>
          {({ data, loading, error, refetch }) => { // Refetches products by ID
            if (loading) return <div>Loading…</div>;
            if (error) return <div>{error.message}</div>;

            const nodesById = {};
            data.nodes.forEach(node => nodesById[node.id] = node);

            return (
              <>
                <Card>
                  <ResourceList
                    showHeader
                    resourceName={{ singular: 'Product', plural: 'Products' }}
                    items={data.nodes}
                    selectable
                    selectedItems={this.state.selectedItems}
                    onSelectionChange={selectedItems => {
                      const selectedNodes = {};
                      selectedItems.forEach(item => selectedNodes[item] = nodesById[item]);

                      return this.setState({
                        selectedItems: selectedItems,
                        selectedNodes: selectedNodes,
                      });
                    }}
                    renderItem={item => {
                      const media = (
                        <Thumbnail
                          source={
                            item.images.edges[0]
                              ? item.images.edges[0].node.originalSrc
                              : ''
                          }
                          alt={
                            item.images.edges[0]
                              ? item.images.edges[0].node.altText
                              : ''
                          }
                        />
                      );
                      const price = item.variants.edges[0].node.price;
                      return (
                        <ResourceList.Item
                          id={item.id}
                          media={media}
                          accessibilityLabel={`View details for ${item.title}`}
                          verticalAlignment="center"
                          onClick={() => {
                            let index = this.state.selectedItems.indexOf(item.id);
                            const node = nodesById[item.id];
                            if (index === -1) {
                                this.state.selectedItems.push(item.id);
                                this.state.selectedNodes[item.id] = node;
                            } else {
                              this.state.selectedItems.splice(index, 1);
                                delete this.state.selectedNodes[item.id];
                            }

                            this.setState({
                              selectedItems: this.state.selectedItems,
                              selectedNodes: this.state.selectedNodes,
                              });
                          }}
                        >
                          <Stack alignment="center">
                            <Stack.Item fill>
                              <h3>
                                <TextStyle variation="strong">
                                  {item.title}
                                </TextStyle>
                              </h3>
                            </Stack.Item>
                            <Stack.Item>
                              <p>${price}</p>
                            </Stack.Item>
                          </Stack>
                        </ResourceList.Item>
                      );
                    }}
                  />
                </Card>

              <ApplyRandomPrices selectedItems={this.state.selectedNodes} onUpdate={refetch} />
            </>
          );
        }}
      </Query>
    );
  }
}
export default ResourceListWithProducts;
````
In your app, you can now update the prices of products.

![GIF showing how to populate data in a development store](https://shopify.dev/assets/apps/randomize-prices-a8c49c220e447a3b5ac233f582eddd2a9bc81050c32c601b5de6ae99001e8ae8.gif)
    
   
    

## Next steps[](https://shopify.dev/apps/getting-started/add-functionality#next-steps)

-   Use  [webhooks](https://shopify.dev/apps/webhooks)  to stay in sync with Shopify or execute code after a specific event occurs in a shop.
-   Identify your  [app business model](https://shopify.dev/apps/billing/models)  and learn how to use the  [Billing API](https://shopify.dev/apps/billing)  to bill customers with recurring monthly charges or one-time purchases.
-   Learn how to use  [app extensions](https://shopify.dev/apps/app-extensions)  to add features to Shopify admin or POS.
-   Explore the  [GraphQL Admin API](https://shopify.dev/api/admin/graphql/reference)  and  [REST Admin API](https://shopify.dev/api/admin/rest/reference)  references.