This project is a functional implementation and reproduction of the work developed by Amber Israelsen (Technical Trainer & Software Developer). The project was built following the technical specifications provided in her GitHub Repository and instructional YouTube Video.
This deployment demonstrates the practical application of an end-to-end serverless architecture on AWS. Key implementations include:
- Continuous Deployment: Provisioned AWS Amplify to establish a CI/CD pipeline, enabling automated frontend deployments triggered by GitHub repository commits.
- Identity Management: Configured Amazon Cognito User Pools to facilitate secure user registration and authentication, specifically configured for a Single-Page Application (SPA) environment.
- Serverless Computing: Developed AWS Lambda functions to execute backend business logic and process ride requests.
- Data Persistence: Implemented Amazon DynamoDB as a NoSQL database solution for high-availability storage of application transaction data.
- API Orchestration: Deployed Amazon API Gateway as a secure RESTful interface, utilizing Cognito Authorizers for request validation.
The following Node.js 20.x code was implemented to handle the backend processing. It validates authorization, selects a unicorn from the fleet, and records the transaction to DynamoDB.
import { randomBytes } from 'crypto';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(client);
const fleet = [
{ Name: 'Angel', Color: 'White', Gender: 'Female' },
{ Name: 'Gil', Color: 'White', Gender: 'Male' },
{ Name: 'Rocinante', Color: 'Yellow', Gender: 'Female' },
];
export const handler = async (event, context) => {
if (!event.requestContext.authorizer) {
return errorResponse('Authorization not configured', context.awsRequestId);
}
const rideId = toUrlString(randomBytes(16));
const username = event.requestContext.authorizer.claims['cognito:username'];
const requestBody = JSON.parse(event.body);
const pickupLocation = requestBody.PickupLocation;
const unicorn = findUnicorn(pickupLocation);
try {
await recordRide(rideId, username, unicorn);
return {
statusCode: 201,
body: JSON.stringify({
RideId: rideId,
Unicorn: unicorn,
Eta: '30 seconds',
Rider: username,
}),
headers: {
'Access-Control-Allow-Origin': '*',
},
};
} catch (err) {
console.error(err);
return errorResponse(err.message, context.awsRequestId);
}
};
function findUnicorn(pickupLocation) {
return fleet[Math.floor(Math.random() * fleet.length)];
}
async function recordRide(rideId, username, unicorn) {
const params = {
TableName: 'Rides',
Item: {
RideId: rideId,
User: username,
Unicorn: unicorn,
RequestTime: new Date().toISOString(),
},
};
await ddb.send(new PutCommand(params));
}
function toUrlString(buffer) {
return buffer.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
function errorResponse(errorMessage, awsRequestId) {
return {
statusCode: 500,
body: JSON.stringify({
Error: errorMessage,
Reference: awsRequestId,
}),
headers: {
'Access-Control-Allow-Origin': '*',
},
};
}The following JSON payload was utilized to verify the API Gateway and Lambda integration:
{
"path": "/ride",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Authorization": "TOKEN_REDACTED",
"content-type": "application/json; charset=UTF-8"
},
"requestContext": {
"authorizer": {
"claims": {
"cognito:username": "test_user"
}
}
},
"body": "{\"PickupLocation\":{\"Latitude\":47.6174,\"Longitude\":-122.2883}}"
}