Skip to main content

API Gateway Troubleshooting Guide

Problem: 404 Errors on API Routes

If you're getting 404 errors on https://api.hashpass.tech/api/*, it usually means the custom domain, API mapping, or Lambda deployment is out of sync in the target account.

Current State

api.hashpass.tech and api-dev.hashpass.tech should point to the target-account API Gateway custom domains.

Active Lambda names:

  • Production: hashpass-prod-expo-router-api
  • Development: hashpass-dev-expo-router-api

The target web deploy helper updates these functions and verifies /api/config/versions after each deploy. If the endpoint reports an older release than package.json, the deploy is stale and must be treated as failed.

Quick Checklist

1. Verify API Gateway Exists

  • Go to AWS API Gateway Console
  • Check if there's an API named hashpassApi or similar
  • Verify it has routes configured for /api/*

2. Verify Lambda Function is Deployed

  • Go to AWS Lambda Console
  • Check if function hashpass-prod-expo-router-api or hashpass-dev-expo-router-api exists
  • Verify it's deployed and has the latest code
  • Check CloudWatch logs for errors

3. Verify Custom Domain Configuration

  • In API Gateway Console → Custom Domain Names
  • Check if api.hashpass.tech is configured
  • Verify domain status is "Available"
  • Check that the API mapping is configured

4. Verify DNS Configuration

  • Check Route 53 or your DNS provider
  • Verify api.hashpass.tech has a CNAME or A record
    • Should point to API Gateway's domain, not a stale web edge target

Step-by-Step Configuration

Step 1: Create/Update Lambda Function

  1. Go to AWS Lambda Console

  2. Create or update function hashpass-prod-expo-router-api or hashpass-dev-expo-router-api:

    • Runtime: Node.js 18.x or 20.x
    • Handler: index.handler or handler.handler
    • Timeout: 30 seconds (or more)
    • Memory: 512 MB (or more)
  3. Upload the function code:

    • The function code is packaged by packages/tools/scripts/package-lambda.sh
    • The handler lives in packages/infra/lambda/index.js

Step 2: Configure API Gateway

  1. Create REST API:

    • Go to API Gateway Console
    • Create new REST API or use existing
    • Name: hashpassApi
  2. Create Resource:

    • Create resource /api
    • Create resource /api/{proxy+} (catch-all for all API routes)
  3. Create Method:

    • Select ANY method on /api/{proxy+}
    • Integration type: Lambda Function
    • Select your Lambda function
    • Enable "Use Lambda Proxy integration"
  4. Deploy API:

    • Create new stage (e.g., prod or dev)
    • Note the Invoke URL

Step 3: Configure Custom Domain

  1. In API Gateway Console → Custom Domain Names:

    • Click "Create"
    • Domain name: api.hashpass.tech
    • Certificate: Select ACM certificate for *.hashpass.tech or hashpass.tech
    • Click "Create domain name"
  2. Configure API Mapping:

    • Click on the domain name
    • Click "API mappings" → "Configure API mappings"
    • Add mapping:
      • API: Select your API
      • Stage: Select your stage (e.g., prod)
      • Path: (leave empty for root)
    • Save
  3. Get Target Domain:

    • Note the "API Gateway domain name" (e.g., d-xxxxx.execute-api.us-east-1.amazonaws.com)

Step 4: Configure DNS

  1. In Route 53 or your DNS provider:

    • Create CNAME record:
      • Name: api
      • Type: CNAME
      • Value: API Gateway domain name from Step 3
      • TTL: 300
  2. Wait for DNS propagation (can take up to 48 hours, usually much faster)

Step 5: Test

  1. Test API Gateway directly:

    curl https://YOUR-API-ID.execute-api.REGION.amazonaws.com/prod/api/config/versions
  2. Test custom domain:

    curl https://api.hashpass.tech/api/config/versions

Lambda Function Handler

The Lambda function should use Expo Server to handle API routes. Here's a basic handler:

const { createRequestHandler } = require('@expo/server/adapter/node');

const handler = createRequestHandler({
build: require('path').join(__dirname, 'server'),
});

exports.handler = async (event) => {
try {
// Convert API Gateway event to Request
const request = new Request(
`https://${event.requestContext.domainName}${event.path}${event.rawQueryString ? `?${event.rawQueryString}` : ''}`,
{
method: event.httpMethod,
headers: new Headers(event.headers || {}),
body: event.body ? event.body : undefined,
}
);

// Handle with Expo Server
const response = await handler(request);
const body = await response.text();
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});

return {
statusCode: response.status,
headers: {
...headers,
'Access-Control-Allow-Origin': '*',
},
body,
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: error.message }),
};
}
};

Common Issues

Issue: 404 Not Found

Causes:

  • API Gateway route not configured
  • Lambda function not deployed
  • Custom domain not mapped to API

Solutions:

  • Verify API Gateway has /api/{proxy+} route
  • Check Lambda function exists and is deployed
  • Verify custom domain API mapping

Issue: 502 Bad Gateway

Causes:

  • Lambda function error
  • Lambda timeout
  • Lambda not returning correct format

Solutions:

  • Check CloudWatch logs for Lambda errors
  • Increase Lambda timeout
  • Verify Lambda returns correct API Gateway format

Issue: CORS Errors

Causes:

  • Missing CORS headers in Lambda response
  • API Gateway CORS not configured

Solutions:

  • Add CORS headers in Lambda response (see handler above)
  • Configure CORS in API Gateway if needed

Issue: DNS Not Resolving

Causes:

  • DNS record not created
  • DNS propagation delay
  • Wrong DNS record type

Solutions:

  • Verify CNAME record exists in Route 53
  • Wait for DNS propagation (use dig api.hashpass.tech to check)
  • Ensure using CNAME, not A record

Verification Commands

# Check DNS resolution
dig api.hashpass.tech
nslookup api.hashpass.tech

# Test API Gateway directly (replace with your API ID and region)
curl https://YOUR-API-ID.execute-api.us-east-1.amazonaws.com/prod/api/config/versions

# Test custom domain
curl https://api.hashpass.tech/api/config/versions

Next Steps

  1. Verify all steps above are completed
  2. Check CloudWatch logs for any errors
  3. Test the endpoint with curl or Postman
  4. If still not working, check API Gateway logs and Lambda logs