Build scalable content pipelines with Node.js/TypeScript, Firebase, and ML recommendations. Supports user submissions, automated moderation via Cloud Vision API, external platform imports, and personalized feeds.
Build and maintain the UpStyles Explore API - a scalable content pipeline supporting user-generated submissions, automated content moderation, external platform imports, and ML-powered personalized recommendations.
Create a robust content pipeline for the UpStyles Explore feature supporting:
1. User-generated submissions with automated moderation
2. External content import (Instagram, TikTok, YouTube)
3. ML-powered personalized recommendations
**ALWAYS include `.js` extensions in local imports** (ES Modules requirement):
```typescript
// ✅ CORRECT
import { getFirestore } from './firebase.js';
import { SubmissionService } from '../services/submissions.js';
// ❌ WRONG
import { getFirestore } from './firebase';
```
```
explore-api/
├── src/
│ ├── index.ts # Express server + middleware
│ ├── routes/
│ │ ├── index.ts # Route aggregator
│ │ └── submissions.ts # Submission endpoints
│ ├── services/
│ │ ├── submissions.ts # Business logic
│ │ └── moderation.ts # Vision API integration
│ ├── middleware/
│ │ ├── auth.ts # Firebase Auth verification
│ │ └── rate-limit.ts # Rate limiting logic
│ └── lib/
│ ├── firebase.ts # Firebase Admin initialization
│ └── validation.ts # Zod schemas
```
Use Zod schemas from `lib/validation.ts` for all input validation:
```typescript
import { SubmissionSchema } from '../lib/validation.js';
router.post('/submissions', async (req, res) => {
const input = SubmissionSchema.parse(req.body); // Throws on invalid
// Process validated input
});
```
```typescript
import { authenticateUser, AuthenticatedRequest } from '../middleware/auth.js';
router.post('/endpoint', authenticateUser, async (req: AuthenticatedRequest, res) => {
const userId = req.user!.uid; // Guaranteed to exist after auth middleware
});
```
Apply appropriate rate limiters per endpoint type:
```typescript
import { rateLimitSubmissions, rateLimitAPI } from '../middleware/rate-limit.js';
// High-cost operations (submissions) - 10/day per user
router.post('/submissions', authenticateUser, rateLimitSubmissions, handler);
// General endpoints - 60/minute per IP
app.use(rateLimitAPI);
```
```typescript
try {
// Business logic
} catch (error: any) {
if (error.name === 'ZodError') {
return res.status(400).json({ error: 'Invalid input', details: error.errors });
}
console.error('[Service] Error:', error);
res.status(500).json({ error: 'Internal server error' });
}
```
1. Create route handler in `src/routes/`:
```typescript
import { Router } from 'express';
const router = Router();
export default router;
```
2. Define Zod schema in `src/lib/validation.ts`:
```typescript
export const MyInputSchema = z.object({ ... });
```
3. Implement service in `src/services/`:
```typescript
export class MyService { ... }
```
4. Register route in `src/routes/index.ts`:
```typescript
import myRouter from './my-route.js';
router.use('/my-path', myRouter);
```
```typescript
{
userId: string;
status: "pending" | "approved" | "rejected" | "flagged";
type: "design" | "technique" | "product" | "tutorial";
title: string;
description: string;
mediaUrls: string[];
tags: string[];
difficulty?: string;
priceRange?: string;
materials?: string[];
moderationFlags: {
spam: number; // 0-1 confidence
inappropriate: number;
aiGenerated: boolean;
};
submittedAt: Timestamp;
reviewedAt?: Timestamp;
reviewedBy?: string;
rejectionReason?: string;
approvedEntryId?: string;
}
```
```typescript
{
userId: string;
interests: string[];
engagementHistory: {
viewedEntries: string[];
savedEntries: string[];
sharedEntries: string[];
};
mlProfile?: {
embeddings: number[];
lastUpdated: Timestamp;
};
}
```
1. User submits content → `POST /api/explore/submissions`
2. Upload media to Cloud Storage (if needed)
3. Run `moderateSubmission()` from `services/moderation.ts`:
- Check rapid submission pattern (spam detection)
- Scan all images via Vision API
- Detect adult/violence/racy content
4. Determine status:
- `safe` → status: `"pending"`
- `flagged` → status: `"flagged"` (manual review required)
5. Store submission in Firestore
6. Return submission ID + status to client
```typescript
const query = db.collection('explore_submissions')
.orderBy('submittedAt', 'desc')
.limit(limit + 1); // Fetch one extra to check hasMore
if (cursor) {
const cursorDoc = await db.collection('explore_submissions').doc(cursor).get();
query = query.startAfter(cursorDoc);
}
const snapshot = await query.get();
const docs = snapshot.docs.slice(0, limit);
const hasMore = snapshot.docs.length > limit;
return {
items: docs.map(doc => doc.data()),
nextCursor: hasMore ? docs[docs.length - 1].id : undefined,
};
```
```typescript
import { requireRole } from '../middleware/auth.js';
// Only moderators can access
router.get('/moderation/queue', authenticateUser, requireRole('moderator'), handler);
```
For critical writes (e.g., approval):
```typescript
await db.runTransaction(async (transaction) => {
const submissionRef = db.collection('explore_submissions').doc(submissionId);
const entryRef = db.collection('explore_collections').doc(collectionId)
.collection('entries').doc();
transaction.update(submissionRef, { status: 'approved' });
transaction.set(entryRef, entryData);
});
```
```bash
npm install
./scripts/start_emulators.sh
npm run dev
curl http://localhost:8080/api/health
curl -X POST http://localhost:8080/api/explore/submissions \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"design","title":"Test","mediaUrls":["https://example.com/img.jpg"],"tags":["test"]}'
```
```bash
firebase apphosting:backends:create explore-api
git push origin main
```
```bash
gcloud run deploy explore-api \
--source . \
--region us-east4 \
--allow-unauthenticated
```
**"Cannot find module" errors**
→ Verify `.js` extensions in all local imports
**"Rate limit exceeded"**
→ Check `RATE_LIMIT_SUBMISSIONS_DAILY` in `.env`
**"Vision API quota exceeded"**
→ Increase quota or implement caching
**"Firebase Admin not initialized"**
→ Check `FIREBASE_PROJECT_ID` and credentials path
Track:
Set alerts for:
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/explore-api-developer/raw