Building a Custom App with Shopify CLI and Node.js

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

Introduction

Shopify’s ecosystem empowers merchants with extensible storefronts, admin interfaces, and integrations via custom apps. The Shopify CLI streamlines app creation, authentication, and deployment, while Node.js offers a performant, JavaScript-first backend. In this comprehensive tutorial, you’ll learn how to scaffold a Shopify custom app, implement OAuth authentication, subscribe to webhooks, add REST and GraphQL endpoints, and deploy your app—using modern Node.js frameworks and the Shopify App CLI. By the end, you’ll have a robust foundation to build features like product synchronization, order automation, and embedded admin UI components.

Prerequisites

Before you begin, ensure you have:

  • Shopify Partner account (create at partners.shopify.com)
  • Node.js v14+ and npm installed
  • Git command-line tools
  • ngrok or similar tunneling tool for local HTTPS development
  • Basic familiarity with JavaScript/TypeScript and Express or Koa

1. Install Shopify CLI and Create Your App

2. Configure App Credentials

  1. Create App in Partner Dashboard
    • In your Partner account, go to Apps → Create app → Custom app
    • Set the App URL and Allowed redirection URL(s) to your ngrok URL, e.g., https://abcd1234.ngrok.io/api/auth/callback
  2. Add API Key & Secret to .env envCopyEditSHOPIFY_API_KEY=your_api_key SHOPIFY_API_SECRET=your_api_secret SCOPES=read_products,write_orders HOST=https://abcd1234.ngrok.io
  3. Restart your dev server bashCopyEditnpm run dev

3. Implement OAuth Authentication

The CLI template uses @shopify/koa-shopify-auth. In Express, you’ll use @shopify/shopify-express or passport-based flow:

  1. Initialize OAuth middleware jsCopyEditimport shopifyAuth, { verifyRequest } from '@shopify/shopify-express'; app.use( shopifyAuth({ apiKey: process.env.SHOPIFY_API_KEY, secret: process.env.SHOPIFY_API_SECRET, scopes: process.env.SCOPES.split(','), afterAuth(ctx) { const { shop, accessToken } = ctx.state.shopify; // Save to session or database ctx.redirect('/'); }, }), );
  2. Protect routes jsCopyEditapp.get('/api/products', verifyRequest(), async (req, res) => { const session = await Shopify.Utils.loadCurrentSession(req, res); const client = new Shopify.Clients.Rest(session.shop, session.accessToken); const products = await client.get({ path: 'products' }); res.json(products); });

4. Building REST and GraphQL Endpoints

4.1 REST Example: Fetch Products

jsCopyEditapp.get('/api/products', verifyRequest(), async (req, res) => {
  const session = await Shopify.Utils.loadCurrentSession(req, res);
  const client = new Shopify.Clients.Rest(session.shop, session.accessToken);
  const response = await client.get({ path: 'products', query: { limit: 10 } });
  res.json(response.body.products);
});

4.2 GraphQL Example: Query Orders

jsCopyEditimport { Shopify } from '@shopify/shopify-api';

app.post('/api/graphql', verifyRequest(), async (req, res) => {
  const session = await Shopify.Utils.loadCurrentSession(req, res);
  const client = new Shopify.Clients.Graphql(session.shop, session.accessToken);
  const data = await client.query({
    data: `{
      orders(first:5) {
        edges { node { id name totalPriceSet { shopMoney { amount currencyCode } } } }
      }
    }`,
  });
  res.json(data.body.data.orders);
});

5. Subscribing to Webhooks

Webhooks let your app respond to store events (e.g., new orders):

  1. Register webhook on install jsCopyEditimport { registerWebhook } from '@shopify/koa-shopify-webhooks'; await registerWebhook({ address: `${HOST}/webhooks/orders/create`, topic: 'ORDERS_CREATE', accessToken, shop, });
  2. Handle incoming webhooks jsCopyEditapp.post('/webhooks/orders/create', koaBody(), async (ctx) => { const order = ctx.request.body; // Process order: fulfillment, notifications, etc. console.log(`New order: ${order.id}`); ctx.res.statusCode = 200; });

6. Adding Embedded App UI with Polaris

  1. Install Polaris bashCopyEditcd web npm install @shopify/polaris @shopify/app-bridge-react
  2. Wrap your App in AppProvider jsxCopyEditimport { AppProvider } from '@shopify/polaris'; import { Provider as AppBridgeProvider } from '@shopify/app-bridge-react'; function MyApp({ Component, pageProps }) { const config = { apiKey: process.env.SHOPIFY_API_KEY, shopOrigin: shopOrigin }; return ( <AppBridgeProvider config={config}> <AppProvider i18n={{}}> <Component {...pageProps} /> </AppProvider> </AppBridgeProvider> ); }
  3. Build a Dashboard Page jsxCopyEditimport { Card, Page, DataTable } from '@shopify/polaris'; function Dashboard({ products }) { const rows = products.map(p => [p.id, p.title, p.vendor]); return ( <Page title="Products"> <Card> <DataTable columnContentTypes={['text','text','text']} headings={['ID','Title','Vendor']} rows={rows} /> </Card> </Page> ); }
  4. Fetch data via API
    In getServerSideProps, call your /api/products endpoint to populate the dashboard.

7. Billing and Subscriptions

If you plan to monetize your app, use the Shopify Billing API:

  1. Create a one-time charge jsCopyEditconst billingResponse = await Shopify.Billing.Invoices.create({ session, lineItems: [{ name: 'Pro Plan', quantity: 1, unitPrice: '20.00' }], test: true, }); ctx.redirect(billingResponse.invoice.paymentUrl);
  2. Verify payment before activating features.

8. Testing and Deployment

  1. Local testing with ngrok bashCopyEditngrok http 3000 Update your app’s URLs in the Partner Dashboard.
  2. Automated tests
    • Use Jest and Supertest for your server endpoints.
    • Cypress for end-to-end UI flows within the embedded app.
  3. Deploy to production
    • Push code to a repository (GitHub, GitLab).
    • Use CI/CD pipelines to build and deploy to Heroku, Vercel, or your preferred host.
    • Secure environment variables (API keys, secrets) via your platform’s secrets manager.

Conclusion

Building a custom Shopify app with the CLI and Node.js gives you the power to extend merchant stores with tailored functionality—whether automating order workflows, integrating third-party services, or adding rich embedded UIs. By following this guide, you’ve learned how to scaffold your app, authenticate via OAuth, expose REST and GraphQL endpoints, handle webhooks, create a polished Polaris-based admin interface, and deploy your solution. From here, explore deeper features—script tags, theme extensions, and Admin UI extensions—to create differentiated, high-value Shopify apps. Happy coding!

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts