# Build Secure Shopify Apps with App Bridge

As a developer working with Shopify's ecosystem, I recently built a multi-tenant SaaS application that synchronizes customer data between Shopify stores and external services. In this article, I'll share my experience and technical insights into creating a secure, scalable Shopify app using App Bridge.

## Project Overview

Our application needed to:
- Handle multiple Shopify stores (multi-tenancy)
- Process customer data securely
- Provide a seamless embedded experience
- Manage OAuth flows and webhooks
- Handle billing subscriptions

Let's dive into how we accomplished these requirements using Rails 8 and Shopify's App Bridge.

## Setting Up the Foundation

First, we set up our Rails application with the necessary Shopify integrations. Here's how our initial configuration looked:

```ruby
ShopifyApp.configure do |config|
  config.embedded_app = true
  config.scope = "read_customers,write_customers"
  config.after_authenticate_job = false
  config.api_version = "2024-10"
  config.shop_session_repository = "Shop"
  config.webhooks = [
    { topic: "app/uninstalled", address: "webhooks/app_uninstalled" },
    { topic: "customers/create", address: "webhooks/customers_create" },
    { topic: "customers/update", address: "webhooks/customers_update" }
  ]
end
```

## Multi-tenant Data Model

Our core data model revolves around the `Shop` model, which handles multi-tenancy:

```ruby
class Shop < ActiveRecord::Base
  include ShopifyApp::ShopSessionStorageWithScopes
  
  has_many :customers, dependent: :destroy
  
  encrypts :api_key, deterministic: true
end
```

## Embedded App Architecture

One of the key aspects was creating a seamless embedded experience. We achieved this through our layout configuration:

```erb
<%# app/views/layouts/embedded_app.html.erb %>
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/@shopify/app-bridge@3.7.9"></script>
    <script>
      var AppBridge = window['app-bridge'];
      var createApp = AppBridge.default;
      var app = createApp({
        apiKey: "<%= ShopifyApp.configuration.api_key %>",
        host: "<%= @host %>",
        forceRedirect: true
      });
    </script>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

## Secure Authentication Flow

We implemented authenticated controllers to ensure secure access:

```ruby
class AuthenticatedController < ApplicationController
  include ShopifyApp::EnsureHasSession
  
  before_action :ensure_store_settings
  
  private
  
  def ensure_store_settings
    redirect_to settings_path unless current_shop.setup_completed?
  end
end
```

## Webhook Processing

Handling webhooks securely was crucial for our app. Here's our webhook processing implementation:

```ruby
class WebhooksController < ApplicationController
  include ShopifyApp::WebhookVerification

  def customers_create
    shop = Shop.find_by(shopify_domain: params[:shop_domain])
    
    if shop
      shop.with_shopify_session do
        process_customer_data(params)
      end
    end
    
    head :ok
  end
  
  private
  
  def process_customer_data(data)
    CustomerProcessingJob.perform_later(
      shop_id: shop.id,
      customer_data: data
    )
  end
end
```

## Background Job Processing

We used Solid Queue for reliable background processing:

```ruby
class CustomerProcessingJob < ApplicationJob
  def perform(shop_id:, customer_data:)
    shop = Shop.find(shop_id)
    
    shop.with_shopify_session do
      customer = shop.customers.find_or_initialize_by(
        shopify_customer_id: customer_data["id"]
      )
      
      customer.update!(
        email: customer_data["email"],
        first_name: customer_data["first_name"],
        last_name: customer_data["last_name"]
      )
    end
  end
end
```

## Billing Integration

We implemented Shopify's billing API to handle subscriptions:

```ruby
config.billing = ShopifyApp::BillingConfiguration.new(
  charge_name: "App Subscription",
  amount: 4.99,
  interval: ShopifyApp::BillingConfiguration::INTERVAL_EVERY_30_DAYS,
  trial_days: 7
)
```

## User Interface with Tailwind CSS

We created a clean, responsive interface using Tailwind CSS:

```erb
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-12 pt-20">
  <div class="sm:flex sm:items-center">
    <div class="sm:flex-auto">
      <h1 class="text-base font-semibold leading-6 text-gray-900">
        Customers
      </h1>
      <p class="mt-2 text-sm text-gray-700">
        Manage your synchronized customers
      </p>
    </div>
  </div>
  
  <%= render "customer_list", customers: @customers %>
</div>
```

## Lessons Learned

Throughout this project, I learned several valuable lessons:

1. **Session Management**: Always use Shopify's session tokens for authentication rather than storing raw access tokens.

2. **Webhook Reliability**: Implement idempotency in webhook processing to handle potential duplicate events.

3. **Background Jobs**: Use background jobs for any operations that might take more than a few seconds to complete.

4. **Error Handling**: Implement comprehensive error handling and logging, especially for webhook processing and API calls.

5. **Security**: Always encrypt sensitive data and never expose API keys in the frontend.

## Conclusion

Building a multi-tenant Shopify app requires careful consideration of security, scalability, and user experience. By leveraging Rails, App Bridge, and modern development practices, we created a robust application that securely handles multiple stores and their data.

The combination of Shopify's App Bridge, Rails 8, and modern tools like Solid Queue and Tailwind CSS provided a solid foundation for building a scalable SaaS application.

## Next Steps

If you're building a Shopify app, consider these recommendations:

1. Start with a solid authentication and authorization system
2. Implement webhook handling early in the development process
3. Use background jobs for long-running tasks
4. Plan for scalability from the beginning
5. Follow Shopify's security best practices

Remember that building a multi-tenant application requires careful consideration of data isolation and security at every level of your application.

---

Happy Coding!
