Skip to content

Commit 2d6bd87

Browse files
Simplify deployment to single Express service (ATXP-258) (#15)
* Simplify deployment architecture to single Express service - **Single-service architecture**: Express server now serves both API endpoints and React frontend - **Simplified deployment**: Eliminates inter-service hostname issues across all platforms - **Enhanced static file serving**: Robust path resolution with fallback handling - **Updated documentation**: README reflects single-service production architecture - **Optimized configurations**: Vercel and Render configs now use single service deployment - **Streamlined npm scripts**: `npm start` builds and runs single production server Closes ATXP-258
1 parent da237b3 commit 2d6bd87

5 files changed

Lines changed: 59 additions & 49 deletions

File tree

README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ agent-demo/
3030

3131
## Features
3232

33-
- **Express Backend**: RESTful API with endpoints for text submission and retrieval
33+
- **Full-Stack Express**: Single Express server serves both API endpoints and React frontend
3434
- **React Frontend**: Modern, responsive UI with real-time updates
3535
- **Development Mode**: Hot reloading for both frontend and backend
36-
- **Production Ready**: Build system for deployment
37-
- **CORS Enabled**: Cross-origin requests supported
36+
- **Production Ready**: Single-service deployment with built-in static file serving
37+
- **CORS Enabled**: Cross-origin requests supported for development
3838
- **Error Handling**: Comprehensive error handling and user feedback
3939

4040
## API Endpoints
@@ -90,14 +90,15 @@ After deploying, you'll need to provide your ATXP connection string through the
9090

9191
### Production Mode
9292

93-
To run in production mode (with optimized builds and single browser opening):
93+
To run in production mode (single server serving both API and frontend):
9494
```bash
95-
npm run start
95+
npm run build
96+
npm start
9697
```
9798

98-
This runs the built backend and frontend in production mode without React StrictMode's double-mounting behavior.
99+
This builds both components and starts the Express server in production mode, serving both the API endpoints and the React frontend from a single service.
99100

100-
### Running Separately
101+
### Running Separately (Development Only)
101102

102103
- **Backend only**: `npm run server`
103104
- **Frontend only**: `npm run client`
@@ -114,6 +115,12 @@ This runs the built backend and frontend in production mode without React Strict
114115
npm start
115116
```
116117

118+
The server will start on the configured port (default: 3001) and serve both:
119+
- API endpoints at `/api/*`
120+
- React frontend at all other routes
121+
122+
**Note**: Always run `npm run build` before `npm start` to ensure you have the latest production builds.
123+
117124
## Environment Variables
118125

119126
### Backend Configuration
@@ -124,7 +131,7 @@ Create a `.env` file in the `backend/` directory:
124131
# Server port configuration
125132
PORT=3001
126133
127-
# Frontend port (for CORS configuration)
134+
# Frontend port (for CORS configuration in development)
128135
FRONTEND_PORT=3000
129136
130137
NODE_ENV=development
@@ -134,18 +141,20 @@ NODE_ENV=development
134141
#ATXP_CONNECTION_STRING=your_connection_string_here
135142
```
136143

137-
### Frontend Configuration
144+
### Frontend Configuration (Development Only)
138145

139-
Create a `.env` file in the `frontend/` directory:
146+
For development mode, create a `.env` file in the `frontend/` directory:
140147

141148
```env
142149
# Frontend development server port
143150
PORT=3000
144151
145-
# Backend server port (for API calls)
152+
# Backend server port (for API calls during development)
146153
REACT_APP_BACKEND_PORT=3001
147154
```
148155

156+
**Note**: In production mode, the frontend is served by the Express server, so frontend environment variables are not needed.
157+
149158
### Custom Port Configuration
150159

151160
By default, the application runs on:

backend/server.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
22
import cors from 'cors';
33
import bodyParser from 'body-parser';
44
import path from 'path';
5+
import fs from 'fs';
56
import dotenv from 'dotenv';
67
import { sendSSEUpdate, addSSEClient, removeSSEClient, sendStageUpdate, sendPaymentUpdate } from './stage';
78

@@ -390,12 +391,40 @@ app.get('/api/validate-connection', (req: Request, res: Response) => {
390391
}
391392
});
392393

394+
// Helper to resolve static path for frontend build
395+
function getStaticPath() {
396+
// Try ./frontend/build first (works when running from project root in development)
397+
let candidate = path.join(__dirname, './frontend/build');
398+
if (fs.existsSync(candidate)) {
399+
return candidate;
400+
}
401+
// Try ../frontend/build (works when running from backend/ directory)
402+
candidate = path.join(__dirname, '../frontend/build');
403+
if (fs.existsSync(candidate)) {
404+
return candidate;
405+
}
406+
// Try ../../frontend/build (works when running from backend/dist/ in production)
407+
candidate = path.join(__dirname, '../../frontend/build');
408+
if (fs.existsSync(candidate)) {
409+
return candidate;
410+
}
411+
// Fallback: throw error
412+
throw new Error('No frontend build directory found. Make sure to run "npm run build" first.');
413+
}
414+
393415
// Serve static files in production
394416
if (process.env.NODE_ENV === 'production') {
395-
app.use(express.static(path.join(__dirname, '../frontend/build')));
417+
// Add static file serving middleware
418+
app.use(express.static(getStaticPath()));
396419

420+
// Handle client-side routing by serving index.html for non-API routes
397421
app.get('*', (req: Request, res: Response) => {
398-
res.sendFile(path.join(__dirname, '../frontend/build', 'index.html'));
422+
// Only serve index.html for non-API routes
423+
if (!req.path.startsWith('/api/')) {
424+
res.sendFile(path.join(getStaticPath(), 'index.html'));
425+
} else {
426+
res.status(404).json({ error: 'API endpoint not found' });
427+
}
399428
});
400429
}
401430

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
"main": "index.js",
66
"scripts": {
77
"dev": "concurrently \"npm run server\" \"npm run client\"",
8-
"start": "concurrently \"npm run server:prod\" \"npm run client:prod\"",
8+
"start": "npm run server:prod",
99
"server": "cd backend && npm run dev",
1010
"server:prod": "cd backend && npm start",
1111
"client": "cd frontend && npm start",
12-
"client:prod": "cd frontend && NODE_ENV=production npm start",
12+
"client:prod": "echo 'In production mode, frontend is served by backend. Use npm start instead.'",
1313
"build": "npm run build:backend && npm run build:frontend",
1414
"build:backend": "cd backend && npm run build",
1515
"build:frontend": "cd frontend && npm run build",

render.yaml

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,13 @@
11
services:
22
- type: web
3-
name: atxp-express-backend
3+
name: atxp-express-app
44
runtime: node
55
plan: free
6-
buildCommand: cd backend && npm install && npm run build
6+
buildCommand: npm run install-all && npm run build
77
startCommand: cd backend && npm start
88
envVars:
99
- key: NODE_ENV
1010
value: production
1111
- key: PORT
1212
value: 10000
13-
- key: FRONTEND_PORT
14-
value: 3000
15-
healthCheckPath: /api/health
16-
17-
- type: web
18-
name: atxp-express-frontend
19-
runtime: static
20-
buildCommand: cd frontend && npm install && npm run build
21-
staticPublishPath: frontend/build
22-
routes:
23-
- type: rewrite
24-
source: /api/*
25-
# TODO: Replace 'atxp-express-backend' with your actual backend service name
26-
# The backend service URL will be: https://YOUR-BACKEND-SERVICE-NAME.onrender.com
27-
destination: https://atxp-express-backend.onrender.com/api/*
28-
- type: rewrite
29-
source: /*
30-
destination: /index.html
13+
healthCheckPath: /api/health

vercel.json

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,17 @@
55
"src": "backend/server.ts",
66
"use": "@vercel/node",
77
"config": {
8-
"includeFiles": ["backend/**"]
9-
}
10-
},
11-
{
12-
"src": "frontend/package.json",
13-
"use": "@vercel/static-build",
14-
"config": {
15-
"buildCommand": "npm run build",
16-
"outputDirectory": "build"
8+
"includeFiles": ["backend/**", "frontend/build/**"]
179
}
1810
}
1911
],
2012
"routes": [
21-
{
22-
"src": "/api/(.*)",
23-
"dest": "/backend/server.ts"
24-
},
2513
{
2614
"src": "/(.*)",
27-
"dest": "/frontend/$1"
15+
"dest": "/backend/server.ts"
2816
}
2917
],
18+
"installCommand": "npm run install-all && npm run build",
3019
"env": {
3120
"NODE_ENV": "production"
3221
},

0 commit comments

Comments
 (0)