Fullstack Project: Dari Nol sampai Production
Gabungkan semua teknologi dalam satu proyek fullstack end-to-end
Tujuan Pembelajaran
- Bisa membuat proyek fullstack lengkap
- Mengerti alur kerja fullstack developer
- Mampu deploy aplikasi fullstack
Analogi
Fullstack Flow:
Frontend (React+Vite+Tailwind+shadcn) → API (Bun+Hono) → Database (Drizzle+PostgreSQL) → Cache (Redis) → Deploy (Dokploy)
Semua teknologi yang dipelajari digabungkan dalam satu proyek nyata
Penjelasan Konsep
Ini adalah chapter penutup yang menggabungkan SEMUA teknologi yang sudah kita pelajari. Kita akan membuat aplikasi fullstack sederhana: Task Management App.
Fitur Aplikasi
- Autentikasi (login/register)
- CRUD tasks
- Real-time updates
- File attachments
- Responsive UI
Tech Stack
- Frontend: React + Vite + Tailwind CSS + shadcn/ui
- Backend: Bun + Hono + Drizzle ORM
- Database: PostgreSQL (via Supabase atau self-hosted)
- Cache: Redis
- Deployment: Dokploy
Arsitektur
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Frontend │ ──── │ Hono │ ──── │ Drizzle │
│ (Vercel) │ API │ (Bun) │ SQL │ (PostgreSQL)│
└─────────────┘ └──────┬──────┘ └─────────────┘
│
┌──────┴──────┐
│ Redis │
│ (Cache) │
└─────────────┘
Langkah Development
- Backend Setup
- Init Bun project
- Setup Drizzle + PostgreSQL
- Define schema (users, tasks,
Attachments)
- Create Hono routes
- Add auth middleware (JWT)
- Add cache layer (Redis)
- Test API
- Frontend Setup
- Init Vite + React + TypeScript
- Setup Tailwind + shadcn/ui
- Create pages and components
- Connect to API
- Add real-time (Supabase atau polling)
- Handle file uploads
- Integration
- Environment variables
- CORS config
- Error handling
- Loading states
- Deployment
- Deploy backend ke Dokploy
- Deploy frontend ke Vercel
- Setup environment variables
- Configure domain
- Test end-to-end
Best Practices
- Gunakan TypeScript types yang shared antara frontend dan backend
- Validasi input di frontend DAN backend
- Handle error gracefully
- Implement loading dan error states
- Gunakan React Query atau SWR untuk data fetching
- Add proper logging di backend
Selamat! Kamu sudah menguasai fullstack JavaScript modern.
Dari frontend interaktif sampai backend scalable dan deployment production. Selanjutnya: build something amazing!
Inti yang Perlu Dipahami
Bagian ini berfokus pada bisa membuat proyek fullstack lengkap., mengerti alur kerja fullstack developer., dan mampu deploy aplikasi fullstack. Jangan terburu-buru menghafal istilahnya. Lebih penting untuk memahami peran setiap konsep dan kapan konsep itu muncul dalam pekerjaan web development.
Saat membaca Fullstack Project, gunakan tujuan belajar sebagai penanda arah. Kalau kamu sudah bisa menjelaskan tujuan itu dengan kata-katamu sendiri, berarti fondasinya mulai terbentuk.
Cara Membayangkannya
Semua teknologi yang dipelajari digabungkan dalam satu proyek nyata. Analogi ini dipakai supaya konsep teknis tidak terasa melayang. Hubungkan setiap istilah dengan perannya: siapa yang meminta, siapa yang memproses, data apa yang berpindah, dan hasil apa yang diharapkan.
Kalau analoginya sudah terasa masuk akal, barulah lihat istilah teknisnya. Cara ini membuat materi lebih mudah dipahami daripada langsung menghafal definisi.
Saat Melihat Contoh Kode
Contoh kode pada chapter ini memakai bahasa typescript. Bacalah contoh kode sebagai ilustrasi alur, bukan sebagai bagian yang harus langsung dihafal. Shared types memastikan konsistensi antara frontend dan backend. Cache invalidation setelah mutasi data. API abstraction di frontend untuk clean code.
Perhatikan nama fungsi, urutan langkah, dan data yang berpindah. Biasanya tiga hal itu sudah cukup untuk memahami hubungan antara teori dan praktik.
Konteks dalam Perjalanan Belajar
Setiap konsep di platform ini dipilih karena dipakai di industri. Fokus pada pemahaman, bukan hafalan.
Kamu sudah di bagian lanjutan. Mulai pikirkan bagaimana konsep ini dipakai di dunia kerja — bukan hanya untuk belajar, tapi untuk membangun produk nyata.
Gunakan pertanyaan reflektif dan prompt AI di akhir chapter sebagai latihan aktif. Membaca saja tidak cukup — kamu perlu menjelaskan ulang dengan kata-katamu sendiri.
Contoh Kode
// Shared types (shared between frontend and backend)
// types.ts
export interface Task {
id: number;
title: string;
description: string | null;
status: 'todo' | 'in_progress' | 'done';
priority: 'low' | 'medium' | 'high';
userId: number;
createdAt: string;
updatedAt: string;
}
export interface CreateTaskInput {
title: string;
description?: string;
priority?: 'low' | 'medium' | 'high';
}
export interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
}
// Frontend: API client
// api.ts
const API_URL = import.meta.env.VITE_API_URL;
async function fetcher<T>(endpoint: string, options?: RequestInit): Promise<T> {
const res = await fetch(`${API_URL}${endpoint}`, {
headers: { 'Content-Type': 'application/json' },
...options,
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
export const api = {
tasks: {
list: () => fetcher<Task[]>('/api/tasks'),
create: (data: CreateTaskInput) => fetcher<Task>('/api/tasks', {
method: 'POST',
body: JSON.stringify(data),
}),
update: (id: number, data: Partial<Task>) => fetcher<Task>(`/api/tasks/${id}`, {
method: 'PATCH',
body: JSON.stringify(data),
}),
delete: (id: number) => fetcher<void>(`/api/tasks/${id}`, { method: 'DELETE' }),
},
};
// Backend: Route handler
// routes/tasks.ts
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const tasks = new Hono();
const createSchema = z.object({
title: z.string().min(1),
description: z.string().optional(),
priority: z.enum(['low', 'medium', 'high']).default('medium'),
});
tasks.get('/', async (c) => {
const userId = c.get('userId');
const cached = await redis.get(`tasks:${userId}`);
if (cached) return c.json(JSON.parse(cached));
const data = await db.select().from(tasksTable).where(eq(tasksTable.userId, userId));
await redis.setex(`tasks:${userId}`, 60, JSON.stringify(data));
return c.json(data);
});
tasks.post('/', zValidator('json', createSchema), async (c) => {
const userId = c.get('userId');
const input = c.req.valid('json');
const [task] = await db.insert(tasksTable).values({ ...input, userId }).returning();
await redis.del(`tasks:${userId}`); // Invalidate cache
return c.json(task, 201);
});
export default tasks;Penjelasan Kode
Prompt AI
Mulai proyek fullstack-mu! Pilih ide yang menarik dan implementasikan dengan stack yang sudah dipelajari.
Pertanyaan Reflektif
Dokumentasikan journey-mu. Blog tentang pengalaman building dan deploying aplikasi fullstack — ini akan menjadi portfolio yang kuat.