| Crates.io | shopify-client |
| lib.rs | shopify-client |
| version | 0.14.0 |
| created_at | 2025-12-10 20:53:58.791532+00 |
| updated_at | 2026-01-25 10:18:09.972311+00 |
| description | Modular Shopify library for Rust: API client, type-safe models, and webhook parsing utilities |
| homepage | https://github.com/sinha-sahil/shopify-rust-client |
| repository | https://github.com/sinha-sahil/shopify-rust-client |
| max_upload_size | |
| id | 1978779 |
| size | 170,996 |
A Rust client library for interacting with the Shopify Admin API. Supports both REST and GraphQL APIs with a focus on app development and order management.
reqwest for asynchronous HTTP requestsAdd this to your Cargo.toml:
[dependencies]
shopify-client = "0.1.0"
Or install directly:
cargo add shopify-client
The library exposes the following public modules:
ShopifyClient - Main client for making API callstypes - All type definitions organized by resource (e.g., types::order, types::subscription)webhooks - Webhook parsing utilities and typesBeforeRequestCallback, AfterRequestCallback, RequestCallbacksuse shopify_client::ShopifyClient; // Main client
use shopify_client::types::order::*; // Order types
use shopify_client::types::subscription::*; // Subscription types
use shopify_client::webhooks::*; // Webhook utilities
use shopify_client::{BeforeRequestCallback, AfterRequestCallback}; // Callback types
use shopify_client::ShopifyClient;
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None, // Optional API version, defaults to "2026-01"
);
use shopify_client::ShopifyClient;
use std::sync::Arc;
let before_request = Arc::new(|url: &str, body: Option<&str>, _headers: &reqwest::header::HeaderMap| {
println!("→ Request to {}", url);
if let Some(body) = body {
println!(" Body: {}", body);
}
});
let after_request = Arc::new(|url: &str, response: &str, _headers: &reqwest::header::HeaderMap| {
println!("← Response from {} ({} bytes)", url, response.len());
});
let client = ShopifyClient::new_with_callbacks(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
Some(before_request),
Some(after_request),
);
use shopify_client::ShopifyClient;
#[tokio::main]
async fn main() {
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
);
let order_id = "1234567890".to_string();
match client.order.get_with_id(&order_id).await {
Ok(response) => {
println!("Order: {:?}", response.order);
}
Err(e) => {
eprintln!("Error: {:?}", e);
}
}
}
use shopify_client::ShopifyClient;
#[tokio::main]
async fn main() {
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
);
let order_name = "1001".to_string(); // Without the # prefix
match client.order.get_with_name(&order_name).await {
Ok(response) => {
println!("Orders found: {}", response.orders.len());
for order in response.orders {
println!("Order {}: {}", order.name, order.id);
}
}
Err(e) => {
eprintln!("Error: {:?}", e);
}
}
}
use shopify_client::ShopifyClient;
use shopify_client::types::order::{PatchOrderRequest, PatchOrder};
#[tokio::main]
async fn main() {
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
);
let order_id = "1234567890".to_string();
let patch_request = PatchOrderRequest {
order: PatchOrder {
tags: vec!["processed".to_string(), "priority".to_string()],
},
};
match client.order.patch(&order_id, &patch_request).await {
Ok(response) => {
println!("Order updated: {:?}", response.order);
}
Err(e) => {
eprintln!("Error: {:?}", e);
}
}
}
use shopify_client::ShopifyClient;
use shopify_client::types::subscription::{CreateRecurringSubscriptionRequest, AppPricingInterval};
#[tokio::main]
async fn main() {
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
);
let request = CreateRecurringSubscriptionRequest {
name: "Premium Plan".to_string(),
price: 29.99,
currency_code: "USD".to_string(),
return_url: "https://your-app.com/billing".to_string(),
interval: Some(AppPricingInterval::Every30Days),
trial_days: Some(7),
test: Some(true),
discount: None,
};
match client.subscription.create_recurring(&request).await {
Ok(response) => {
println!("Subscription created: {:?}", response);
println!("Confirmation URL: {}", response.confirmation_url);
}
Err(e) => {
eprintln!("Error: {:?}", e);
}
}
}
use shopify_client::ShopifyClient;
use shopify_client::types::discount::DiscountAutomaticAppInput;
#[tokio::main]
async fn main() {
let client = ShopifyClient::new(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
);
let input = DiscountAutomaticAppInput {
title: "Summer Sale".to_string(),
function_handle: "my-discount-function".to_string(),
starts_at: "2024-06-01T00:00:00Z".to_string(),
ends_at: None,
combines_with: None,
discount_classes: None,
context: None,
metafields: None,
applies_on_subscription: None,
applies_on_one_time_purchase: None,
recurring_cycle_limit: None,
};
match client.discount.create_automatic_app_discount(&input).await {
Ok(response) => {
println!("Discount created: {:?}", response);
}
Err(e) => {
eprintln!("Error: {:?}", e);
}
}
}
use shopify_client::webhooks::{parse_webhook_with_header, WebhookPayload};
fn handle_webhook(topic_header: &str, payload: &str) {
match parse_webhook_with_header(topic_header, payload) {
Ok(WebhookPayload::CustomersDataRequest(data)) => {
println!("Customer data request for shop: {}", data.shop_domain);
println!("Customer: {:?}", data.customer);
println!("Orders requested: {:?}", data.orders_requested);
}
Ok(WebhookPayload::CustomersRedact(data)) => {
println!("Customer redact request for shop: {}", data.shop_domain);
println!("Customer to redact: {:?}", data.customer);
println!("Orders to redact: {:?}", data.orders_to_redact);
}
Ok(WebhookPayload::ShopRedact(data)) => {
println!("Shop redact request for shop: {}", data.shop_domain);
println!("Shop ID: {}", data.shop_id);
}
Err(e) => {
eprintln!("Failed to parse webhook: {:?}", e);
}
}
}
The library is organized into a modular structure for easy extensibility:
src/
├── lib.rs # Main client entry point and exports
├── types/ # Public type definitions
│ ├── mod.rs # Type module exports
│ ├── order.rs # Order types (REST)
│ ├── subscription.rs # Subscription types (GraphQL)
│ ├── discount.rs # Discount types (GraphQL)
│ ├── app_installation.rs # App installation types (GraphQL)
│ ├── cart_transform.rs # Cart transform types (GraphQL)
│ └── shopify_functions.rs # Shopify functions types (GraphQL)
├── webhooks/ # Public webhook parsing module
│ ├── mod.rs # Webhook parsing functions
│ └── types.rs # Webhook payload types
├── common/ # Internal shared utilities (private)
│ ├── mod.rs
│ ├── types.rs # Common types (APIError, RequestCallbacks)
│ ├── utils.rs # Utility functions
│ └── http.rs # Centralized GraphQL execution
└── services/ # Internal API services (private)
├── order/ # Order service (REST)
├── subscription/ # Subscription service (GraphQL)
├── discount/ # Discount service (GraphQL)
├── app_installation/ # App installation service (GraphQL)
├── cart_transform/ # Cart transform service (GraphQL)
└── shopify_functions/ # Shopify functions service (GraphQL)
├── mod.rs # Service struct with public methods
└── remote.rs # Internal API implementation
ShopifyClient once and access services through itShopifyClient, types, and webhookscommon, services) is privateThe library provides strongly-typed models organized by resource:
types::order)types::subscription)types::discount)types::app_installation)types::cart_transform)types::shopify_functions)webhooks::types)The library uses a custom APIError enum for comprehensive error handling:
pub enum APIError {
ServerError { errors: String },
FailedToParse,
NetworkError,
}
Errors are returned through Result<T, APIError> for easy error propagation and handling.
This client requires a Shopify Admin API access token. You can obtain one by:
read_orders, write_orders)The library supports optional before/after request callbacks for logging, monitoring, and observability:
use shopify_client::ShopifyClient;
use std::sync::Arc;
let before = Arc::new(|url: &str, body: Option<&str>, headers: &reqwest::header::HeaderMap| {
println!("Making request to: {}", url);
if let Some(body) = body {
println!("Request body: {}", body);
}
});
let after = Arc::new(|url: &str, response_body: &str, headers: &reqwest::header::HeaderMap| {
println!("Received response from: {}", url);
println!("Response size: {} bytes", response_body.len());
});
let client = ShopifyClient::new_with_callbacks(
"https://your-shop.myshopify.com".to_string(),
"your-access-token".to_string(),
None,
Some(before),
Some(after),
);
Features:
new() for no callbacks, new_with_callbacks() for callbacksCurrently uses Shopify Admin API version 2026-01.
reqwest (with JSON support)serde (with derive feature)serde_jsonContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under your preferred license.
This is an unofficial Shopify client library. For official SDKs and documentation, visit Shopify's Developer Documentation.