| { |
| "repository_url": "https://github.com/ronelsolomon/AWS-backend.git", |
| "owner": "ronelsolomon", |
| "name": "AWS-backend.git", |
| "extracted_at": "2026-03-02T22:48:23.535261", |
| "files": { |
| "template.yaml": { |
| "content": "AWSTemplateFormatVersion: '2010-09-09'\nDescription: 'Basic AWS Backend with API Gateway, Lambda, and DynamoDB'\n\nParameters:\n EnvironmentName:\n Description: Environment name (e.g., dev, staging, prod)\n Type: String\n Default: dev\n AllowedValues: [dev, staging, prod]\n\nResources:\n # DynamoDB Table\n ItemsTable:\n Type: AWS::DynamoDB::Table\n Properties:\n TableName: !Sub \"${EnvironmentName}-ItemsTable\"\n BillingMode: PAY_PER_REQUEST\n AttributeDefinitions:\n - AttributeName: id\n AttributeType: S\n KeySchema:\n - AttributeName: id\n KeyType: HASH\n\n # IAM Role for Lambda\n LambdaExecutionRole:\n Type: AWS::IAM::Role\n Properties:\n AssumeRolePolicyDocument:\n Version: '2012-10-17'\n Statement:\n - Effect: Allow\n Principal:\n Service: [lambda.amazonaws.com]\n Action: ['sts:AssumeRole']\n ManagedPolicyArns:\n - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'\n Policies:\n - PolicyName: DynamoDBAccess\n PolicyDocument:\n Version: '2012-10-17'\n Statement:\n - Effect: Allow\n Action:\n - 'dynamodb:GetItem'\n - 'dynamodb:PutItem'\n - 'dynamodb:UpdateItem'\n - 'dynamodb:DeleteItem'\n - 'dynamodb:Scan'\n Resource: !GetAtt ItemsTable.Arn\n\n # Lambda Function\n ApiFunction:\n Type: AWS::Lambda::Function\n Properties:\n FunctionName: !Sub \"${EnvironmentName}-api-handler\"\n Runtime: nodejs18.x\n Handler: app.handler\n Role: !GetAtt LambdaExecutionRole.Arn\n Code:\n ZipFile: |\n // This will be replaced with the actual code during deployment\n exports.handler = require('./app').handler;\n Environment:\n Variables:\n TABLE_NAME: !Ref ItemsTable\n NODE_ENV: !Ref EnvironmentName\n\n # API Gateway\n ApiGateway:\n Type: AWS::ApiGateway::RestApi\n Properties:\n Name: !Sub \"${EnvironmentName}-api\"\n Description: 'API Gateway for the basic backend'\n EndpointConfiguration:\n Types:\n - REGIONAL\n\n # API Gateway Resource\n ApiResource:\n Type: AWS::ApiGateway::Resource\n Properties:\n RestApiId: !Ref ApiGateway\n ParentId: !GetAtt ApiGateway.RootResourceId\n PathPart: '{proxy+}'\n\n # API Gateway Method\n ApiMethod:\n Type: AWS::ApiGateway::Method\n Properties:\n RestApiId: !Ref ApiGateway\n ResourceId: !Ref ApiResource\n HttpMethod: ANY\n AuthorizationType: NONE\n Integration:\n Type: AWS_PROXY\n IntegrationHttpMethod: POST\n Uri: !Sub \"arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations\"\n\n # Lambda Permission for API Gateway\n ApiGatewayPermission:\n Type: AWS::Lambda::Permission\n Properties:\n Action: 'lambda:InvokeFunction'\n FunctionName: !GetAtt ApiFunction.Arn\n Principal: apigateway.amazonaws.com\n SourceArn: !Sub \"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*\"\n\n # API Gateway Deployment\n ApiDeployment:\n DependsOn: ApiMethod\n Type: AWS::ApiGateway::Deployment\n Properties:\n RestApiId: !Ref ApiGateway\n StageName: !Ref EnvironmentName\n\nOutputs:\n ApiEndpoint:\n Description: \"API Gateway endpoint URL\"\n Value: !Sub \"https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${EnvironmentName}\"\n ItemsTableName:\n Description: \"DynamoDB Table Name\"\n Value: !Ref ItemsTable\n", |
| "size": 3598, |
| "language": "yaml" |
| }, |
| "deploy.js": { |
| "content": "const AWS = require('aws-sdk');\nconst fs = require('fs');\nconst path = require('path');\nconst { promisify } = require('util');\nconst readFile = promisify(fs.readFile);\n\n// Configure AWS\nAWS.config.update({ region: 'us-east-1' }); // Change to your preferred region\nconst cloudFormation = new AWS.CloudFormation();\nconst iam = new AWS.IAM();\nconst lambda = new AWS.Lambda();\nconst apiGateway = new AWS.APIGateway();\nconst dynamoDB = new AWS.DynamoDB();\n\n// Stack configuration\nconst STACK_NAME = 'BasicBackendStack';\nconst STACK_TEMPLATE = 'template.yaml';\nconst LAMBDA_ZIP = 'function.zip';\n\nasync function deploy() {\n try {\n console.log('Deploying AWS Backend...');\n \n // 1. Create or update CloudFormation stack\n const templateBody = await readFile(path.join(__dirname, STACK_TEMPLATE), 'utf8');\n \n const stackExists = await checkStackExists(STACK_NAME);\n \n if (stackExists) {\n console.log('Updating existing stack...');\n await updateStack(templateBody);\n } else {\n console.log('Creating new stack...');\n await createStack(templateBody);\n }\n \n console.log('Deployment completed successfully!');\n \n } catch (error) {\n console.error('Deployment failed:', error);\n process.exit(1);\n }\n}\n\nasync function checkStackExists(stackName) {\n try {\n await cloudFormation.describeStacks({ StackName: stackName }).promise();\n return true;\n } catch (error) {\n if (error.code === 'ValidationError' && error.message.includes('does not exist')) {\n return false;\n }\n throw error;\n }\n}\n\nasync function createStack(templateBody) {\n const params = {\n StackName: STACK_NAME,\n TemplateBody: templateBody,\n Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],\n OnFailure: 'ROLLBACK',\n TimeoutInMinutes: 30\n };\n \n await cloudFormation.createStack(params).promise();\n await cloudFormation.waitFor('stackCreateComplete', { StackName: STACK_NAME }).promise();\n}\n\nasync function updateStack(templateBody) {\n const params = {\n StackName: STACK_NAME,\n TemplateBody: templateBody,\n Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']\n };\n \n try {\n await cloudFormation.updateStack(params).promise();\n await cloudFormation.waitFor('stackUpdateComplete', { StackName: STACK_NAME }).promise();\n } catch (error) {\n if (error.message.includes('No updates are to be performed')) {\n console.log('No updates to perform');\n return;\n }\n throw error;\n }\n}\n\n// Start deployment\ndeploy();\n", |
| "size": 2506, |
| "language": "javascript" |
| }, |
| "README.md": { |
| "content": "# AWS Serverless Backend\n\nA production-ready AWS serverless backend with Cognito authentication, API Gateway, Lambda, and DynamoDB, built with AWS CDK and Python.\n\n## β¨ Features\n\n- π **Authentication**: JWT-based authentication with Amazon Cognito\n- π **Serverless**: Fully managed services with automatic scaling\n- π **Infrastructure as Code**: AWS CDK for reliable deployments\n- π **RESTful API**: Complete CRUD operations with validation\n- π **Monitoring**: Built-in CloudWatch logging and metrics\n- π **Security**: IAM roles with least privilege, input validation\n- π **CORS Support**: Pre-configured for web applications\n- π§ͺ **Testing**: Unit and integration test setup\n- π§ **Developer Experience**: Local development and debugging support\n\n## π Quick Start\n\n### Prerequisites\n\n- Python 3.9+\n- Node.js 16.x+ (for CDK)\n- AWS CLI configured with admin permissions\n- AWS CDK v2 installed (`npm install -g aws-cdk`)\n\n### Installation\n\n1. **Clone the repository**\n ```bash\n git clone https://github.com/yourusername/aws-serverless-backend.git\n cd aws-serverless-backend\n ```\n\n2. **Set up Python virtual environment**\n ```bash\n python -m venv .venv\n source .venv/bin/activate # On Windows: .venv\\Scripts\\activate\n ```\n\n3. **Install dependencies**\n ```bash\n pip install -r requirements-dev.txt\n npm install\n ```\n\n4. **Configure environment**\n ```bash\n cp .env.example .env\n # Edit .env with your AWS account and region\n ```\n\n## π Project Structure\n\n```\n.\nβββ app.py # CDK application entry point\nβββ backend/\nβ βββ backend_stack.py # Main CDK stack definition\nβββ lambda/\nβ βββ lambda_function.py # Lambda function handler\nβ βββ requirements.txt # Lambda dependencies\nβββ tests/ # Unit and integration tests\nβββ .env.example # Example environment variables\nβββ package.json # Node.js dependencies\nβββ requirements-dev.txt # Development dependencies\n```\n\n## π Deployment\n\n### First-time Setup\n\n```bash\n# Bootstrap CDK in your AWS account (first time only)\ncdk bootstrap aws://ACCOUNT-NUMBER/REGION\n```\n\n### Deploy to AWS\n\n```bash\n# Deploy the stack\ncdk deploy\n\n# Deploy to a specific stage (dev/staging/prod)\ncdk deploy --context stage=prod\n```\n\n### Deploy Outputs\n\nAfter successful deployment, you'll receive:\n- API Gateway URL\n- Cognito User Pool ID\n- Cognito Client ID\n- DynamoDB Table Name\n\n## π§ Local Development\n\n### Run Lambda Locally\n\n```bash\n# Install local dependencies\npip install -r lambda/requirements.txt\n\n# Set environment variables\nexport TABLE_NAME=local-table\nexport AWS_ACCESS_KEY_ID=test\nexport AWS_SECRET_ACCESS_KEY=test\nexport AWS_DEFAULT_REGION=us-east-1\n\n# Start local API Gateway and Lambda\nsam local start-api\n```\n\n### Test API Endpoints\n\n```bash\n# Register a new user\ncurl -X POST $API_URL/auth/register \\\n -H \"Content-Type: application/json\" \\\n -d '{\"email\":\"user@example.com\",\"password\":\"SecurePass123!\"}'\n\n# Get authentication token\nTOKEN=$(curl -X POST $API_URL/auth/login \\\n -H \"Content-Type: application/json\" \\\n -d '{\"email\":\"user@example.com\",\"password\":\"SecurePass123!\"}' | jq -r '.token')\n\n# Access protected endpoints\ncurl -H \"Authorization: Bearer $TOKEN\" $API_URL/items\n```\n\n## π§ͺ Testing\n\n### Run Unit Tests\n\n```bash\npytest tests/unit -v\n```\n\n### Run Integration Tests\n\n```bash\n# Deploy to a test environment\ncdk deploy --context stage=test\n\n# Run integration tests\npytest tests/integration -v\n```\n\n## π‘ Security\n\n- **Authentication**: JWT tokens with Cognito\n- **Authorization**: IAM roles with least privilege\n- **Data Protection**: Encryption at rest and in transit\n- **Input Validation**: Pydantic models for all API inputs\n- **Secrets Management**: AWS Secrets Manager or Parameter Store for sensitive data\n\n## π Monitoring and Logging\n\n- **CloudWatch Logs**: All Lambda function logs\n- **CloudWatch Metrics**: API Gateway and Lambda metrics\n- **X-Ray Tracing**: Distributed tracing enabled\n- **Error Tracking**: Structured error logging\n\n## π CI/CD (GitHub Actions)\n\nPre-configured GitHub Actions workflows for:\n- Linting and type checking on PRs\n- Unit tests on push\n- Automated deployments to staging/production\n\n## π§Ή Cleanup\n\nTo remove all AWS resources:\n\n```bash\ncdk destroy\n# Or for a specific stage\ncdk destroy --context stage=prod\n```\n\n## π License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## π€ Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## π Learning Path\n\n### Core AWS Services\n\n1. **AWS CDK (Cloud Development Kit)**\n - **What it is**: Framework for defining cloud infrastructure using familiar programming languages\n - **Key Concepts**:\n - Constructs: Basic building blocks (like `Stack`, `Table`, `Function`)\n - Stacks: Units of deployment\n - Environments: AWS account/region combinations\n - **Example**: `BackendStack` in `backend_stack.py` defines all resources\n\n2. **AWS Lambda**\n - **What it is**: Serverless compute service\n - **Key Concepts**:\n - Handler function: Entry point for execution\n - Runtime: Python 3.9 in our case\n - Environment variables: Secure configuration\n - **Example**: `lambda_function.py` contains the request handler\n\n3. **Amazon API Gateway**\n - **What it is**: Managed service for creating and managing APIs\n - **Key Concepts**:\n - REST API: Resource-based HTTP endpoints\n - Integration: Connection to Lambda functions\n - Authorizers: JWT validation with Cognito\n - **Example**: Defined in `backend_stack.py` with CORS and authentication\n\n4. **Amazon DynamoDB**\n - **What it is**: NoSQL database service\n - **Key Concepts**:\n - Tables: Collections of items\n - Items: Individual records\n - GSIs: Global Secondary Indexes for flexible querying\n - **Example**: `ItemsTable` in `backend_stack.py`\n\n5. **Amazon Cognito**\n - **What it is**: User authentication and authorization\n - **Key Concepts**:\n - User Pools: User directories\n - App Clients: Application configurations\n - JWT Tokens: Secure authentication\n - **Example**: `UserPool` and `UserPoolClient` in `backend_stack.py`\n\n### Python Concepts\n\n1. **Pydantic Models**\n ```python\n class ItemCreate(BaseModel):\n name: str\n description: str\n ```\n - Used for request/response validation\n - Automatic type conversion\n - Schema documentation\n\n2. **AWS Lambda Powertools**\n - `@logger.inject_lambda_context`: Structured logging\n - `@tracer.capture_lambda_handler`: Distributed tracing\n - `@metrics.log_metrics`: Custom metrics\n\n3. **Error Handling**\n ```python\n try:\n # Code that might fail\n except ClientError as e:\n # Handle AWS service errors\n except Exception as e:\n # Handle unexpected errors\n ```\n\n### Security Concepts\n\n1. **IAM Roles & Policies**\n - Least privilege principle\n - Managed policies vs. inline policies\n - Service roles for AWS services\n\n2. **JWT Authentication**\n - Token-based authentication flow\n - Claims validation\n - Token expiration and refresh\n\n### Best Practices\n\n1. **Infrastructure as Code**\n - Version controlled infrastructure\n - Repeatable deployments\n - Environment parity\n\n2. **Serverless Architecture**\n - Event-driven design\n - Stateless functions\n - Managed services where possible\n\n## π Frontend Development Guide\n\nThis section covers how to build a frontend application that works with your serverless backend.\n\n### 1. Authentication Flow\n\n#### Setting Up Cognito\n\n```javascript\n// Install required packages\n// npm install amazon-cognito-identity-js @aws-amplify/auth\n\nimport { CognitoUserPool, CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js';\n\nconst poolData = {\n UserPoolId: 'YOUR_USER_POOL_ID',\n ClientId: 'YOUR_APP_CLIENT_ID'\n};\n\nconst userPool = new CognitoUserPool(poolData);\n\n// Sign Up\nconst signUp = (email, password) => {\n return new Promise((resolve, reject) => {\n userPool.signUp(email, password, [], null, (err, result) => {\n if (err) return reject(err);\n resolve(result.user);\n });\n });\n};\n\n// Sign In\nconst signIn = (email, password) => {\n const authDetails = new AuthenticationDetails({\n Username: email,\n Password: password\n });\n\n const userData = {\n Username: email,\n Pool: userPool\n };\n\n const cognitoUser = new CognitoUser(userData);\n\n return new Promise((resolve, reject) => {\n cognitoUser.authenticateUser(authDetails, {\n onSuccess: (result) => {\n const token = result.getIdToken().getJwtToken();\n localStorage.setItem('token', token);\n resolve({ user: cognitoUser, token });\n },\n onFailure: (err) => reject(err)\n });\n });\n};\n```\n\n### 2. Data Modeling\n\n#### Frontend Models\n\n```typescript\n// types/item.ts\nexport interface Item {\n id: string;\n name: string;\n description: string;\n createdAt: string;\n updatedAt: string;\n userId: string;\n}\n\n// API Response Types\nexport interface ApiResponse<T> {\n data?: T;\n error?: string;\n status: number;\n}\n```\n\n### 3. API Integration\n\n#### API Client Setup\n\n```typescript\n// api/client.ts\nconst API_URL = process.env.REACT_APP_API_URL;\n\nexport const apiClient = async <T>(\n endpoint: string,\n options: RequestInit = {}\n): Promise<ApiResponse<T>> => {\n const token = localStorage.getItem('token');\n \n const headers = {\n 'Content-Type': 'application/json',\n ...(token && { 'Authorization': `Bearer ${token}` }),\n ...options.headers,\n };\n\n try {\n const response = await fetch(`${API_URL}${endpoint}`, {\n ...options,\n headers,\n });\n\n const data = await response.json().catch(() => ({}));\n \n if (!response.ok) {\n return { error: data.message || 'Request failed', status: response.status };\n }\n\n return { data, status: response.status };\n } catch (error) {\n return { error: 'Network error', status: 500 };\n }\n};\n```\n\n#### Example API Calls\n\n```typescript\n// api/items.ts\nimport { apiClient } from './client';\nimport { Item } from '../types/item';\n\nexport const fetchItems = async (): Promise<ApiResponse<Item[]>> => {\n return apiClient<Item[]>('/items');\n};\n\nexport const createItem = async (item: Omit<Item, 'id' | 'createdAt' | 'updatedAt' | 'userId'>) => {\n return apiClient<Item>('/items', {\n method: 'POST',\n body: JSON.stringify(item),\n });\n};\n```\n\n### 4. State Management\n\n#### React Context Example\n\n```tsx\n// context/ItemsContext.tsx\nimport React, { createContext, useContext, useEffect, useState } from 'react';\nimport { fetchItems, createItem } from '../api/items';\nimport { Item } from '../types/item';\n\ninterface ItemsContextType {\n items: Item[];\n loading: boolean;\n error: string | null;\n addItem: (item: Omit<Item, 'id' | 'createdAt' | 'updatedAt' | 'userId'>) => Promise<void>;\n refreshItems: () => Promise<void>;\n}\n\nconst ItemsContext = createContext<ItemsContextType | undefined>(undefined);\n\nexport const ItemsProvider: React.FC = ({ children }) => {\n const [items, setItems] = useState<Item[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const loadItems = async () => {\n try {\n setLoading(true);\n const { data, error } = await fetchItems();\n \n if (error) throw new Error(error);\n if (data) setItems(data);\n } catch (err) {\n setError(err.message);\n } finally {\n setLoading(false);\n }\n };\n\n const addItem = async (item: Omit<Item, 'id' | 'createdAt' | 'updatedAt' | 'userId'>) => {\n const { data, error } = await createItem(item);\n if (error) throw new Error(error);\n if (data) setItems(prev => [...prev, data]);\n };\n\n useEffect(() => {\n loadItems();\n }, []);\n\n return (\n <ItemsContext.Provider\n value={{\n items,\n loading,\n error,\n addItem,\n refreshItems: loadItems,\n }}\n >\n {children}\n </ItemsContext.Provider>\n );\n};\n\nexport const useItems = () => {\n const context = useContext(ItemsContext);\n if (context === undefined) {\n throw new Error('useItems must be used within an ItemsProvider');\n }\n return context;\n};\n```\n\n### 5. Monitoring and Error Tracking\n\n#### Frontend Monitoring Setup\n\n```typescript\n// utils/monitoring.ts\nimport * as Sentry from '@sentry/react';\nimport { Integrations } from '@sentry/tracing';\n\nconst initMonitoring = () => {\n if (process.env.NODE_ENV === 'production') {\n Sentry.init({\n dsn: process.env.REACT_APP_SENTRY_DSN,\n integrations: [new Integrations.BrowserTracing()],\n tracesSampleRate: 0.2,\n environment: process.env.NODE_ENV,\n });\n }\n};\n\nconst logError = (error: Error, context?: Record<string, any>) => {\n console.error(error, context);\n if (process.env.NODE_ENV === 'production') {\n Sentry.captureException(error, { extra: context });\n }\n};\n\nexport { initMonitoring, logError, Sentry };\n```\n\n#### API Error Handling Middleware\n\n```typescript\n// api/errorHandler.ts\nexport const withErrorHandling = async <T>(\n apiCall: () => Promise<ApiResponse<T>>,\n onError?: (error: string) => void\n): Promise<ApiResponse<T>> => {\n try {\n const result = await apiCall();\n if (result.error) {\n logError(new Error(result.error), { status: result.status });\n onError?.(result.error);\n }\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'An unknown error occurred';\n logError(new Error(message));\n onError?.(message);\n return { error: message, status: 500 };\n }\n};\n```\n\n### 6. Environment Configuration\n\nCreate a `.env` file in your frontend project:\n\n```env\nREACT_APP_API_URL=your-api-gateway-url\nREACT_APP_USER_POOL_ID=your-cognito-user-pool-id\nREACT_APP_CLIENT_ID=your-cognito-app-client-id\nREACT_APP_SENTRY_DSN=your-sentry-dsn\n```\n\n### 7. Deployment\n\n#### CI/CD Pipeline Example (GitHub Actions)\n\n```yaml\n# .github/workflows/deploy-frontend.yml\nname: Deploy Frontend\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build-and-deploy:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v2\n \n - name: Set up Node.js\n uses: actions/setup-node@v2\n with:\n node-version: '16'\n \n - name: Install dependencies\n run: npm ci\n \n - name: Build\n run: |\n echo \"REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}\" >> .env\n echo \"REACT_APP_USER_POOL_ID=${{ secrets.REACT_APP_USER_POOL_ID }}\" >> .env\n echo \"REACT_APP_CLIENT_ID=${{ secrets.REACT_APP_CLIENT_ID }}\" >> .env\n npm run build\n \n - name: Deploy to AWS S3 & CloudFront\n uses: jakejarvis/s3-sync-action@v0.5.1\n with:\n args: --delete\n env:\n AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}\n AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n AWS_REGION: 'us-east-1'\n SOURCE_DIR: 'build'\n```\n\n## π Documentation & Resources\n\n### Frontend Development\n- [AWS Amplify Documentation](https://docs.amplify.aws/)\n- [React Documentation](https://reactjs.org/docs/getting-started.html)\n- [TypeScript Handbook](https://www.typescriptlang.org/docs/)\n- [Sentry for React](https://docs.sentry.io/platforms/javascript/guides/react/)\n\n### Backend Integration\n- [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/v2/guide/home.html)\n- [AWS Lambda Python Guide](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html)\n- [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html)\n- [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html)\n- [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/)\n- [Serverless Best Practices](https://docs.aws.amazon.com/whitepapers/latest/serverless-architectures-lambda/best-practices.html)\n", |
| "size": 16136, |
| "language": "markdown" |
| }, |
| "requirements-dev.txt": { |
| "content": "aws-cdk-lib>=2.0.0\nconstructs>=10.0.0\npytest>=7.0.0\npylint>=2.15.0\nblack>=22.10.0\nmypy>=0.991\nboto3>=1.26.0\npython-dotenv>=0.21.0\npytest-cov>=4.0.0\naws-cdk.aws-apigatewayv2-alpha>=2.0.0\ntypes-requests>=2.28.0\npydantic>=1.10.0\n", |
| "size": 226, |
| "language": "text" |
| }, |
| "package.json": { |
| "content": "{\n \"name\": \"aws-serverless-backend\",\n \"version\": \"1.0.0\",\n \"description\": \"Advanced AWS Serverless Backend with Cognito, API Gateway, Lambda, and DynamoDB\",\n \"private\": true,\n \"scripts\": {\n \"deploy\": \"cdk deploy --all\",\n \"destroy\": \"cdk destroy --all\",\n \"synth\": \"cdk synth\",\n \"test\": \"pytest\",\n \"lint\": \"pylint **/*.py\"\n },\n \"devDependencies\": {\n \"aws-cdk\": \"^2.0.0\",\n \"aws-cdk-lib\": \"^2.0.0\",\n \"constructs\": \"^10.0.0\"\n },\n \"dependencies\": {\n \"@aws-solutions-constructs/aws-apigateway-lambda\": \"^2.0.0\",\n \"@aws-solutions-constructs/aws-cognito-apigateway-lambda\": \"^2.0.0\",\n \"@aws-solutions-constructs/aws-dynamodb-lambda\": \"^2.0.0\"\n }\n}\n", |
| "size": 682, |
| "language": "json" |
| }, |
| ".gitattributes": { |
| "content": "# Auto detect text files and perform LF normalization\n* text=auto\n", |
| "size": 66, |
| "language": "unknown" |
| }, |
| "app.py": { |
| "content": "#!/usr/bin/env python3\nfrom aws_cdk import App, Environment\nfrom backend.backend_stack import BackendStack\nfrom dotenv import load_dotenv\nimport os\n\n# Load environment variables\nload_dotenv()\n\napp = App()\n\n# Get environment variables\naccount = os.getenv('CDK_DEFAULT_ACCOUNT')\nregion = os.getenv('CDK_DEFAULT_REGION', 'us-east-1')\nenv = Environment(account=account, region=region)\n\n# Create stack\nBackendStack(\n app,\n \"ServerlessBackendStack\",\n env=env,\n description=\"Serverless backend with Cognito, API Gateway, Lambda, and DynamoDB\"\n)\n\napp.synth()\n", |
| "size": 563, |
| "language": "python" |
| }, |
| "app.js": { |
| "content": "const express = require('express');\nconst AWS = require('aws-sdk');\nconst serverless = require('serverless-http');\nconst { v4: uuidv4 } = require('uuid');\n\n// Initialize Express app\nconst app = express();\napp.use(express.json());\n\n// Configure AWS\nconst dynamoDb = new AWS.DynamoDB.DocumentClient();\nconst TABLE_NAME = process.env.TABLE_NAME || 'ItemsTable';\n\n// Health check endpoint\napp.get('/health', (req, res) => {\n res.json({ status: 'ok', timestamp: new Date().toISOString() });\n});\n\n// Create item\napp.post('/items', async (req, res) => {\n try {\n const id = uuidv4();\n const { name, description } = req.body;\n \n const params = {\n TableName: TABLE_NAME,\n Item: {\n id,\n name,\n description,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString()\n }\n };\n\n await dynamoDb.put(params).promise();\n res.status(201).json(params.Item);\n } catch (error) {\n console.error('Error creating item:', error);\n res.status(500).json({ error: 'Could not create item' });\n }\n});\n\n// Get all items\napp.get('/items', async (req, res) => {\n try {\n const params = {\n TableName: TABLE_NAME\n };\n\n const result = await dynamoDb.scan(params).promise();\n res.json(result.Items || []);\n } catch (error) {\n console.error('Error fetching items:', error);\n res.status(500).json({ error: 'Could not fetch items' });\n }\n});\n\n// Get single item\napp.get('/items/:id', async (req, res) => {\n try {\n const { id } = req.params;\n \n const params = {\n TableName: TABLE_NAME,\n Key: { id }\n };\n\n const result = await dynamoDb.get(params).promise();\n \n if (!result.Item) {\n return res.status(404).json({ error: 'Item not found' });\n }\n \n res.json(result.Item);\n } catch (error) {\n console.error('Error fetching item:', error);\n res.status(500).json({ error: 'Could not fetch item' });\n }\n});\n\n// Update item\napp.put('/items/:id', async (req, res) => {\n try {\n const { id } = req.params;\n const { name, description } = req.body;\n \n const params = {\n TableName: TABLE_NAME,\n Key: { id },\n UpdateExpression: 'set #name = :name, description = :desc, updatedAt = :updatedAt',\n ExpressionAttributeNames: {\n '#name': 'name'\n },\n ExpressionAttributeValues: {\n ':name': name,\n ':desc': description,\n ':updatedAt': new Date().toISOString()\n },\n ReturnValues: 'ALL_NEW'\n };\n\n const result = await dynamoDb.update(params).promise();\n res.json(result.Attributes);\n } catch (error) {\n console.error('Error updating item:', error);\n res.status(500).json({ error: 'Could not update item' });\n }\n});\n\n// Delete item\napp.delete('/items/:id', async (req, res) => {\n try {\n const { id } = req.params;\n \n const params = {\n TableName: TABLE_NAME,\n Key: { id },\n ReturnValues: 'ALL_OLD'\n };\n\n const result = await dynamoDb.delete(params).promise();\n \n if (!result.Attributes) {\n return res.status(404).json({ error: 'Item not found' });\n }\n \n res.json({ message: 'Item deleted successfully' });\n } catch (error) {\n console.error('Error deleting item:', error);\n res.status(500).json({ error: 'Could not delete item' });\n }\n});\n\n// Error handling middleware\napp.use((err, req, res, next) => {\n console.error(err.stack);\n res.status(500).json({ error: 'Something went wrong!' });\n});\n\n// Export the serverless handler\nmodule.exports.handler = serverless(app);\n\n// For local development\nif (process.env.NODE_ENV !== 'production') {\n const PORT = process.env.PORT || 3000;\n app.listen(PORT, () => {\n console.log(`Server is running on http://localhost:${PORT}`);\n });\n}\n", |
| "size": 3749, |
| "language": "javascript" |
| }, |
| "frontend/package.json": { |
| "content": "{\n \"name\": \"serverless-frontend\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"@sentry/react\": \"^7.0.0\",\n \"@sentry/tracing\": \"^7.0.0\",\n \"@testing-library/jest-dom\": \"^5.16.5\",\n \"@testing-library/react\": \"^13.4.0\",\n \"@testing-library/user-event\": \"^13.5.0\",\n \"@types/jest\": \"^27.5.2\",\n \"@types/node\": \"^16.18.0\",\n \"@types/react\": \"^18.0.24\",\n \"@types/react-dom\": \"^18.0.8\",\n \"amazon-cognito-identity-js\": \"^5.2.10\",\n \"axios\": \"^1.1.3\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\",\n \"react-router-dom\": \"^6.4.3\",\n \"react-scripts\": \"5.0.1\",\n \"typescript\": \"^4.8.4\",\n \"web-vitals\": \"^2.1.4\"\n },\n \"scripts\": {\n \"start\": \"react-scripts start\",\n \"build\": \"react-scripts build\",\n \"test\": \"react-scripts test\",\n \"eject\": \"react-scripts eject\",\n \"lint\": \"eslint src --ext .ts,.tsx\"\n },\n \"eslintConfig\": {\n \"extends\": [\n \"react-app\",\n \"react-app/jest\"\n ]\n },\n \"browserslist\": {\n \"production\": [\n \">0.2%\",\n \"not dead\",\n \"not op_mini all\"\n ],\n \"development\": [\n \"last 1 chrome version\",\n \"last 1 firefox version\",\n \"last 1 safari version\"\n ]\n },\n \"devDependencies\": {\n \"@types/react-router-dom\": \"^5.3.3\",\n \"@typescript-eslint/eslint-plugin\": \"^5.38.1\",\n \"@typescript-eslint/parser\": \"^5.38.1\",\n \"eslint\": \"^8.24.0\",\n \"eslint-config-prettier\": \"^8.5.0\",\n \"eslint-plugin-prettier\": \"^4.2.1\",\n \"eslint-plugin-react\": \"^7.31.8\",\n \"prettier\": \"^2.7.1\"\n }\n}\n", |
| "size": 1517, |
| "language": "json" |
| }, |
| "frontend/src/context/AuthContext.tsx": { |
| "content": "import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';\nimport { signIn, signUp, signOut, getCurrentUser, isAuthenticated, CognitoUser } from '../services/auth';\n\ninterface AuthContextType {\n user: CognitoUser | null;\n loading: boolean;\n error: string | null;\n login: (email: string, password: string) => Promise<void>;\n register: (email: string, password: string, name: string) => Promise<void>;\n logout: () => void;\n isAuthenticated: boolean;\n}\n\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\n\nexport const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {\n const [user, setUser] = useState<CogniteUser | null>(null);\n const [loading, setLoading] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const loadUser = async () => {\n try {\n const currentUser = await getCurrentUser();\n setUser(currentUser);\n } catch (error) {\n console.error('Error loading user', error);\n } finally {\n setLoading(false);\n }\n };\n\n loadUser();\n }, []);\n\n const login = async (email: string, password: string) => {\n setLoading(true);\n setError(null);\n try {\n await signIn(email, password);\n const currentUser = await getCurrentUser();\n setUser(currentUser);\n } catch (error) {\n console.error('Login error:', error);\n setError(error instanceof Error ? error.message : 'Login failed');\n throw error;\n } finally {\n setLoading(false);\n }\n };\n\n const register = async (email: string, password: string, name: string) => {\n setLoading(true);\n setError(null);\n try {\n await signUp({ email, password, name });\n // Auto-login after registration\n await login(email, password);\n } catch (error) {\n console.error('Registration error:', error);\n setError(error instanceof Error ? error.message : 'Registration failed');\n throw error;\n } finally {\n setLoading(false);\n }\n };\n\n const logout = () => {\n signOut();\n setUser(null);\n };\n\n const value = {\n user,\n loading,\n error,\n login,\n register,\n logout,\n isAuthenticated: !!user,\n };\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n};\n\nexport const useAuth = (): AuthContextType => {\n const context = useContext(AuthContext);\n if (context === undefined) {\n throw new Error('useAuth must be used within an AuthProvider');\n }\n return context;\n};\n\nexport default AuthContext;\n", |
| "size": 2573, |
| "language": "unknown" |
| }, |
| "frontend/src/services/api.ts": { |
| "content": "import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';\nimport { getToken } from './auth';\n\nconst API_URL = process.env.REACT_APP_API_URL || 'http://localhost:3000';\n\nclass ApiClient {\n private client: AxiosInstance;\n\n constructor() {\n this.client = axios.create({\n baseURL: API_URL,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n // Add request interceptor for auth token\n this.client.interceptors.request.use(\n (config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n },\n (error) => {\n return Promise.reject(error);\n }\n );\n }\n\n // Generic request method\n async request<T>(config: AxiosRequestConfig): Promise<T> {\n try {\n const response: AxiosResponse<T> = await this.client.request<T>(config);\n return response.data;\n } catch (error) {\n if (axios.isAxiosError(error)) {\n throw new Error(error.response?.data?.message || 'An error occurred');\n }\n throw error;\n }\n }\n\n // CRUD Operations for Items\n async getItems<T>(): Promise<T> {\n return this.request<T>({\n method: 'GET',\n url: '/items',\n });\n }\n\n async getItem<T>(id: string): Promise<T> {\n return this.request<T>({\n method: 'GET',\n url: `/items/${id}`,\n });\n }\n\n async createItem<T>(data: any): Promise<T> {\n return this.request<T>({\n method: 'POST',\n url: '/items',\n data,\n });\n }\n\n async updateItem<T>(id: string, data: any): Promise<T> {\n return this.request<T>({\n method: 'PUT',\n url: `/items/${id}`,\n data,\n });\n }\n\n async deleteItem<T>(id: string): Promise<T> {\n return this.request<T>({\n method: 'DELETE',\n url: `/items/${id}`,\n });\n }\n}\n\nexport const apiClient = new ApiClient();\n", |
| "size": 1900, |
| "language": "typescript" |
| }, |
| "frontend/src/services/auth.ts": { |
| "content": "import { CognitoUser, CognitoUserPool, AuthenticationDetails, CognitoUserSession } from 'amazon-cognito-identity-js';\n\nconst poolData = {\n UserPoolId: process.env.REACT_APP_USER_POOL_ID || '',\n ClientId: process.env.REACT_APP_CLIENT_ID || ''\n};\n\nexport const userPool = new CognitoUserPool(poolData);\n\ninterface SignUpParams {\n email: string;\n password: string;\n name: string;\n}\n\nexport const signUp = async ({ email, password, name }: SignUpParams): Promise<CognitoUser> => {\n return new Promise((resolve, reject) => {\n const attributeList = [\n {\n Name: 'email',\n Value: email\n },\n {\n Name: 'name',\n Value: name\n }\n ];\n\n userPool.signUp(email, password, attributeList, [], (err, result) => {\n if (err) {\n reject(err);\n return;\n }\n if (result) {\n resolve(result.user);\n }\n });\n });\n};\n\nexport const signIn = (email: string, password: string): Promise<CognitoUserSession> => {\n const authenticationDetails = new AuthenticationDetails({\n Username: email,\n Password: password\n });\n\n const userData = {\n Username: email,\n Pool: userPool\n };\n\n const cognitoUser = new CognitoUser(userData);\n\n return new Promise((resolve, reject) => {\n cognitoUser.authenticateUser(authenticationDetails, {\n onSuccess: (session) => {\n localStorage.setItem('token', session.getIdToken().getJwtToken());\n resolve(session);\n },\n onFailure: (err) => {\n reject(err);\n },\n newPasswordRequired: () => {\n // Handle case where user needs to set a new password\n reject(new Error('New password required'));\n }\n });\n });\n};\n\nexport const signOut = (): void => {\n const user = userPool.getCurrentUser();\n if (user) {\n user.signOut();\n }\n localStorage.removeItem('token');\n};\n\nexport const getCurrentUser = (): Promise<CognitoUser | null> => {\n return new Promise((resolve) => {\n const user = userPool.getCurrentUser();\n \n if (!user) {\n resolve(null);\n return;\n }\n\n user.getSession((err: Error | null) => {\n if (err) {\n resolve(null);\n return;\n }\n resolve(user);\n });\n });\n};\n\nexport const getToken = (): string | null => {\n return localStorage.getItem('token');\n};\n\nexport const isAuthenticated = async (): Promise<boolean> => {\n try {\n const user = await getCurrentUser();\n return !!user;\n } catch (error) {\n return false;\n }\n};\n", |
| "size": 2472, |
| "language": "typescript" |
| }, |
| "backend/backend_stack.py": { |
| "content": "from aws_cdk import (\n Stack,\n aws_dynamodb as dynamodb,\n aws_lambda as _lambda,\n aws_apigateway as apigw,\n aws_cognito as cognito,\n aws_iam as iam,\n aws_logs as logs,\n RemovalPolicy,\n Duration,\n)\nfrom constructs import Construct\n\nclass BackendStack(Stack):\n def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:\n super().__init__(scope, construct_id, **kwargs)\n\n # Create Cognito User Pool\n user_pool = cognito.UserPool(\n self, \"UserPool\",\n user_pool_name=\"serverless-backend-users\",\n self_sign_up_enabled=True,\n sign_in_aliases={\"email\": True},\n auto_verify={\"email\": True},\n password_policy={\n \"min_length\": 8,\n \"require_lowercase\": True,\n \"require_uppercase\": True,\n \"require_digits\": True,\n \"require_symbols\": True,\n },\n account_recovery=cognito.AccountRecovery.EMAIL_ONLY,\n removal_policy=RemovalPolicy.DESTROY\n )\n\n # Add App Client\n user_pool_client = cognito.UserPoolClient(\n self, \"UserPoolClient\",\n user_pool=user_pool,\n auth_flows={\"admin_user_password\": True, \"user_password\": True, \"user_srp\": True},\n o_auth={\n \"flows\": {\"authorization_code_grant\": True},\n \"scopes\": [cognito.OAuthScope.EMAIL, cognito.OAuthScope.OPENID, cognito.OAuthScope.PROFILE],\n \"callback_urls\": [\"http://localhost:3000/callback\"],\n \"logout_urls\": [\"http://localhost:3000\"]\n }\n )\n\n # Create DynamoDB Table\n table = dynamodb.Table(\n self, \"ItemsTable\",\n table_name=\"serverless-items\",\n partition_key={\"name\": \"id\", \"type\": dynamodb.AttributeType.STRING},\n sort_key={\"name\": \"created_at\", \"type\": dynamodb.AttributeType.STRING},\n billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,\n removal_policy=RemovalPolicy.DESTROY,\n point_in_time_recovery=True\n )\n\n # Add GSI for querying by user\n table.add_global_secondary_index(\n index_name=\"user-index\",\n partition_key={\"name\": \"user_id\", \"type\": dynamodb.AttributeType.STRING},\n sort_key={\"name\": \"created_at\", \"type\": dynamodb.AttributeType.STRING}\n )\n\n # Create Lambda execution role\n lambda_role = iam.Role(\n self, \"LambdaExecutionRole\",\n assumed_by=iam.ServicePrincipal(\"lambda.amazonaws.com\"),\n managed_policies=[\n iam.ManagedPolicy.from_aws_managed_policy_name(\"service-role/AWSLambdaBasicExecutionRole\"),\n iam.ManagedPolicy.from_aws_managed_policy_name(\"AmazonDynamoDBFullAccess\"),\n iam.ManagedPolicy.from_aws_managed_policy_name(\"AmazonCognitoReadOnly\")\n ]\n )\n\n # Create Lambda function\n lambda_fn = _lambda.Function(\n self, \"ApiHandler\",\n runtime=_lambda.Runtime.PYTHON_3_9,\n handler=\"lambda_function.lambda_handler\",\n code=_lambda.Code.from_asset(\"lambda\"),\n role=lambda_role,\n environment={\n \"USER_POOL_ID\": user_pool.user_pool_id,\n \"CLIENT_ID\": user_pool_client.user_pool_client_id,\n \"TABLE_NAME\": table.table_name,\n \"REGION\": self.region\n },\n timeout=Duration.seconds(30),\n memory_size=256,\n log_retention=logs.RetentionDays.ONE_WEEK\n )\n\n # Grant Lambda access to DynamoDB\n table.grant_read_write_data(lambda_fn)\n\n # Create API Gateway with Cognito Authorizer\n authorizer = apigw.CognitoUserPoolsAuthorizer(\n self, \"CognitoAuthorizer\",\n cognito_user_pools=[user_pool]\n )\n\n # Create REST API\n api = apigw.RestApi(\n self, \"ServerlessApi\",\n default_cors_preflight_options={\n \"allow_origins\": apigw.Cors.ALL_ORIGINS,\n \"allow_methods\": apigw.Cors.ALL_METHODS,\n \"allow_headers\": [\"Content-Type\", \"Authorization\"],\n \"allow_credentials\": True\n },\n deploy_options={\n \"stage_name\": \"prod\",\n \"logging_level\": apigw.MethodLoggingLevel.INFO,\n \"metrics_enabled\": True\n }\n )\n\n # Add resources and methods\n items = api.root.add_resource(\"items\")\n item = items.add_resource(\"{id}\")\n\n # Add CORS preflight for OPTIONS\n self._add_cors_options(items)\n self._add_cors_options(item)\n\n # Integrate Lambda with API Gateway\n lambda_integration = apigw.LambdaIntegration(\n lambda_fn,\n proxy=True,\n integration_responses=[{\"statusCode\": \"200\"}]\n )\n\n # Add methods with Cognito authorization\n items.add_method(\"GET\", lambda_integration, authorizer=authorizer)\n items.add_method(\"POST\", lambda_integration, authorizer=authorizer)\n item.add_method(\"GET\", lambda_integration, authorizer=authorizer)\n item.add_method(\"PUT\", lambda_integration, authorizer=authorizer)\n item.add_method(\"DELETE\", lambda_integration, authorizer=authorizer)\n\n # Outputs\n self.api_url = api.url\n self.user_pool_id = user_pool.user_pool_id\n self.user_pool_client_id = user_pool_client.user_pool_client_id\n\n def _add_cors_options(self, resource):\n resource.add_method(\n 'OPTIONS',\n apigw.MockIntegration(\n integration_responses=[{\n 'statusCode': '200',\n 'responseParameters': {\n 'method.response.header.Access-Control-Allow-Headers': \"'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'\",\n 'method.response.header.Access-Control-Allow-Origin': \"'*'\",\n 'method.response.header.Access-Control-Allow-Methods': \"'OPTIONS,GET,PUT,POST,DELETE'\"\n }\n }],\n passthrough_behavior=apigw.PassthroughBehavior.WHEN_NO_MATCH,\n request_templates={\"application/json\": \"{statusCode: 200}\"}\n ),\n method_responses=[{\n 'statusCode': '200',\n 'responseParameters': {\n 'method.response.header.Access-Control-Allow-Headers': True,\n 'method.response.header.Access-Control-Allow-Methods': True,\n 'method.response.header.Access-Control-Allow-Origin': True,\n }\n }]\n )\n", |
| "size": 6747, |
| "language": "python" |
| }, |
| "lambda/requirements.txt": { |
| "content": "boto3>=1.26.0\npython-jose[cryptography]>=3.3.0\nrequests>=2.28.0\npydantic>=1.10.0\npython-dotenv>=0.21.0\naws-lambda-powertools>=2.0.0\n", |
| "size": 132, |
| "language": "text" |
| }, |
| "lambda/lambda_function.py": { |
| "content": "import os\nimport json\nimport boto3\nimport logging\nfrom datetime import datetime\nfrom typing import Dict, Any, Optional\nfrom boto3.dynamodb.conditions import Key\nfrom botocore.exceptions import ClientError\nfrom pydantic import BaseModel, validator\nfrom aws_lambda_powertools import Logger, Tracer, Metrics\nfrom aws_lambda_powertools.utilities.typing import LambdaContext\n\n# Initialize utilities\nlogger = Logger()\ntracer = Tracer()\nmetrics = Metrics()\n\n# Initialize AWS clients\ndynamodb = boto3.resource('dynamodb')\ntable_name = os.environ['TABLE_NAME']\ntable = dynamodb.Table(table_name)\n\n# Pydantic Models for request/response validation\nclass ItemCreate(BaseModel):\n name: str\n description: str\n\nclass ItemUpdate(BaseModel):\n name: Optional[str] = None\n description: Optional[str] = None\n\nclass ResponseModel(BaseModel):\n statusCode: int\n body: str\n headers: Dict[str, str] = {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n\n# Utility functions\ndef get_user_id(event: Dict[str, Any]) -> str:\n \"\"\"Extract user ID from Cognito JWT token\"\"\"\n try:\n return event['requestContext']['authorizer']['claims']['sub']\n except (KeyError, TypeError) as e:\n logger.error(f\"Error extracting user ID: {str(e)}\")\n raise ValueError(\"Invalid authorization token\")\n\ndef build_response(status_code: int, body: Any) -> Dict[str, Any]:\n \"\"\"Build API Gateway response\"\"\"\n return {\n 'statusCode': status_code,\n 'body': json.dumps(body, default=str),\n 'headers': {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n }\n\n# Lambda handler\n@logger.inject_lambda_context(log_event=True)\n@tracer.capture_lambda_handler\n@metrics.log_metrics(capture_cold_start_metric=True)\ndef lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict[str, Any]:\n \"\"\"\n Main Lambda handler for the API Gateway\n \"\"\"\n try:\n http_method = event['httpMethod']\n path = event['resource']\n \n # Route the request\n if http_method == 'GET' and path == '/items':\n return list_items(event)\n elif http_method == 'POST' and path == '/items':\n return create_item(event)\n elif http_method == 'GET' and path.startswith('/items/'):\n return get_item(event)\n elif http_method == 'PUT' and path.startswith('/items/'):\n return update_item(event)\n elif http_method == 'DELETE' and path.startswith('/items/'):\n return delete_item(event)\n else:\n return build_response(404, {'message': 'Not Found'})\n \n except Exception as e:\n logger.error(f\"Error processing request: {str(e)}\")\n return build_response(500, {'message': 'Internal Server Error'})\n\n# CRUD Operations\ndef list_items(event: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"List all items for the authenticated user\"\"\"\n try:\n user_id = get_user_id(event)\n \n response = table.query(\n IndexName='user-index',\n KeyConditionExpression=Key('user_id').eq(user_id)\n )\n \n return build_response(200, {\n 'items': response.get('Items', [])\n })\n \n except Exception as e:\n logger.error(f\"Error listing items: {str(e)}\")\n return build_response(500, {'message': 'Failed to list items'})\n\ndef create_item(event: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Create a new item\"\"\"\n try:\n user_id = get_user_id(event)\n request_body = json.loads(event['body'])\n \n # Validate input\n item_data = ItemCreate(**request_body)\n \n # Generate item ID and timestamps\n item_id = context.aws_request_id\n timestamp = datetime.utcnow().isoformat()\n \n # Prepare item\n item = {\n 'id': item_id,\n 'user_id': user_id,\n 'created_at': timestamp,\n 'updated_at': timestamp,\n **item_data.dict()\n }\n \n # Save to DynamoDB\n table.put_item(Item=item)\n \n return build_response(201, item)\n \n except Exception as e:\n logger.error(f\"Error creating item: {str(e)}\")\n return build_response(500, {'message': 'Failed to create item'})\n\ndef get_item(event: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Get a single item by ID\"\"\"\n try:\n user_id = get_user_id(event)\n item_id = event['pathParameters']['id']\n \n # Get item from DynamoDB\n response = table.get_item(\n Key={'id': item_id}\n )\n \n item = response.get('Item')\n if not item:\n return build_response(404, {'message': 'Item not found'})\n \n # Ensure the user owns this item\n if item.get('user_id') != user_id:\n return build_response(403, {'message': 'Forbidden'})\n \n return build_response(200, item)\n \n except ClientError as e:\n logger.error(f\"DynamoDB error: {str(e)}\")\n return build_response(500, {'message': 'Error retrieving item'})\n except Exception as e:\n logger.error(f\"Error getting item: {str(e)}\")\n return build_response(500, {'message': 'Internal server error'})\n\ndef update_item(event: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Update an existing item\"\"\"\n try:\n user_id = get_user_id(event)\n item_id = event['pathParameters']['id']\n request_body = json.loads(event['body'])\n \n # Validate input\n update_data = ItemUpdate(**request_body).dict(exclude_unset=True)\n \n if not update_data:\n return build_response(400, {'message': 'No valid fields to update'})\n \n # Prepare update expression\n update_expression = []\n expression_attribute_values = {}\n \n for key, value in update_data.items():\n update_expression.append(f\"{key} = :{key}\")\n expression_attribute_values[f\":{key}\"] = value\n \n # Add updated_at timestamp\n update_expression.append(\"updated_at = :updated_at\")\n expression_attribute_values[\":updated_at\"] = datetime.utcnow().isoformat()\n \n # Update item in DynamoDB\n response = table.update_item(\n Key={'id': item_id},\n UpdateExpression=\"SET \" + \", \".join(update_expression),\n ExpressionAttributeValues=expression_attribute_values,\n ConditionExpression=\"user_id = :user_id\",\n ExpressionAttributeValues={\n **expression_attribute_values,\n \":user_id\": user_id\n },\n ReturnValues=\"ALL_NEW\"\n )\n \n return build_response(200, response['Attributes'])\n \n except ClientError as e:\n if e.response['Error']['Code'] == 'ConditionalCheckFailedException':\n return build_response(403, {'message': 'Forbidden'})\n logger.error(f\"DynamoDB error: {str(e)}\")\n return build_response(500, {'message': 'Error updating item'})\n except Exception as e:\n logger.error(f\"Error updating item: {str(e)}\")\n return build_response(500, {'message': 'Internal server error'})\n\ndef delete_item(event: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Delete an item\"\"\"\n try:\n user_id = get_user_id(event)\n item_id = event['pathParameters']['id']\n \n # Delete item from DynamoDB\n response = table.delete_item(\n Key={'id': item_id},\n ConditionExpression=\"user_id = :user_id\",\n ExpressionAttributeValues={\n ':user_id': user_id\n },\n ReturnValues=\"ALL_OLD\"\n )\n \n if 'Attributes' not in response:\n return build_response(404, {'message': 'Item not found'})\n \n return build_response(200, {'message': 'Item deleted successfully'})\n \n except ClientError as e:\n if e.response['Error']['Code'] == 'ConditionalCheckFailedException':\n return build_response(403, {'message': 'Forbidden'})\n logger.error(f\"DynamoDB error: {str(e)}\")\n return build_response(500, {'message': 'Error deleting item'})\n except Exception as e:\n logger.error(f\"Error deleting item: {str(e)}\")\n return build_response(500, {'message': 'Internal server error'})\n", |
| "size": 8410, |
| "language": "python" |
| } |
| }, |
| "_cache_metadata": { |
| "url": "https://github.com/ronelsolomon/AWS-backend.git", |
| "content_type": "github", |
| "cached_at": "2026-03-02T22:48:23.536393", |
| "cache_key": "6fdad44d776a0611631264d389d28f83" |
| } |
| } |