I have the following code in an expressApp.ts:
// functions/expressApp.ts
import express from 'express';
import path from 'path';
import serverless from 'serverless-http';
import axios from 'axios';
const app = express();
// Adjust basePath to point to the public directory at the root of the deployment environment
const basePath = path.resolve(__dirname, '..', 'public'); // Moving up one level from `functions`
console.log("Starting Express app...");
console.log("Base path for static files:", basePath);
// Set up middleware to serve static files from basePath
app.use(express.static(basePath));
// Define city coordinates
const cityCoordinates: { [key: string]: [number, number] } = {
london: [51.508, -0.126],
'new-york': [40.7128, -74.006],
};
// Function to capitalize city names
function capitalizeCityName(city: string): string {
return city.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
// Route to serve index.html for each city
app.get('/:city', async (req, res) => {
const city = req.params.city.toLowerCase();
if (!cityCoordinates[city]) {
console.error(`City not found: ${city}`);
res.status(404).send("City not found");
return;
}
const indexPath = path.join(basePath, 'index.html');
console.log(`Attempting to send file: ${indexPath}`);
res.sendFile(indexPath, err => {
if (err) {
console.error("Error sending file:", err);
res.status(500).send("Error serving content");
}
});
});
// Route to fetch data for each city
app.get('/api/places/:city', async (req, res) => {
const city = req.params.city.toLowerCase();
if (!cityCoordinates[city]) {
res.status(404).send("City not found");
return;
}
const airtableApiUrl = 'https://api.airtable.com/v0/[airtable base ID present in code]/Places';
const apiKey = process.env.AIRTABLE_API_KEY;
async function fetchDataWithPagination(offset: string = ''): Promise<any[]> {
try {
const response = await axios.get(airtableApiUrl, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
params: {
offset: offset,
filterByFormula: `{City} = '${capitalizeCityName(city)}'`
},
});
const records = response.data.records;
const newOffset = response.data.offset;
if (newOffset) {
return records.concat(await fetchDataWithPagination(newOffset));
} else {
return records;
}
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
try {
const allRecords = await fetchDataWithPagination();
res.json({
coordinates: cityCoordinates[city],
data: allRecords,
});
} catch (error) {
console.error("Error fetching data:", error);
res.status(500).send("Server error");
}
});
// Export the serverless function handler
export const handler = serverless(app);
and the following as my netlify.toml
:
[build]
command = "npm install && npm run build"
publish = "functions"
functions = "functions"
[functions]
external_node_modules = ["express"]
node_bundler = "esbuild"
[[redirects]]
force = true
from = "/*"
status = 200
to = "/.netlify/functions/expressApp/:splat"
When I deploy to netlify (although it doesn’t work in dev
either) I get the following error in my functions logs:
ERROR Error sending file: [Error: ENOENT: no such file or directory, stat '/var/task/public/index.html'] {
errno: -2,
code: 'ENOENT',
syscall: 'stat',
path: '/var/task/public/index.html',
expose: false,
statusCode: 404,
status: 404
}
My file structure is:
netlify.toml
functions/
- expressApp.ts
- public/
-- index.html
which appears to get converted on deploy to
expressApp.ts
- public/
-- index.html
Any idea what is going wrong referencing my static files?