# AWS Step Functions Cookbook

By Jag Reehal, 2024-11-01. Canonical: https://arrangeactassert.com/posts/aws-step-functions-cookbook/

<!-- Excerpt Start -->

This is my AWS Step Functions Mapping and Patterns and cookbook covering data manipulation, concurrency patterns, error handling, and advanced workflows.

<!-- Excerpt End -->

## Table of Contents

1. [Basic Concepts](#basic-concepts)
2. [Path Processing Fundamentals](#path-processing-fundamentals)
3. [Concurrency Patterns](#concurrency-patterns)
4. [Advanced State Patterns](#advanced-state-patterns)
5. [Complex Workflows](#complex-workflows)
6. [Error Handling & Resilience](#error-handling--resilience)
7. [Integration Patterns](#integration-patterns)
8. [Best Practices & Tips](#best-practices--tips)

---

## Basic Concepts

### Processing Order Visualization

```txt
Input → InputPath → Parameters → Task → ResultSelector → ResultPath → OutputPath → Output
```

### JSONPath Quick Reference

```json
// First book's title
$.store.book[0].title

// All book titles
$.store.book[*].title

// Books under $10
$.store.book[?(@.price < 10)]

// All authors
$..author

// Number of books
$.store.book.length()
```

---

## Path Processing Fundamentals

AWS Step Functions allows you to manipulate the data passing through your state machine at various stages using `InputPath`, `Parameters`, `ResultSelector`, `ResultPath`, and `OutputPath`.

### 1. InputPath with Complex Filtering

#### Example: Filter Nested Arrays

**Initial State Input:**

```json
{
  "orders": [
    { "id": 1, "status": "PENDING", "amount": 100 },
    { "id": 2, "status": "SHIPPED", "amount": 200 },
    { "id": 3, "status": "PENDING", "amount": 150 }
  ],
  "metadata": {
    "requestId": "abc-123"
  }
}
```

**State Definition with `InputPath`:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessPendingOrders",
  "InputPath": "$.orders[?(@.status == 'PENDING')]",
  "ResultPath": "$.processedOrders",
  "End": true
}
```

**Lambda Receives:**

```json
[
  { "id": 1, "status": "PENDING", "amount": 100 },
  { "id": 3, "status": "PENDING", "amount": 150 }
]
```

**If Lambda Returns:**

```json
{
  "processedCount": 2,
  "totalAmount": 250
}
```

**Resulting State Output:**

```json
{
  "orders": [
    { "id": 1, "status": "PENDING", "amount": 100 },
    { "id": 2, "status": "SHIPPED", "amount": 200 },
    { "id": 3, "status": "PENDING", "amount": 150 }
  ],
  "metadata": {
    "requestId": "abc-123"
  },
  "processedOrders": {
    "processedCount": 2,
    "totalAmount": 250
  }
}
```

#### Example: Multiple Field Selection

**Initial State Input:**

```json
{
  "orderId": "order-456",
  "items": [
    { "productId": "prod-1", "quantity": 2 },
    { "productId": "prod-2", "quantity": 1 }
  ],
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "zip": "12345"
  },
  "customerInfo": {
    "name": "John Doe",
    "email": "john@example.com"
  }
}
```

**State Definition with `Parameters`:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessOrderDetails",
  "Parameters": {
    "orderDetails": {
      "id.$": "$.orderId",
      "items.$": "$.items[*].productId",
      "shipping.$": "$.address"
    },
    "customerEmail.$": "$.customerInfo.email"
  },
  "ResultPath": "$.orderProcessingResult",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "orderDetails": {
    "id": "order-456",
    "items": ["prod-1", "prod-2"],
    "shipping": {
      "street": "123 Main St",
      "city": "Anytown",
      "zip": "12345"
    }
  },
  "customerEmail": "john@example.com"
}
```

**If Lambda Returns:**

```json
{
  "confirmationNumber": "CONF-789",
  "estimatedDelivery": "2023-12-01"
}
```

**Resulting State Output:**

```json
{
  "orderId": "order-456",
  "items": [
    { "productId": "prod-1", "quantity": 2 },
    { "productId": "prod-2", "quantity": 1 }
  ],
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "zip": "12345"
  },
  "customerInfo": {
    "name": "John Doe",
    "email": "john@example.com"
  },
  "orderProcessingResult": {
    "confirmationNumber": "CONF-789",
    "estimatedDelivery": "2023-12-01"
  }
}
```

### 2. Advanced Parameters Usage

#### Example: Dynamic Array Processing and Intrinsic Functions

**Initial State Input:**

```json
{
  "item": "product-123",
  "items": ["product-123", "product-456"],
  "stockStatus": "true",
  "date": "2023-11-15",
  "time": "14:30:00",
  "metadata": {
    "sessionId": "sess-001"
  }
}
```

**State Definition with `Parameters`:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessData",
  "Parameters": {
    "itemsArray.$": "States.Array($.item)",
    "itemsCount.$": "States.ArrayLength($.items)",
    "hasStock.$": "States.StringToBoolean($.stockStatus)",
    "processTime": {
      "startTime.$": "$$.State.EnteredTime",
      "formatted.$": "States.Format('{}T{}Z', $.date, $.time)"
    },
    "metadata": {
      "executionStartTime.$": "$$.Execution.StartTime",
      "retryCount.$": "$$.State.RetryCount",
      "sessionId.$": "$.metadata.sessionId"
    }
  },
  "ResultPath": "$.processingResult",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "itemsArray": ["product-123"],
  "itemsCount": 2,
  "hasStock": true,
  "processTime": {
    "startTime": "2023-11-15T14:30:00Z",
    "formatted": "2023-11-15T14:30:00Z"
  },
  "metadata": {
    "executionStartTime": "2023-11-15T14:29:50Z",
    "retryCount": 0,
    "sessionId": "sess-001"
  }
}
```

**If Lambda Returns:**

```json
{
  "status": "Processed",
  "processedAt": "2023-11-15T14:30:10Z"
}
```

**Resulting State Output:**

```json
{
  "item": "product-123",
  "items": ["product-123", "product-456"],
  "stockStatus": "true",
  "date": "2023-11-15",
  "time": "14:30:00",
  "metadata": {
    "sessionId": "sess-001"
  },
  "processingResult": {
    "status": "Processed",
    "processedAt": "2023-11-15T14:30:10Z"
  }
}
```

### 3. Replacing or Overwriting Data in State Output

#### Example 1: Overwriting State Data Using ResultPath

**Initial State Input:**

```json
{
  "orderId": "order-456",
  "status": "NEW",
  "customerInfo": {
    "name": "Jane Doe",
    "email": "jane@example.com"
  }
}
```

**State Definition with ResultPath Overwrite:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessOrderStatus",
  "Parameters": {
    "orderDetails": {
      "id.$": "$.orderId",
      "status": "PROCESSED"
    }
  },
  "ResultPath": "$",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "orderDetails": {
    "id": "order-456",
    "status": "PROCESSED"
  }
}
```

**If Lambda Returns:**

```json
{
  "status": "SUCCESS",
  "processedAt": "2023-12-01T10:00:00Z"
}
```

**Resulting State Output:**

```json
{
  "status": "SUCCESS",
  "processedAt": "2023-12-01T10:00:00Z"
}
```

Using `ResultPath` with the `"$"` path overwrites the entire state input, replacing it with the result from the Lambda function.

---

#### Example 2: Partially Replacing Data with ResultPath

**Initial State Input:**

```json
{
  "orderId": "order-123",
  "items": [
    { "productId": "prod-1", "quantity": 1 },
    { "productId": "prod-2", "quantity": 3 }
  ],
  "customer": {
    "name": "Alice",
    "email": "alice@example.com"
  },
  "metadata": {
    "requestId": "req-789"
  }
}
```

**State Definition with Partial Overwrite Using ResultPath:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:UpdateOrder",
  "Parameters": {
    "orderId.$": "$.orderId",
    "items.$": "$.items[*].productId"
  },
  "ResultPath": "$.updatedOrder",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "orderId": "order-123",
  "items": ["prod-1", "prod-2"]
}
```

**If Lambda Returns:**

```json
{
  "status": "UPDATED",
  "timestamp": "2023-12-01T12:30:00Z"
}
```

**Resulting State Output:**

```json
{
  "orderId": "order-123",
  "items": [
    { "productId": "prod-1", "quantity": 1 },
    { "productId": "prod-2", "quantity": 3 }
  ],
  "customer": {
    "name": "Alice",
    "email": "alice@example.com"
  },
  "metadata": {
    "requestId": "req-789"
  },
  "updatedOrder": {
    "status": "UPDATED",
    "timestamp": "2023-12-01T12:30:00Z"
  }
}
```

In this example, the `ResultPath` directs the result to the `updatedOrder` field in the existing state, allowing a partial overwrite.

---

## Concurrency Patterns

### 1. Parallel State Processing

#### Example: Basic Parallel Processing

**Initial State Input:**

```json
{
  "orderId": "order-789",
  "customerId": "cust-123",
  "items": ["item-1", "item-2"]
}
```

**State Definition with `Parallel`:**

```json
{
  "Type": "Parallel",
  "Branches": [
    {
      "StartAt": "ProcessOrders",
      "States": {
        "ProcessOrders": {
          "Type": "Task",
          "Resource": "arn:aws:lambda:region:account-id:function:ProcessOrders",
          "End": true
        }
      }
    },
    {
      "StartAt": "UpdateInventory",
      "States": {
        "UpdateInventory": {
          "Type": "Task",
          "Resource": "arn:aws:lambda:region:account-id:function:UpdateInventory",
          "End": true
        }
      }
    }
  ],
  "ResultPath": "$.parallelResults",
  "End": true
}
```

**Tasks Receive:**

- **ProcessOrders Lambda Receives:**

  ```json
  {
    "orderId": "order-789",
    "customerId": "cust-123",
    "items": ["item-1", "item-2"]
  }
  ```

- **UpdateInventory Lambda Receives:**

  ```json
  {
    "orderId": "order-789",
    "customerId": "cust-123",
    "items": ["item-1", "item-2"]
  }
  ```

**If Lambdas Return:**

- **ProcessOrders Returns:**

  ```json
  {
    "orderStatus": "Processed"
  }
  ```

- **UpdateInventory Returns:**

  ```json
  {
    "inventoryStatus": "Updated"
  }
  ```

**Resulting State Output:**

```json
{
  "orderId": "order-789",
  "customerId": "cust-123",
  "items": ["item-1", "item-2"],
  "parallelResults": [
    {
      "orderStatus": "Processed"
    },
    {
      "inventoryStatus": "Updated"
    }
  ]
}
```

#### Example: Parallel with Shared Context

**Initial State Input:**

```json
{
  "userId": "user-456",
  "sessionData": {
    "sessionId": "sess-789",
    "timestamp": "2023-11-15T15:00:00Z"
  },
  "preferences": {
    "theme": "dark",
    "notifications": true
  }
}
```

**State Definition with Shared `Parameters`:**

```json
{
  "Type": "Parallel",
  "Parameters": {
    "context.$": "$.sessionData",
    "userId.$": "$.userId"
  },
  "Branches": [
    {
      "StartAt": "Branch1",
      "States": {
        "Branch1": {
          "Type": "Task",
          "Resource": "arn:aws:lambda:region:account-id:function:BranchFunction1",
          "End": true
        }
      }
    },
    {
      "StartAt": "Branch2",
      "States": {
        "Branch2": {
          "Type": "Task",
          "Resource": "arn:aws:lambda:region:account-id:function:BranchFunction2",
          "End": true
        }
      }
    }
  ],
  "ResultSelector": {
    "branch1Result.$": "$[0]",
    "branch2Result.$": "$[1]"
  },
  "ResultPath": "$.branchResults",
  "End": true
}
```

**Tasks Receive:**

- **Both Lambdas Receive:**

  ```json
  {
    "context": {
      "sessionId": "sess-789",
      "timestamp": "2023-11-15T15:00:00Z"
    },
    "userId": "user-456"
  }
  ```

**If Lambdas Return:**

- **BranchFunction1 Returns:**

  ```json
  {
    "activityLog": "Activity recorded"
  }
  ```

- **BranchFunction2 Returns:**

  ```json
  {
    "preferenceUpdate": "Preferences saved"
  }
  ```

**Resulting State Output:**

```json
{
  "userId": "user-456",
  "sessionData": {
    "sessionId": "sess-789",
    "timestamp": "2023-11-15T15:00:00Z"
  },
  "preferences": {
    "theme": "dark",
    "notifications": true
  },
  "branchResults": {
    "branch1Result": {
      "activityLog": "Activity recorded"
    },
    "branch2Result": {
      "preferenceUpdate": "Preferences saved"
    }
  }
}
```

### 2. Map State with Concurrency Control

#### Example: Basic Map with Concurrency

**Initial State Input:**

```json
{
  "orders": [
    { "orderId": "order-1", "amount": 100 },
    { "orderId": "order-2", "amount": 150 },
    { "orderId": "order-3", "amount": 200 },
    { "orderId": "order-4", "amount": 250 },
    { "orderId": "order-5", "amount": 300 }
  ]
}
```

**State Definition with `Map`:**

```json
{
  "Type": "Map",
  "ItemsPath": "$.orders",
  "MaxConcurrency": 2,
  "Iterator": {
    "StartAt": "ProcessSingleOrder",
    "States": {
      "ProcessSingleOrder": {
        "Type": "Task",
        "Resource": "arn:aws:lambda:region:account-id:function:ProcessSingleOrder",
        "End": true
      }
    }
  },
  "ResultPath": "$.processedOrders",
  "End": true
}
```

**Lambda Receives (per iteration):**

- **First Iteration:**

  ```json
  { "orderId": "order-1", "amount": 100 }
  ```

- **Second Iteration:**

  ```json
  { "orderId": "order-2", "amount": 150 }
  ```

_(Up to MaxConcurrency of 2 at a time.)_

**If Lambdas Return:**

```json
{
  "orderId": "order-X",
  "status": "Processed"
}
```

**Resulting State Output:**

```json
{
  "orders": [
    { "orderId": "order-1", "amount": 100 },
    { "orderId": "order-2", "amount": 150 },
    { "orderId": "order-3", "amount": 200 },
    { "orderId": "order-4", "amount": 250 },
    { "orderId": "order-5", "amount": 300 }
  ],
  "processedOrders": [
    { "orderId": "order-1", "status": "Processed" },
    { "orderId": "order-2", "status": "Processed" },
    { "orderId": "order-3", "status": "Processed" },
    { "orderId": "order-4", "status": "Processed" },
    { "orderId": "order-5", "status": "Processed" }
  ]
}
```

#### Example: Map with `ItemSelector`

**Initial State Input:**

```json
{
  "array": [1, 4, 9, 16, 25]
}
```

**State Definition with `Map` and `ItemSelector`:**

```json
{
  "Type": "Map",
  "ItemsPath": "$.array",
  "ItemSelector": {
    "index.$": "$$.Map.Item.Index",
    "value.$": "$$.Map.Item.Value",
    "sqrtValue.$": "States.MathSqrt($$.Map.Item.Value)"
  },
  "Iterator": {
    "StartAt": "ProcessItem",
    "States": {
      "ProcessItem": {
        "Type": "Pass",
        "ResultPath": "$",
        "End": true
      }
    }
  },
  "ResultPath": "$.results",
  "End": true
}
```

**Data Processed in Each Iteration:**

- **First Iteration:**

  ```json
  {
    "index": 0,
    "value": 1,
    "sqrtValue": 1.0
  }
  ```

- **Second Iteration:**

  ```json
  {
    "index": 1,
    "value": 4,
    "sqrtValue": 2.0
  }
  ```

**Resulting State Output:**

```json
{
  "array": [1, 4, 9, 16, 25],
  "results": [
    { "index": 0, "value": 1, "sqrtValue": 1.0 },
    { "index": 1, "value": 4, "sqrtValue": 2.0 },
    { "index": 2, "value": 9, "sqrtValue": 3.0 },
    { "index": 3, "value": 16, "sqrtValue": 4.0 },
    { "index": 4, "value": 25, "sqrtValue": 5.0 }
  ]
}
```

---

## Advanced State Patterns

### 1. Dynamic Branching with Choice State

**Initial State Input:**

```json
{
  "order": {
    "total": 1200,
    "priority": "HIGH",
    "items": [
      { "productId": "prod-1", "category": "STANDARD" },
      { "productId": "prod-2", "category": "FRAGILE" }
    ],
    "shipping": {
      "express": true
    }
  }
}
```

**State Definition with `Choice`:**

```json
{
  "Type": "Choice",
  "Choices": [
    {
      "And": [
        {
          "Variable": "$.order.total",
          "NumericGreaterThan": 1000
        },
        {
          "Variable": "$.order.priority",
          "StringEquals": "HIGH"
        }
      ],
      "Next": "PremiumProcessing"
    },
    {
      "Or": [
        {
          "Variable": "$.order.items[?(@.category == 'FRAGILE')]",
          "IsPresent": true
        },
        {
          "Variable": "$.order.shipping.express",
          "BooleanEquals": true
        }
      ],
      "Next": "SpecialHandling"
    }
  ],
  "Default": "StandardProcessing"
}
```

**Execution Path:**

- Since `$.order.total` > 1000 and `$.order.priority` == "HIGH", the execution moves to `PremiumProcessing`.

---

### 2. Wait States with Dynamic Duration

**Initial State Input:**

```json
{
  "task": "WaitForApproval",
  "delay": 30 // Wait for 30 seconds
}
```

**State Definition with `Wait`:**

```json
{
  "Type": "Wait",
  "SecondsPath": "$.delay",
  "Next": "CheckApprovalStatus"
}
```

**Execution Flow:**

- The state machine waits for 30 seconds (as specified in `$.delay`) before proceeding to `CheckApprovalStatus`.

---

## Complex Workflows

### 1. Orchestration Pattern

**Initial State Input:**

```json
{
  "orderId": "order-999",
  "customerId": "cust-789",
  "items": ["item-5", "item-6"]
}
```

**State Machine Definition:**

```json
{
  "StartAt": "InitializeOrder",
  "States": {
    "InitializeOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:InitializeOrder",
      "Next": "ParallelProcessing"
    },
    "ParallelProcessing": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "ValidateInventory",
          "States": {
            "ValidateInventory": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:region:account-id:function:ValidateInventory",
              "End": true
            }
          }
        },
        {
          "StartAt": "ProcessPayment",
          "States": {
            "ProcessPayment": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:region:account-id:function:ProcessPayment",
              "End": true
            }
          }
        }
      ],
      "ResultPath": "$.processingResults",
      "Next": "EvaluateResults"
    },
    "EvaluateResults": {
      "Type": "Choice",
      "Choices": [
        {
          "And": [
            {
              "Variable": "$.processingResults[0].inventoryAvailable",
              "BooleanEquals": true
            },
            {
              "Variable": "$.processingResults[1].paymentSuccessful",
              "BooleanEquals": true
            }
          ],
          "Next": "FulfillOrder"
        }
      ],
      "Default": "HandleFailure"
    },
    "FulfillOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:FulfillOrder",
      "End": true
    },
    "HandleFailure": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:HandleFailure",
      "End": true
    }
  }
}
```

**Execution Flow:**

1. **InitializeOrder** Lambda receives the initial input and performs setup tasks.
2. **ParallelProcessing** runs **ValidateInventory** and **ProcessPayment** in parallel.
3. **EvaluateResults** checks if both inventory is available and payment was successful.
4. If both are true, it proceeds to **FulfillOrder**; otherwise, it moves to **HandleFailure**.

**Resulting State Output:**

Depends on the execution path taken. If successful, the order is fulfilled; if not, failure handling is executed.

### 2. Saga Pattern with Compensation

**Initial State Input:**

```json
{
  "reservationDetails": {
    "flightId": "FL-123",
    "hotelId": "HT-456",
    "carRentalId": "CR-789"
  }
}
```

**State Machine Definition:**

```json
{
  "StartAt": "ReserveFlight",
  "States": {
    "ReserveFlight": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:ReserveFlight",
      "Next": "ReserveHotel",
      "Catch": [
        {
          "ErrorEquals": ["States.ALL"],
          "ResultPath": "$.errorInfo",
          "Next": "CleanupState"
        }
      ]
    },
    "ReserveHotel": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:ReserveHotel",
      "Next": "ReserveCar",
      "Catch": [
        {
          "ErrorEquals": ["States.ALL"],
          "ResultPath": "$.errorInfo",
          "Next": "CompensateFlight"
        }
      ]
    },
    "ReserveCar": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:ReserveCar",
      "Next": "CompleteBooking",
      "Catch": [
        {
          "ErrorEquals": ["States.ALL"],
          "ResultPath": "$.errorInfo",
          "Next": "CompensateHotel"
        }
      ]
    },
    "CompensateHotel": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:CancelHotel",
      "Next": "CompensateFlight"
    },
    "CompensateFlight": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:CancelFlight",
      "Next": "CleanupState"
    },
    "CompleteBooking": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:region:account-id:function:CompleteBooking",
      "End": true
    },
    "CleanupState": {
      "Type": "Pass",
      "End": true
    }
  }
}
```

**Execution Flow:**

- If any reservation step fails, the state machine invokes compensation steps to undo previous actions.

**Resulting State Output:**

Depends on the success or failure of each reservation step and the execution of compensation actions.

---

## Error Handling & Resilience

### 1. Comprehensive Error Handling

#### Example: Retry Configuration with Catch

**State Definition with `Retry` and `Catch`:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessData",
  "Retry": [
    {
      "ErrorEquals": ["States.Timeout"],
      "IntervalSeconds": 2,
      "MaxAttempts": 3,
      "BackoffRate": 2.0
    },
    {
      "ErrorEquals": ["CustomError"],
      "IntervalSeconds": 1,
      "MaxAttempts": 2
    }
  ],
  "Catch": [
    {
      "ErrorEquals": ["States.ALL"],
      "ResultPath": "$.errorInfo",
      "Next": "ErrorHandler"
    }
  ],
  "End": true
}
```

**Execution Flow:**

- The task retries on `States.Timeout` and `CustomError` according to specified settings.
- If all retries fail or another error occurs, it catches the error and transitions to `ErrorHandler`.

### 2. Circuit Breaker Implementation

**State Definition with Circuit Breaker Logic:**

```json
{
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$.serviceHealth.errorCount",
      "NumericGreaterThan": 5,
      "Next": "CircuitOpen"
    }
  ],
  "Default": "ProcessNormally"
}
```

**Execution Flow:**

- If `$.serviceHealth.errorCount` exceeds 5, the workflow transitions to `CircuitOpen`, preventing further processing.
- Otherwise, it proceeds to `ProcessNormally`.

---

## Integration Patterns

### 1. Service Integration Pattern

#### Example: SQS `SendMessage` Integration

**Initial State Input:**

```json
{
  "orderId": "order-1010",
  "message": "Order placed successfully."
}
```

**State Definition with SQS Integration:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:states:::sqs:sendMessage",
  "Parameters": {
    "QueueUrl": "https://sqs.region.amazonaws.com/account-id/queue-name",
    "MessageBody.$": "$.message",
    "MessageAttributes": {
      "OrderId": {
        "DataType": "String",
        "StringValue.$": "$.orderId"
      }
    }
  },
  "ResultSelector": {
    "MessageId.$": "$.MessageId",
    "Timestamp.$": "$$.State.EnteredTime"
  },
  "ResultPath": "$.sqsResult",
  "End": true
}
```

**Resulting State Output:**

```json
{
  "orderId": "order-1010",
  "message": "Order placed successfully.",
  "sqsResult": {
    "MessageId": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
    "Timestamp": "2023-11-15T16:00:00Z"
  }
}
```

### 2. API Gateway Integration

**Initial State Input:**

```json
{
  "orderData": {
    "orderId": "order-2020",
    "items": ["item-A", "item-B"]
  }
}
```

**State Definition with API Gateway Integration:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:states:::apigateway:invoke",
  "Parameters": {
    "ApiEndpoint": "https://myapi.execute-api.region.amazonaws.com",
    "Method": "POST",
    "Headers": {
      "Content-Type": "application/json"
    },
    "Path": "/orders",
    "RequestBody.$": "$.orderData"
  },
  "ResultSelector": {
    "StatusCode.$": "$.StatusCode",
    "ResponseBody.$": "$.ResponseBody"
  },
  "ResultPath": "$.apiResponse",
  "End": true
}
```

**Resulting State Output:**

```json
{
  "orderData": {
    "orderId": "order-2020",
    "items": ["item-A", "item-B"]
  },
  "apiResponse": {
    "StatusCode": 200,
    "ResponseBody": {
      "message": "Order received",
      "orderId": "order-2020"
    }
  }
}
```

---

## Best Practices & Tips

### 1. Use Intrinsic Functions Effectively

**Example: Utilizing Intrinsic Functions in `Parameters`**

**Initial State Input:**

```json
{
  "date": "2023-11-15",
  "time": "17:00:00",
  "item": "item-XYZ",
  "items": ["item-XYZ", "item-ABC"],
  "status": "true"
}
```

**State Definition with Intrinsic Functions:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessIntrinsic",
  "Parameters": {
    "timestamp.$": "States.Format('{}T{}Z', $.date, $.time)",
    "itemsArray.$": "States.Array($.item)",
    "itemsCount.$": "States.ArrayLength($.items)",
    "isValid.$": "States.StringToBoolean($.status)"
  },
  "ResultPath": "$.intrinsicResult",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "timestamp": "2023-11-15T17:00:00Z",
  "itemsArray": ["item-XYZ"],
  "itemsCount": 2,
  "isValid": true
}
```

### 2. Implement Idempotency for Reliability

**State Definition with Idempotency Key:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:ProcessWithIdempotency",
  "Parameters": {
    "idempotencyKey.$": "States.Format('{}-{}', $.orderId, $$.State.Name)",
    "payload.$": "$"
  },
  "ResultPath": "$.idempotentResult",
  "End": true
}
```

**Ensures that each execution can be uniquely identified and avoids duplicate processing.**

### 3. Monitor Execution Progress with CloudWatch

**State Definition with CloudWatch Integration:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:states:::cloudwatch:putMetricData",
  "Parameters": {
    "Namespace": "StepFunctions/Processing",
    "MetricData": [
      {
        "MetricName": "ProcessingTime",
        "Value.$": "$.duration",
        "Unit": "Milliseconds",
        "Dimensions": [
          {
            "Name": "ExecutionId",
            "Value.$": "$$.Execution.Id"
          }
        ]
      }
    ]
  },
  "ResultPath": "$.cloudWatchResult",
  "End": true
}
```

**Resulting State Output:**

Includes metrics data sent to CloudWatch for monitoring purposes.

#### 4. Replace Data Effectively with OutputPath

Using `OutputPath` to selectively replace or retain data in the output state can help keep workflow data clean and relevant.

##### Example 3: Replacing Data with OutputPath

**Initial State Input:**

```json
{
  "userId": "user-789",
  "preferences": {
    "notifications": true,
    "theme": "light"
  },
  "settings": {
    "email": "user@example.com",
    "subscription": "basic"
  }
}
```

**State Definition with OutputPath Replacement:**

```json
{
  "Type": "Task",
  "Resource": "arn:aws:lambda:region:account-id:function:UpdatePreferences",
  "Parameters": {
    "userId.$": "$.userId",
    "preferences.$": "$.preferences"
  },
  "OutputPath": "$.preferences",
  "End": true
}
```

**Lambda Receives:**

```json
{
  "userId": "user-789",
  "preferences": {
    "notifications": true,
    "theme": "light"
  }
}
```

**If Lambda Returns:**

```json
{
  "notifications": false,
  "theme": "dark"
}
```

**Resulting State Output:**

```json
{
  "notifications": false,
  "theme": "dark"
}
```

By setting the `OutputPath` to `$.preferences`, the final state output retains only the updated preferences data, replacing the initial input with the result of this task.