Final Project: Full-Stack Deployment Pipeline
Duration: 60 minutes Difficulty: 🔴 Advanced
Project Overview
Build and deploy a complete full-stack application with:
- React frontend on Vercel
- Node.js API on Railway with PostgreSQL
- MkDocs documentation on GitHub Pages
- Storybook component library on Chromatic
- Full CI/CD pipeline with GitHub Actions
Project Specification
Application: AI-Powered Task Manager
Features:
- User authentication (JWT)
- Create, read, update, delete tasks
- AI-powered task suggestions (OpenAI API)
- Real-time updates (WebSockets)
- Component library documentation
- API documentation
Phase 1: Backend API (20 minutes)
Database Setup
# In Railway: Add PostgreSQL database
# Note the DATABASE_URL from environment variables
API Implementation
// server.js
import express from 'express';
import pg from 'pg';
import cors from 'cors';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import OpenAI from 'openai';
const app = express();
const PORT = process.env.PORT || 3000;
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
app.use(cors({ origin: process.env.FRONTEND_URL }));
app.use(express.json());
// Initialize database
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS tasks (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
title VARCHAR(255) NOT NULL,
description TEXT,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
`);
// Authentication middleware
function authenticateToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'Token required' });
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'Invalid token' });
req.user = user;
next();
});
}
// Routes
app.post('/api/auth/register', async (req, res) => {
const { email, password } = req.body;
const hash = await bcrypt.hash(password, 10);
const result = await pool.query(
'INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING id',
[email, hash]
);
const token = jwt.sign({ id: result.rows[0].id }, process.env.JWT_SECRET);
res.json({ token });
});
app.get('/api/tasks', authenticateToken, async (req, res) => {
const result = await pool.query(
'SELECT * FROM tasks WHERE user_id = $1',
[req.user.id]
);
res.json(result.rows);
});
app.post('/api/tasks/suggest', authenticateToken, async (req, res) => {
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: 'Suggest 3 actionable tasks based on the user context.' },
{ role: 'user', content: req.body.context }
]
});
res.json({ suggestions: completion.choices[0].message.content });
});
app.listen(PORT, () => console.log(`API running on port ${PORT}`));
Deploy to Railway
git add .
git commit -m "Complete API implementation"
git push origin main
Set environment variables in Railway:
DATABASE_URL(auto-set)JWT_SECRETOPENAI_API_KEYFRONTEND_URL
Phase 2: Frontend Application (20 minutes)
React Setup with Vite
npm create vite@latest task-manager-ui -- --template react
cd task-manager-ui
npm install axios react-router-dom zustand
State Management
// src/store/useTaskStore.js
import { create } from 'zustand';
import axios from 'axios';
const API_URL = import.meta.env.VITE_API_URL;
export const useTaskStore = create((set, get) => ({
tasks: [],
loading: false,
fetchTasks: async () => {
set({ loading: true });
const token = localStorage.getItem('token');
const { data } = await axios.get(`${API_URL}/api/tasks`, {
headers: { Authorization: `Bearer ${token}` }
});
set({ tasks: data, loading: false });
},
addTask: async (task) => {
const token = localStorage.getItem('token');
const { data } = await axios.post(`${API_URL}/api/tasks`, task, {
headers: { Authorization: `Bearer ${token}` }
});
set({ tasks: [...get().tasks, data] });
},
getSuggestions: async (context) => {
const token = localStorage.getItem('token');
const { data } = await axios.post(
`${API_URL}/api/tasks/suggest`,
{ context },
{ headers: { Authorization: `Bearer ${token}` }}
);
return data.suggestions;
}
}));
Deploy to Vercel
# Create .env.production
echo "VITE_API_URL=https://your-api.railway.app" > .env.production
git add .
git commit -m "Complete frontend"
git push
# Deploy
vercel --prod
Phase 3: Documentation (10 minutes)
API Documentation
Create docs/api/endpoints.md:
# API Endpoints
## Authentication
### POST /api/auth/register
Register a new user.
**Request:**
```json
{
"email": "user@example.com",
"password": "securepassword"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Tasks
GET /api/tasks
List all tasks for authenticated user.
Headers:
- Authorization: Bearer {token}
Response:
[
{
"id": 1,
"title": "Complete deployment",
"completed": false,
"created_at": "2025-01-15T10:00:00Z"
}
]
### Deploy Documentation
```bash
mkdocs gh-deploy
Phase 4: CI/CD Pipeline (10 minutes)
Create .github/workflows/full-stack-deploy.yml:
name: Full Stack Deployment
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
deploy-api:
needs: test-api
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Railway
run: |
npm i -g @railway/cli
railway up --service=api
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
deploy-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: ${{ github.ref == 'refs/heads/main' && '--prod' || '' }}
deploy-docs:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
Success Criteria
Your project is complete when:
- API deployed to Railway with PostgreSQL
- Frontend deployed to Vercel
- Documentation live on GitHub Pages
- CI/CD pipeline running successfully
- Health check endpoints responding
- Authentication working
- Task CRUD operations functional
- AI suggestions working
- Preview deployments for PRs
- Custom domain configured (optional)
Bonus Enhancements
- Monitoring: Add Sentry error tracking
- Analytics: Integrate Vercel Analytics
- Performance: Add Redis caching layer
- SEO: Implement meta tags and Open Graph — covered in depth in Phase 10: SEO & AI Discoverability
- PWA: Make frontend installable
Next: 05_assessment.md - Knowledge Check