Skip to content

Commit cadf510

Browse files
authored
Refactor AddNodeApp (#12538)
* Refactor AddNodeApp - Obsolete the existing AddNodeApp that takes a file path only - Introduce a new AddNodeApp that takes a directory path and a file path relative to that directory - Includes a default Dockerfile for the Node application - Add Npm package manager support if the directory contains a package.json file * Minor PR feedback * Optimize the node dockerfile Use multi-stage build. Set NODE_ENV. Expose the port. Use a non-root user. * Use alpine for the base image of NodeApps for size optimization.
1 parent 93c7f53 commit cadf510

File tree

12 files changed

+1846
-23
lines changed

12 files changed

+1846
-23
lines changed

playground/AspireWithJavaScript/AspireJavaScript.AppHost/AppHost.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
var weatherApi = builder.AddProject<Projects.AspireJavaScript_MinimalApi>("weatherapi")
44
.WithExternalHttpEndpoints();
55

6-
builder.AddJavaScriptApp("angular", "../AspireJavaScript.Angular")
6+
builder.AddJavaScriptApp("angular", "../AspireJavaScript.Angular", runScriptName: "start")
77
.WithReference(weatherApi)
88
.WaitFor(weatherApi)
99
.WithHttpEndpoint(env: "PORT")
1010
.WithExternalHttpEndpoints()
1111
.PublishAsDockerFile();
1212

13-
builder.AddJavaScriptApp("react", "../AspireJavaScript.React")
13+
builder.AddJavaScriptApp("react", "../AspireJavaScript.React", runScriptName: "start")
1414
.WithReference(weatherApi)
1515
.WaitFor(weatherApi)
1616
.WithEnvironment("BROWSER", "none") // Disable opening browser on npm start
@@ -19,6 +19,7 @@
1919
.PublishAsDockerFile();
2020

2121
builder.AddJavaScriptApp("vue", "../AspireJavaScript.Vue")
22+
.WithRunScript("start")
2223
.WithNpm(installCommand: "ci") // Use 'npm ci' for clean install, requires lock file
2324
.WithReference(weatherApi)
2425
.WaitFor(weatherApi)
@@ -31,6 +32,12 @@
3132
.WithEnvironment("BROWSER", "none")
3233
.WithExternalHttpEndpoints();
3334

35+
builder.AddNodeApp("node", "../AspireJavaScript.NodeApp", "app.js")
36+
.WithRunScript("dev") // Use 'npm run dev' for development
37+
.WithHttpEndpoint(env: "PORT")
38+
.WithExternalHttpEndpoints()
39+
.PublishAsDockerFile();
40+
3441
weatherApi.PublishWithContainerFiles(reactvite, "./wwwroot");
3542

3643
builder.Build().Run();
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const express = require('express');
2+
const cors = require('cors');
3+
const path = require('path');
4+
5+
const app = express();
6+
const PORT = process.env.PORT || 3000;
7+
8+
// Middleware
9+
app.use(cors());
10+
app.use(express.json());
11+
app.use(express.urlencoded({ extended: true }));
12+
13+
// Root route for browsers (serves HTML)
14+
app.get('/', (req, res) => {
15+
res.sendFile(path.join(__dirname, 'public', 'index.html'));
16+
});
17+
18+
app.get('/api/welcome', (req, res) => {
19+
res.json({
20+
message: 'Welcome to AspireJavaScript Node.js App!',
21+
timestamp: new Date().toISOString(),
22+
version: '1.0.0'
23+
});
24+
});
25+
26+
app.get('/api/health', (req, res) => {
27+
res.json({
28+
status: 'healthy',
29+
timestamp: new Date().toISOString()
30+
});
31+
});
32+
33+
app.get('/api/weather', (req, res) => {
34+
// Mock weather data similar to other Aspire apps
35+
const weatherData = [
36+
{
37+
date: new Date().toISOString().split('T')[0],
38+
temperatureC: Math.floor(Math.random() * 35) - 5,
39+
summary: 'Sunny'
40+
},
41+
{
42+
date: new Date(Date.now() + 86400000).toISOString().split('T')[0],
43+
temperatureC: Math.floor(Math.random() * 35) - 5,
44+
summary: 'Cloudy'
45+
},
46+
{
47+
date: new Date(Date.now() + 172800000).toISOString().split('T')[0],
48+
temperatureC: Math.floor(Math.random() * 35) - 5,
49+
summary: 'Rainy'
50+
}
51+
].map(item => ({
52+
...item,
53+
temperatureF: Math.round((item.temperatureC * 9/5) + 32)
54+
}));
55+
56+
res.json(weatherData);
57+
});
58+
59+
// Serve remaining static files from public directory (after API routes)
60+
app.use(express.static(path.join(__dirname, 'public')));
61+
62+
// Error handling middleware
63+
app.use((err, req, res, next) => {
64+
console.error(err.stack);
65+
res.status(500).json({
66+
message: 'Something went wrong!',
67+
error: process.env.NODE_ENV === 'production' ? {} : err.stack
68+
});
69+
});
70+
71+
// General 404 handler (for non-API routes)
72+
app.use('*', (req, res) => {
73+
res.status(404).json({
74+
message: 'Route not found',
75+
path: req.originalUrl
76+
});
77+
});
78+
79+
// Start server
80+
app.listen(PORT, () => {
81+
console.log(`Server is running on port ${PORT}`);
82+
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
83+
console.log(`Visit: http://localhost:${PORT}`);
84+
});
85+
86+
module.exports = app;

0 commit comments

Comments
 (0)