- Backend: Ruby on Rails 8.0.2.1 (API-only) with PostgreSQL
- Frontend: React 18 with TypeScript, built using Vite
- Database: PostgreSQL
- Deployment: Railway
- Ruby 3.4.4
- Node.js 18+ and npm
- PostgreSQL 14+
cd backend
bundle install
rails db:create
rails db:migrate
rails db:seed
rails server -p 3001Backend will be available at: http://localhost:3001
cd frontend
npm install
npm run devFrontend will be available at: http://localhost:5173
DATABASE_URL=postgresql://username:password@localhost/restaurant_menu_development
RAILS_ENV=development
VITE_API_URL=http://localhost:3001/api/v1
cd backend
bundle exec rspeccd frontend
npm run testcurl "https://menu-backend-production-bf53.up.railway.app/api/v1/restaurants" | jq '.[0] | {name: .name, menus_count: (.menus | length)}'
curl "https://menu-backend-production-bf53.up.railway.app/api/v1/restaurants/1" | jq '{name: .name, menus: [.menus[] | {name: .name, items: [.menu_items[].name]}]}'
curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/restaurants" \
-H "Content-Type: application/json" \
-d '{"restaurant": {"name": "Test Production Restaurant"}}'
Admin endpoints require HTTP Basic Authentication. Default credentials for the DEMO:
- Username:
admin - Password:
password
Set custom credentials via environment variables:
ADMIN_USERNAME- Custom admin usernameADMIN_PASSWORD- Custom admin password
curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/auth/verify" \
-u admin:password \
-H "Content-Type: application/json"
Expected response:
{
"authenticated": true,
"message": "Authentication successful"
}curl "https://menu-backend-production-bf53.up.railway.app/api/v1/menus" | jq '.[0] | {name: .name, restaurant: .restaurant_id, items_count: (.menu_items | length)}'
curl "https://menu-backend-production-bf53.up.railway.app/api/v1/menus/1" | jq '{menu_name: .name, items: [.menu_items[] | {name: .name, price: .price}]}'
curl "https://menu-backend-production-bf53.up.railway.app/api/v1/menu_items" | jq '[.[] | {name: .name, price: .price, appears_on_menus: [.menus[].name]}]'
All import endpoints require admin authentication
curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/import/restaurants" \
-u admin:password \
-H "Content-Type: application/json" \
-d '{
"data": {
"restaurants": [
{
"name": "Production Test Restaurant",
"menus": [
{
"name": "test menu",
"menu_items": [
{"name": "Production Test Item", "price": 12.50}
]
}
]
}
]
}
}' | jq '{success: .success, summary: .summary, logs: .logs | length}'curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/import/restaurants" \
-u admin:password \
-H "Content-Type: application/json" \
-d '{"data": {"restaurants": [{"menus": []}]}}' | jq '{success: .success, errors: .errors}'curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/import/restaurants" \
-u admin:password \
-H "Content-Type: application/json" \
-d '{"data": "invalid json structure"}' | jq '{success: .success, error: .error}'curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/import/restaurants" \
-H "Content-Type: application/json" \
-d '{"data": {"restaurants": []}}'Expected response: HTTP Basic: Access denied.
echo '{
"restaurants": [{
"name": "File Upload Test Restaurant",
"menus": [{
"name": "upload test menu",
"menu_items": [{"name": "Upload Test Item", "price": 9.99}]
}]
}]
}' > test_restaurant.json
curl -X POST "https://menu-backend-production-bf53.up.railway.app/api/v1/import/restaurants" \
-u admin:password \
-F "file=@test_restaurant.json" | jq '{success: .success, summary: .summary}'- Create new Railway service, connect your repository
- Add required gems:
bundle add thruster rack-cors - Set environment variables in Railway dashboard:
RAILS_ENV=productionSECRET_KEY_BASE=<generate with: rails secret>FRONTEND_URL=<your-frontend-public-railway-url>
- Deploy automatically via git push
- Create separate Railway service for frontend
- Add
.nvmrcfile with content:20 - Set environment variables:
VITE_API_URL=<your-backend-public-railway-url>/api/v1
- Deploy automatically via git push
- Use public URLs for CORS configuration
- Node.js 20+ required for Vite 7
- Thruster gem required for Rails 8 deployment