Skip to content

Commit 620b1a3

Browse files
oladayo21claude
andcommitted
Add static file serving to Lambda handler
- Fix 404 errors for client-side JavaScript and CSS files - Add tryServeStaticFile() function to handle /_app/ assets - Serve static files before processing with SvelteKit - Include proper content types and cache headers - Support immutable assets with long-term caching - Handle fonts, images, and other static assets Resolves issue where Lambda returned 404 for SvelteKit client bundles. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c058c7f commit 620b1a3

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

files/handler.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import {
88
convertWebResponseToLambdaEvent,
99
} from '@foladayo/lambda-adapter-kit';
1010
import { getRequest } from '@sveltejs/kit/node';
11+
import { readFileSync } from 'node:fs';
12+
import { fileURLToPath } from 'node:url';
13+
import { dirname, join, extname } from 'node:path';
1114

1215
/* global ENV_PREFIX */
1316

@@ -16,6 +19,9 @@ const server = new Server(manifest);
1619
const body_size_limit = Number.parseInt(env('BODY_SIZE_LIMIT', 'BODY_SIZE_LIMIT'));
1720
const binaryMediaTypes = BINARY_MEDIA_TYPES;
1821

22+
// Get the directory of this handler file
23+
const __dirname = dirname(fileURLToPath(import.meta.url));
24+
1925
await server.init({
2026
env: process.env,
2127
});
@@ -83,6 +89,53 @@ function isALBEvent(event) {
8389
return event.requestContext && 'elb' in event.requestContext;
8490
}
8591

92+
/**
93+
* Serve static files from the bundled client directory
94+
* @param {string} path - The requested path
95+
* @returns {Promise<Response|null>} - Response for static file or null if not found
96+
*/
97+
async function tryServeStaticFile(path) {
98+
// Handle client assets (JS, CSS, etc.)
99+
if (path.startsWith('/_app/') || path.startsWith('/favicon.ico')) {
100+
try {
101+
const filePath = join(__dirname, 'client', path);
102+
const content = readFileSync(filePath);
103+
104+
// Determine content type
105+
const ext = extname(path).toLowerCase();
106+
const contentType = {
107+
'.js': 'application/javascript',
108+
'.css': 'text/css',
109+
'.ico': 'image/x-icon',
110+
'.png': 'image/png',
111+
'.jpg': 'image/jpeg',
112+
'.jpeg': 'image/jpeg',
113+
'.gif': 'image/gif',
114+
'.svg': 'image/svg+xml',
115+
'.woff': 'font/woff',
116+
'.woff2': 'font/woff2',
117+
'.ttf': 'font/ttf',
118+
'.eot': 'application/vnd.ms-fontobject'
119+
}[ext] || 'application/octet-stream';
120+
121+
return new Response(content, {
122+
status: 200,
123+
headers: {
124+
'Content-Type': contentType,
125+
'Cache-Control': path.includes('/immutable/')
126+
? 'public, max-age=31536000, immutable'
127+
: 'public, max-age=3600'
128+
}
129+
});
130+
} catch (error) {
131+
// File not found or error reading
132+
return null;
133+
}
134+
}
135+
136+
return null;
137+
}
138+
86139
/**
87140
* AWS Lambda handler with hybrid architecture
88141
* @param {any} event - Lambda Function URL, API Gateway, or ALB event
@@ -93,6 +146,15 @@ export const handler = async (event, context) => {
93146
try {
94147
// 🔥 Use our superior event conversion (handles all Lambda event types)
95148
const webRequest = convertLambdaEventToWebRequest(event);
149+
150+
// Try to serve static files first
151+
const staticFileResponse = await tryServeStaticFile(new URL(webRequest.url).pathname);
152+
if (staticFileResponse) {
153+
return await convertWebResponseToLambdaEvent(staticFileResponse, {
154+
binaryMediaTypes,
155+
multiValueHeaders: isALBEvent(event),
156+
});
157+
}
96158

97159
// Convert to Node.js request format for SvelteKit
98160
const nodeRequest = convertWebRequestToNodeRequest(webRequest, event);

0 commit comments

Comments
 (0)