A cloud-native video-sharing platform built with Next.js, Firebase, Google Cloud Storage, Cloud Pub/Sub, and Cloud Run. Users can sign in with Google, upload videos, and watch them after automatic server-side transcoding to 360p.
The app is split into three independent services that communicate through Google Cloud infrastructure:
┌─────────────────┐ ┌───────────────────────┐ ┌────────────────────────┐
│ Next.js Client │──────▶│ Firebase Cloud Funcs │──────▶│ Firestore │
│ (yt-web-client)│ │ (yt-api-service) │ │ (users & videos meta) │
└────────┬────────┘ └───────────────────────┘ └────────────────────────┘
│ ▲
│ PUT (signed URL) │ status update
▼ │
┌─────────────────┐ Object Finalize ┌──────────┐ HTTP POST ┌┴───────────────────────┐
│ GCS Raw Bucket │───────────────────▶│ Pub/Sub │──────────────▶│ Video Processing Svc │
└─────────────────┘ └──────────┘ │ (Cloud Run + ffmpeg) │
└───────────┬────────────┘
│ upload
▼
┌─────────────────────┐
│ GCS Processed Bucket │
│ (public access) │
└─────────────────────┘
- Sign in — User authenticates via Google OAuth through Firebase Auth. A Cloud Function trigger automatically creates a user document in Firestore.
- Upload — The client requests a time-limited signed URL from a Cloud Function, then uploads the video file directly to a GCS raw bucket (the video never passes through the API).
- Process — GCS emits an object-finalize event → Pub/Sub → pushes an HTTP message to the video processing service on Cloud Run → ffmpeg transcodes the video to 360p → the result is uploaded to a processed bucket and made public.
- Watch — The client reads video metadata from Firestore and streams the processed video directly from the public GCS bucket using an HTML5
<video>element.
youtube-clone/
├── yt-web-client/ # Next.js 14 frontend
│ ├── app/
│ │ ├── firebase/ # Firebase SDK init, auth helpers, callable functions
│ │ ├── navbar/ # Navbar, Sign-In, and Upload components
│ │ ├── watch/ # Video watch page
│ │ ├── layout.tsx # Root layout with Navbar
│ │ └── page.tsx # Home page — lists uploaded videos
│ └── Dockerfile # Multi-stage build for Cloud Run
│
├── yt-api-service/ # Firebase Cloud Functions (API layer)
│ └── functions/src/index.ts # createUser, generateUploadUrl, getVideos
│
├── video-processing-service/ # Express + ffmpeg (runs on Cloud Run)
│ └── src/
│ ├── index.ts # POST /process-video endpoint
│ ├── storage.ts # GCS upload/download, ffmpeg transcoding
│ └── firestore.ts # Video metadata read/write
│
└── utils/
└── gcs-cors.json # CORS config for the raw video bucket
- Node.js v18+
- Docker
- Firebase CLI (
npm install -g firebase-tools) - Google Cloud SDK (gcloud)
- A Google Cloud project with billing enabled
# Login to Google Cloud and Firebase
gcloud auth login
firebase login
# Create a new Firebase project (or use an existing one)
firebase projects:create <your-project-id>
# Enable required APIs
gcloud services enable \
run.googleapis.com \
pubsub.googleapis.com \
storage.googleapis.com \
firestore.googleapis.com \
cloudbuild.googleapis.com \
--project=<your-project-id>Create two buckets — one for raw uploads and one for processed videos:
gsutil mb -l us-central1 gs://<your-raw-video-bucket>
gsutil mb -l us-central1 gs://<your-processed-video-bucket>Apply the CORS configuration so the browser can upload directly:
gsutil cors set utils/gcs-cors.json gs://<your-raw-video-bucket>Create a topic and a push subscription that points to your Cloud Run video processing service:
# Create the topic
gcloud pubsub topics create <your-topic-name>
# Set up a GCS notification on the raw bucket
gsutil notification create -t <your-topic-name> -f json gs://<your-raw-video-bucket>
# After deploying the processing service (step 5), create the push subscription:
gcloud pubsub subscriptions create <your-sub-name> \
--topic=<your-topic-name> \
--push-endpoint=<your-cloud-run-url>/process-video \
--ack-deadline=600cd yt-api-service
# Install dependencies
cd functions && npm install && cd ..
# Update bucket names in functions/src/index.ts:
# const rawVideoBucketName = "<your-raw-video-bucket>";
# Deploy
firebase deploy --only functionscd video-processing-service
# Update bucket names in src/storage.ts:
# const rawVideoBucketName = "<your-raw-video-bucket>";
# const processedVideoBucketName = "<your-processed-video-bucket>";
# Build and push the Docker image
gcloud builds submit --tag gcr.io/<your-project-id>/video-processing-service
# Deploy to Cloud Run
gcloud run deploy video-processing-service \
--image gcr.io/<your-project-id>/video-processing-service \
--platform managed \
--region us-central1 \
--memory 2Gi \
--timeout 900 \
--allow-unauthenticatedcd yt-web-client
# Install dependencies
npm install
# Create a .env file with your Firebase config
cat > .env << EOF
NEXT_PUBLIC_FIREBASE_KEY=<your-api-key>
NEXT_PUBLIC_AUTH_DOMAIN=<your-project>.firebaseapp.com
NEXT_PUBLIC_PROJECT_ID=<your-project-id>
NEXT_PUBLIC_STORAGE_BUCKET=<your-project>.appspot.com
NEXT_PUBLIC_MESSAGING_SENDER_ID=<your-sender-id>
NEXT_PUBLIC_FIREBASE_APP_ID=<your-app-id>
NEXT_PUBLIC_MEASUREMENT_ID=<your-measurement-id>
EOF
# Update the processed video bucket URL in app/watch/page.tsx:
# const videoPrefix = 'https://storage.googleapis.com/<your-processed-video-bucket>/';
# Run locally
npm run devOpen http://localhost:3000 to see the app.
To deploy the client to Cloud Run:
gcloud builds submit --tag gcr.io/<your-project-id>/yt-web-client
gcloud run deploy yt-web-client \
--image gcr.io/<your-project-id>/yt-web-client \
--platform managed \
--region us-central1| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Next.js 14, React 18, TypeScript | Server & client-side rendering, routing |
| Auth | Firebase Authentication | Google OAuth sign-in |
| API | Firebase Cloud Functions | Signed URL generation, video metadata queries |
| Message Queue | Google Cloud Pub/Sub | Decouples upload events from processing |
| Video Processing | Express, fluent-ffmpeg, Cloud Run | Transcodes videos to 360p inside Docker |
| Database | Cloud Firestore | Stores user profiles and video metadata |
| Storage | Google Cloud Storage | Raw & processed video file storage |
| Containerization | Docker (multi-stage builds) | Lean production images for Cloud Run |