I Built a CLI So AI Agents Can Manage My Shopify Store

I use AI tools for most of my software engineering work now. But every time I needed to do something in Shopify (check an order, update a product, look up a customer) I had to leave the terminal, open the admin dashboard, and click around. The AI could write code, run tests, manage deployments, but it couldn’t touch my store.

So I built shopq: a zero-dependency CLI for the Shopify Admin API, designed from the ground up to work with AI agents.

The Gap

There are three ways to interact with a Shopify store, and none of them fit an AI-first workflow.

The admin dashboard is point-and-click. Sure, you could throw a computer-use agent at it, but a direct CLI will always be simpler and faster than an AI trying to navigate a web UI.

The GraphQL Admin API is powerful, and AI actually works with it pretty well. But building and maintaining a full client around it is overkill if you just want to manage your store. You shouldn’t need to scaffold a whole project to list your products.

Shopify’s own CLI is built for app and theme development, not store management. It doesn’t help you query orders or update products.

I wanted to manage my Shopify store the same way I manage everything else in my dev workflow: from the terminal, with AI doing the heavy lifting.

Built for Agents

shopq was built with AI agents as the primary user.

That means: structured JSON output (--json), predictable exit codes, no interactive prompts, no ambiguity. Everything an agent needs to run a command, parse the result, and decide what to do next.

It also ships with a SKILL.md, so AI tools that support skills can pick it up and understand how to use it immediately. It’s listed in the pi directory too, so users with compatible AI terminals can install the skill and CLI together and start working right away.

Zero dependencies. Just Bun’s built-in capabilities. Install it and go.

What shopq Does

shopq wraps the Shopify Admin GraphQL API and exposes it as simple, composable commands. Products, orders, collections, pages, files, themes. The things you actually touch day-to-day.

# install globally (works with Bun v1.3+ or Node.js v22+)
bun install -g shopq

# store info
shopq shop get

# products
shopq product list
shopq product get "Summer Tee"
shopq product create --title "New Item" --vendor "My Brand"
shopq product update "Summer Tee" --status active

# pages, collections, menus
shopq page list
shopq collection get "frontpage"
shopq menu list

# structured output for piping and automation
shopq product list --json

No GraphQL knowledge required. No browser. Structured, predictable output that both humans and AI agents can work with.

And for the cases where existing commands don’t cover what you need, there’s a direct GraphQL escape hatch:

shopq gql "{ shop { name email } }"
shopq gql - < query.graphql
shopq gql "mutation(...)" --variables '{"id":"..."}'

This covers the long tail. AI is actually great at writing one-off GraphQL queries, so between the built-in commands and gql, you can do pretty much anything the Admin API supports.

Try It

bun install -g shopq

Full source and docs: github.com/c99e/shopq

If you’re building on Shopify and using AI tools in your workflow, give it a try. I’m actively developing it and want to hear what commands or workflows would be most useful.

What Shopify tasks would you want your AI agent to handle?

👋 Hello Dev.to! I’m a Java Full Stack Developer with 8+ Years of Experience

Hi everyone! I’m Sujit Kumar Mishra, a Java Full Stack Developer based in Bhubaneswar, India, with over 8 years of experience building scalable enterprise applications.

I specialize in backend-heavy systems using Java and Spring technologies, while also working across the full stack to deliver complete solutions—from design to deployment.

💼 My Tech Stack

  • Java, Spring Boot, Spring MVC, Spring Security
  • REST APIs & Microservices (learning & implementing)
  • JPA, Hibernate
  • ReactJS, JSP, JavaScript, Bootstrap
  • Oracle, MySQL, PostgreSQL, MongoDB
  • Maven, Gradle, Git, JIRA
  • Apache Tomcat, WebLogic, Azure, DigitalOcean

🤖 Exploring Generative AI

Recently, I’ve been actively working on AI-powered applications:

  • Built AI chatbot platforms
  • Created AI-based code review tools
  • Integrated Generative AI with Spring Boot applications
  • Deploying AI apps on cloud platforms for scalability

🚀 Some Things I’ve Worked On

  • Enterprise banking applications
  • Government e-governance portals
  • E-commerce platforms
  • Real-time dashboards
  • AI-powered developer tools
  • REST API backends for mobile apps

🎯 What I’ll Be Sharing Here

I plan to write about:

  • Spring Boot best practices
  • Real-world backend architecture
  • Microservices learning journey
  • Generative AI with Java
  • Performance optimization tips
  • Debugging production issues
  • Clean code & enterprise development

🙌 Let’s Connect

I’m here to learn, share, and collaborate with the developer community.
Looking forward to contributing practical, real-world Java and backend development insights.

Thanks for reading! 🚀

Mastering Zero-Downtime Deployments with Terraform

How I Updated Production Without Taking My App Offline (Almost)

Day 12 of the 30-Day Terraform Challenge — and today I learned the difference between “it works” and “it works without anyone noticing.”

Deploying infrastructure updates without downtime is one of the hardest problems in operations. It’s also one of the most valuable skills you can have. Today I made it happen.

Well, almost. Let me show you what worked, what broke, and what I learned.

The Problem: Terraform’s Default Behavior is Destructive

When you update a Launch Template or Auto Scaling Group, Terraform does something terrifying by default:

1. Destroy old ASG → All instances terminate → Website goes down ❌
2. Create new ASG → New instances spin up → Website comes back

Between step 1 and step 2, there’s a window of silence. Sometimes seconds. Sometimes minutes. In production, that’s a disaster.

The fix? A little lifecycle rule called create_before_destroy.

The Solution: create_before_destroy

This simple lifecycle block flips the order:

lifecycle {
  create_before_destroy = true
}

Instead of destroy → create, it does:

1. Create new resources
2. Wait for them to be healthy
3. Destroy old resources

No gap. No downtime.

The ASG Naming Problem

There’s a catch. AWS won’t let two Auto Scaling Groups have the same name. When create_before_destroy creates the new ASG before destroying the old one, they’d both have the same name and Terraform would error.

The fix: Use name_prefix instead of a fixed name:

name_prefix = "web-asg-${random_id.asg.hex}-"

Add a random_id that changes when your code changes:

resource "random_id" "asg" {
  keepers = {
    # New ID when user_data changes
    user_data = base64encode(file("${path.module}/user-data.sh"))
  }
  byte_length = 4
}

Now each deployment gets a unique name. Problem solved.

The Test: Updating from v1 to v2

I set up a traffic monitor in one terminal:

while true; do
  curl http://my-alb-dns-name
  sleep 2
done

In another terminal, I updated my user_data script and ran terraform apply.

Here’s what happened:

<h1>Version 1: Hello World!</h1> - 14:34:04
<h1>Version 1: Hello World!</h1> - 14:34:06
<h1>Version 1: Hello World!</h1> - 14:34:08
...
502 Bad Gateway - 14:35:08    # Uh oh!
502 Bad Gateway - 14:35:10
502 Bad Gateway - 14:35:12
...
<h1>Version 2: Updated! Zero Downtime Works!</h1> - 14:35:25
<h1>Version 2: Updated! Zero Downtime Works!</h1> - 14:35:27

What happened? I got 17 seconds of 502 errors. Not quite zero downtime.

Why Did I Get Errors?

The new instances took longer to become healthy than the old instances took to terminate. The ALB had no healthy targets for a brief window.

The fix: Increase health_check_grace_period and add wait_for_capacity_timeout:

resource "aws_autoscaling_group" "web" {
  health_check_grace_period = 300   # 5 minutes to boot
  wait_for_capacity_timeout = "10m" # Wait for healthy instances

  lifecycle {
    create_before_destroy = true
  }
}

With these settings, Terraform waits until the new instances pass health checks before destroying the old ones. No more 502s.

The Blue/Green Alternative

create_before_destroy is great, but it has limitations. For mission-critical apps, teams use blue/green deployments:

  • Blue = current live environment
  • Green = new version, ready to go

Switch traffic instantly with a listener rule:

resource "aws_lb_listener_rule" "blue_green" {
  action {
    type = "forward"
    target_group_arn = var.active_environment == "blue" ? 
      aws_lb_target_group.blue.arn : aws_lb_target_group.green.arn
  }
}

Change active_environment = "green", run terraform apply, and traffic switches in a single API call. Zero downtime. Instant rollback.

What I Learned

The default is dangerous. Never assume Terraform will keep your app online. Always test.

create_before_destroy is your friend. But you need to handle ASG naming with name_prefix and random_id.

Health checks matter. Give your instances time to boot. health_check_grace_period = 300 is your safety net.

Blue/green is cleaner. More setup, but atomic switches and instant rollbacks.

502s taught me more than success would have. I saw exactly where my setup failed and how to fix it.

The Code That Worked

# Unique ASG name
resource "random_id" "asg" {
  keepers = { user_data = base64encode(file("user-data.sh")) }
  byte_length = 4
}

resource "aws_autoscaling_group" "web" {
  name_prefix = "web-asg-${random_id.asg.hex}-"
  health_check_grace_period = 300
  wait_for_capacity_timeout = "10m"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_launch_template" "web" {
  lifecycle {
    create_before_destroy = true
  }
}

The Bottom Line

Zero-downtime deployments aren’t magic. They’re a combination of:

  • The right lifecycle rules (create_before_destroy)
  • Smart naming (name_prefix + random_id)
  • Patience (health_check_grace_period, wait_for_capacity_timeout)
  • The right strategy (blue/green for critical apps)

Today I learned that “it works” isn’t enough. It has to work when nobody’s watching. And now I know how to make that happen.

P.S. Those 502 errors were humbling. But they taught me more than a perfect run ever would. Sometimes failure is the best teacher.