DEV Community

Sherif sani
Sherif sani

Posted on

From Internal Server Error to Success: Debugging AWS Lambda and API Gateway

Building a serverless application is exciting—until you hit a roadblock like the dreaded "Internal Server Error." Recently, I encountered this while creating a simple API with AWS Lambda and API Gateway to process query parameters and return a structured response. This post breaks down my debugging journey, what went wrong, and how I fixed it.


The Problem

My goal was straightforward:

  1. Accept transactionId, type, and amount as query parameters.
  2. Log the parameters and return them in a JSON response.

Here’s the Lambda function I wrote initially:

import json

def lambda_handler(event, context):
    transactionId = event['queryStringParameters']['transactionId']
    transactionType = event['queryStringParameters']['type']
    transactionAmount = event['queryStringParameters']['amount']

    print(f"TransactionId = {transactionId}")
    print(f"TransactionType = {transactionType}")
    print(f"TransactionAmount = {transactionAmount}")

    response = {
        'transactionId': transactionId,
        'type': transactionType,
        'amount': transactionAmount,
        'message': "Hello from lambda land"
    }

    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
        },
        'body': json.dumps(response)
    }
Enter fullscreen mode Exit fullscreen mode

But when I tested the API through the browser, I kept getting:

  • 500 Internal Server Error
  • CORS errors when calling the API from my frontend.

What Went Wrong?

  1. Missing Query Parameters:

    If any query parameter was missing, the code would throw a KeyError. This caused Lambda to fail and returned a generic error message without any details.

  2. No Error Handling:

    There was no mechanism to catch and log errors. This made debugging harder because the API Gateway logs only showed "Internal Server Error."

  3. CORS Issues:

    The response didn’t include Access-Control-Allow-Origin, which is required to enable communication between the frontend and the API Gateway endpoint.


The Fix

I rewrote the Lambda function with robust error handling, validation, and proper CORS configuration.

import json

def lambda_handler(event, context):
    try:
        # Extract query string parameters
        params = event.get('queryStringParameters', {})
        transactionId = params.get('transactionId', 'N/A')
        transactionType = params.get('type', 'N/A')
        transactionAmount = params.get('amount', 'N/A')

        # Validate parameters
        if transactionId == 'N/A' or transactionType == 'N/A' or transactionAmount == 'N/A':
            raise ValueError("Missing required query parameters")

        # Log parameters
        print(f"TransactionId = {transactionId}")
        print(f"TransactionType = {transactionType}")
        print(f"TransactionAmount = {transactionAmount}")

        # Create response
        transactionResponse = {
            'transactionId': transactionId,
            'type': transactionType,
            'amount': transactionAmount,
            'message': "Hello from lambda land"
        }
        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps(transactionResponse)
        }
    except Exception as e:
        print(f"Error: {e}")
        return {
            'statusCode': 500,
            'headers': {'Access-Control-Allow-Origin': '*'},
            'body': json.dumps({'message': 'Internal Server Error', 'error': str(e)})
        }
Enter fullscreen mode Exit fullscreen mode

Key Changes and Why They Work

  1. Safe Parameter Extraction:

    Instead of directly accessing event['queryStringParameters'], I used .get() with default values to prevent KeyError exceptions.

  2. Validation:

    The code now checks if required parameters are missing and raises a ValueError. This ensures we only process valid inputs.

  3. Error Handling with Try-Except:

    Wrapping the logic in try...except allows us to catch errors and return a detailed response while logging the issue for debugging in CloudWatch.

  4. CORS Headers:

    Adding Access-Control-Allow-Origin enables the frontend to communicate with the API successfully.


The Final Test

API Request

curl -X GET "https://<your-api-id>.execute-api.us-east-1.amazonaws.com/test/transactions?transactionId=123&type=credit&amount=1000"
Enter fullscreen mode Exit fullscreen mode

Response

{
    "transactionId": "123",
    "type": "credit",
    "amount": "1000",
    "message": "Hello from lambda land"
}
Enter fullscreen mode Exit fullscreen mode

Takeaways

  1. Validation is Crucial: Always validate inputs to ensure your application works as expected.
  2. Error Handling Simplifies Debugging: Wrapping your logic in try...except helps identify issues quickly.
  3. CORS Configuration Matters: If your API serves a frontend, always include CORS headers in the response.
  4. Logs Are Your Friend: Use print statements to log details in CloudWatch and understand what’s happening.

What’s Next?

Now that I’ve successfully implemented and tested this API, I’m moving on to integrating it with my S3 bucket for file uploads. This will involve:

  • Generating pre-signed URLs in Lambda.
  • Updating the frontend to handle file uploads.

Have you encountered similar challenges while working with serverless applications? Let’s discuss in the comments!


Top comments (0)