Shopify Hydrogen กับ Infrastructure as Code —
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
- Caching: ใช้ Hydrogen Cache API สำหรับ Storefront API Responses
- CDN: ใช้ CloudFront/Cloudflare Cache Static Assets และ Pages
- IaC: ใช้ Terraform จัดการ Infrastructure ทั้งหมด Version Control ด้วย Git
- Environment: แยก Environment (dev/staging/prod) ด้วย Terraform Workspaces
- Monitoring: ใช้ CloudWatch + Datadog/New Relic Monitor Performance
- Security: เก็บ API Keys ใน Secrets Manager ไม่ Hardcode ใน Code
Shopify Hydrogen คืออะไร
React Framework สำหรับ Headless Commerce Shopify Remix Foundation SSR Streaming Caching Storefront API Performance สูง Custom Storefront