SiamCafe.net Blog
Technology

Shopify Hydrogen Infrastructure as Code

shopify hydrogen infrastructure as code
Shopify Hydrogen Infrastructure as Code | SiamCafe Blog
2025-09-13· อ. บอม — SiamCafe.net· 9,933 คำ

Shopify Hydrogen

Shopify Hydrogen เป็น React Framework สำหรับ Headless Commerce ใช้ Remix Foundation รองรับ SSR Streaming Caching เชื่อมกับ Storefront API Performance สูง

Infrastructure as Code จัดการ Infrastructure ด้วย Code Terraform Pulumi สร้าง Servers CDN อัตโนมัติ Version Control Reproducible

Hydrogen Project Setup

# === Shopify Hydrogen Setup ===

# 1. สร้าง Project
npm create @shopify/hydrogen@latest -- --template demo-store
cd my-hydrogen-store

# 2. Project Structure
# my-hydrogen-store/
# ├── app/
# │   ├── components/     # React Components
# │   ├── routes/         # Remix Routes
# │   ├── styles/         # CSS/Tailwind
# │   ├── lib/            # Utilities
# │   └── entry.server.tsx
# ├── public/             # Static files
# ├── .env                # Environment variables
# ├── hydrogen.config.ts  # Hydrogen config
# ├── remix.config.js     # Remix config
# ├── tailwind.config.js  # Tailwind config
# └── package.json

# 3. Environment Variables (.env)
# SESSION_SECRET=your-session-secret
# PUBLIC_STOREFRONT_API_TOKEN=your-storefront-token
# PUBLIC_STORE_DOMAIN=your-store.myshopify.com
# PRIVATE_STOREFRONT_API_TOKEN=your-private-token

# 4. Development
npm run dev
# http://localhost:3000

# 5. Build
npm run build

# 6. Preview Production Build
npm run preview

# 7. Storefront API Query Example
# app/routes/products.$handle.tsx
# import { json, type LoaderFunctionArgs } from '@shopify/remix-oxygen';
# import { useLoaderData } from '@remix-run/react';
#
# export async function loader({ params, context }: LoaderFunctionArgs) {
#   const { product } = await context.storefront.query(PRODUCT_QUERY, {
#     variables: { handle: params.handle },
#   });
#   if (!product) throw new Response('Not Found', { status: 404 });
#   return json({ product });
# }
#
# const PRODUCT_QUERY = `#graphql
#   query Product($handle: String!) {
#     product(handle: $handle) {
#       id
#       title
#       description
#       priceRange {
#         minVariantPrice { amount currencyCode }
#       }
#       images(first: 5) {
#         nodes { url altText width height }
#       }
#       variants(first: 10) {
#         nodes { id title price { amount currencyCode } availableForSale }
#       }
#     }
#   }
# `;

echo "Hydrogen Project created"
echo "  Framework: Remix + React"
echo "  API: Shopify Storefront GraphQL"
echo "  Dev: npm run dev (localhost:3000)"

Terraform Infrastructure

# === Terraform สำหรับ Hydrogen Deployment ===

# main.tf — Infrastructure สำหรับ Hydrogen on AWS
# terraform {
#   required_providers {
#     aws = { source = "hashicorp/aws", version = "~> 5.0" }
#     cloudflare = { source = "cloudflare/cloudflare", version = "~> 4.0" }
#   }
#   backend "s3" {
#     bucket = "terraform-state-hydrogen"
#     key    = "hydrogen/terraform.tfstate"
#     region = "ap-southeast-1"
#   }
# }
#
# provider "aws" { region = "ap-southeast-1" }
#
# # ECR Repository
# resource "aws_ecr_repository" "hydrogen" {
#   name                 = "hydrogen-store"
#   image_tag_mutability = "MUTABLE"
#   image_scanning_configuration { scan_on_push = true }
# }
#
# # ECS Cluster
# resource "aws_ecs_cluster" "main" {
#   name = "hydrogen-cluster"
#   setting { name = "containerInsights" value = "enabled" }
# }
#
# # ECS Task Definition
# resource "aws_ecs_task_definition" "hydrogen" {
#   family                   = "hydrogen-store"
#   network_mode             = "awsvpc"
#   requires_compatibilities = ["FARGATE"]
#   cpu                      = 512
#   memory                   = 1024
#   execution_role_arn       = aws_iam_role.ecs_execution.arn
#
#   container_definitions = jsonencode([{
#     name      = "hydrogen"
#     image     = ":latest"
#     essential = true
#     portMappings = [{ containerPort = 3000, hostPort = 3000 }]
#     environment = [
#       { name = "SESSION_SECRET", value = var.session_secret },
#       { name = "PUBLIC_STORE_DOMAIN", value = var.store_domain },
#     ]
#     logConfiguration = {
#       logDriver = "awslogs"
#       options = {
#         "awslogs-group"  = "/ecs/hydrogen"
#         "awslogs-region" = "ap-southeast-1"
#       }
#     }
#   }])
# }
#
# # ALB
# resource "aws_lb" "hydrogen" {
#   name               = "hydrogen-alb"
#   internal           = false
#   load_balancer_type = "application"
#   security_groups    = [aws_security_group.alb.id]
#   subnets            = var.public_subnets
# }
#
# # CloudFront CDN
# resource "aws_cloudfront_distribution" "hydrogen" {
#   origin {
#     domain_name = aws_lb.hydrogen.dns_name
#     origin_id   = "hydrogen-alb"
#     custom_origin_config {
#       http_port              = 80
#       https_port             = 443
#       origin_protocol_policy = "https-only"
#       origin_ssl_protocols   = ["TLSv1.2"]
#     }
#   }
#   enabled             = true
#   default_cache_behavior {
#     allowed_methods        = ["GET", "HEAD", "OPTIONS"]
#     cached_methods         = ["GET", "HEAD"]
#     target_origin_id       = "hydrogen-alb"
#     viewer_protocol_policy = "redirect-to-https"
#     cache_policy_id        = "658327ea-f89d-4fab-a63d-7e88639e58f6"
#   }
# }

# variables.tf
# variable "session_secret" { type = string sensitive = true }
# variable "store_domain" { type = string }
# variable "public_subnets" { type = list(string) }

# terraform init
# terraform plan
# terraform apply

infrastructure = {
    "Compute": "AWS ECS Fargate (512 CPU, 1GB RAM)",
    "Container": "ECR (Docker Registry)",
    "Load Balancer": "ALB (Application Load Balancer)",
    "CDN": "CloudFront (Global Edge)",
    "DNS": "Route53 / Cloudflare",
    "Monitoring": "CloudWatch + Container Insights",
    "State": "S3 Backend with DynamoDB Lock",
}

print("Terraform Infrastructure:")
for component, detail in infrastructure.items():
    print(f"  {component}: {detail}")

CI/CD Pipeline

# === GitHub Actions CI/CD สำหรับ Hydrogen ===

# .github/workflows/deploy.yml
# name: Deploy Hydrogen
# on:
#   push:
#     branches: [main]
#
# env:
#   AWS_REGION: ap-southeast-1
#   ECR_REPOSITORY: hydrogen-store
#   ECS_CLUSTER: hydrogen-cluster
#   ECS_SERVICE: hydrogen-service
#
# jobs:
#   test:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       - uses: actions/setup-node@v4
#         with:
#           node-version: '20'
#           cache: 'npm'
#       - run: npm ci
#       - run: npm run typecheck
#       - run: npm run lint
#       - run: npm test
#
#   build-deploy:
#     needs: test
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#
#       - name: Configure AWS
#         uses: aws-actions/configure-aws-credentials@v4
#         with:
#           aws-access-key-id: }
#           aws-secret-access-key: }
#           aws-region: }
#
#       - name: Login to ECR
#         id: ecr
#         uses: aws-actions/amazon-ecr-login@v2
#
#       - name: Build and Push
#         env:
#           ECR_REGISTRY: }
#           IMAGE_TAG: }
#         run: |
#           docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
#           docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
#
#       - name: Deploy to ECS
#         run: |
#           aws ecs update-service \
#             --cluster $ECS_CLUSTER \
#             --service $ECS_SERVICE \
#             --force-new-deployment
#
#       - name: Invalidate CloudFront
#         run: |
#           aws cloudfront create-invalidation \
#             --distribution-id } \
#             --paths "/*"

# Dockerfile
# FROM node:20-alpine AS build
# WORKDIR /app
# COPY package*.json ./
# RUN npm ci
# COPY . .
# RUN npm run build
#
# FROM node:20-alpine
# WORKDIR /app
# COPY --from=build /app/build ./build
# COPY --from=build /app/node_modules ./node_modules
# COPY --from=build /app/package.json ./
# EXPOSE 3000
# CMD ["npm", "run", "start"]

pipeline_stages = [
    {"stage": "Test", "steps": ["Typecheck", "Lint", "Unit Tests"]},
    {"stage": "Build", "steps": ["Docker Build", "Push to ECR"]},
    {"stage": "Deploy", "steps": ["Update ECS Service", "Rolling Update"]},
    {"stage": "Post-deploy", "steps": ["CloudFront Invalidation", "Health Check"]},
]

print("CI/CD Pipeline:")
for p in pipeline_stages:
    print(f"  {p['stage']}:")
    for step in p['steps']:
        print(f"    - {step}")

Best Practices

Shopify Hydrogen คืออะไร

React Framework สำหรับ Headless Commerce Shopify Remix Foundation SSR Streaming Caching Storefront API Performance สูง Custom Storefront

Infrastructure as Code คืออะไร

จัดการ Infrastructure ด้วย Code แทน Manual Terraform Pulumi CloudFormation สร้าง Servers Databases CDN อัตโนมัติ Git Version Control Reproducible ลด Human Error

Headless Commerce คืออะไร

แยก Frontend ออกจาก Backend เชื่อม API ปรับ Frontend อิสระ Framework ใดก็ได้ Performance ดีกว่า Theme ปกติ ร้านค้าขนาดใหญ่ Custom Experience

Deploy Hydrogen ไปที่ไหนได้บ้าง

Shopify Oxygen Official ฟรี Shopify Plus Vercel Netlify Cloudflare Workers AWS Lambda@Edge Google Cloud Run Docker Container IaC จัดการ Deployment

สรุป

Shopify Hydrogen ให้ Headless Commerce ที่ Performance สูง Remix React SSR IaC ด้วย Terraform จัดการ AWS ECS CloudFront CDN CI/CD ด้วย GitHub Actions Docker Container Caching ด้วย Hydrogen Cache API แยก Environment ด้วย Workspaces

📖 บทความที่เกี่ยวข้อง

Shopify Hydrogen Hexagonal Architectureอ่านบทความ → Shopify Hydrogen Pub Sub Architectureอ่านบทความ → PHP Filament Infrastructure as Codeอ่านบทความ → Cloudflare Low Code No Codeอ่านบทความ → Grafana Tempo Traces Infrastructure as Codeอ่านบทความ →

📚 ดูบทความทั้งหมด →