← Semua pembelajaran / Astro Nol → Portal Berita
Fase 0 · Fondasi: Node, TypeScript & Cara Pikir Astro

Struktur proyek & konvensi folder

Hanya dua folder di dalam src/ yang punya arti khusus bagi Astro. Sisanya konvensi — tapi konvensi yang layak diikuti kalau kamu ingin kodenya masih terbaca dua tahun lagi.

Sumber asli docs.astro.build Resmi Rangkuman ~7 menit baca

Intisari

  • src/pages/ adalah satu-satunya folder yang menghasilkan rute. Satu berkas = satu URL.
  • public/ disalin apa adanya tanpa diproses; src/assets/ dioptimasi dan diberi nama ber-hash.
  • Menaruh gambar artikel di public/ berarti membuang seluruh optimasi gambar Astro — kesalahan paling mahal di portal berita.
  • src/components/, src/layouts/, src/lib/ murni konvensi. Astro tidak peduli namanya.
  • Alias @/* di tsconfig.json menghapus ../../../ yang bikin refactor menyakitkan.

Struktur yang dipakai sepanjang roadmap ini

src/
├── pages/              ← MENGHASILKAN RUTE. satu berkas = satu URL
│   ├── index.astro                    →  /
│   ├── berita/
│   │   ├── index.astro                →  /berita
│   │   └── [slug].astro               →  /berita/apa-saja
│   ├── perusahaan/
│   │   └── [kode].astro               →  /perusahaan/BBCA
│   └── api/
│       └── midtrans-webhook.ts        →  /api/midtrans-webhook
├── layouts/            ← kerangka halaman (konvensi)
├── components/         ← komponen .astro dan .vue (konvensi)
├── lib/                ← kode non-UI: db, cache, auth (konvensi)
│   ├── db.ts
│   ├── artikel.ts
│   └── midtrans.ts
├── assets/             ← DIPROSES: gambar, font
├── styles/             ← CSS global (konvensi)
└── middleware.ts       ← ISTIMEWA: dijalankan di tiap request

public/                 ← DISALIN APA ADANYA, tidak diproses
├── robots.txt
└── favicon.ico

Yang punya arti khusus bagi Astro cuma tiga: src/pages/, src/middleware.ts, dan src/content.config.ts (Fase 3). Sisanya nama bebas — kamu bisa menaruh komponen di src/potongan/ dan Astro tidak akan protes. Tapi setiap orang yang membaca kodemu nanti mengharapkan nama yang standar, jadi pakai yang standar.

Jebakan nomor satu: public/ vs src/assets/

public/src/assets/
Diproses saat buildTidakYa
Nama berkasPersis seperti aslinyaDiberi hash isi
Gambar dioptimasi & diubah formatTidakYa — WebP/AVIF, beberapa ukuran
Boleh di-cache selamanyaTidak (nama bisa dipakai ulang untuk isi berbeda)Ya (isi berubah → nama berubah)
Referensi di kode/logo.png — string biasaimport logo from "@/assets/logo.png"
Salah ketik ketahuan saatRuntime, sebagai 404Build, sebagai error
---
import { Image } from "astro:assets";
import sampul from "@/assets/sampul-default.jpg";
---
<!-- Dioptimasi: WebP, ukuran tepat, lebar/tinggi terisi otomatis -->
<Image src={sampul} alt="Sampul" width={800} />

<!-- Dari public/: apa adanya, tidak dioptimasi, tidak diperiksa -->
<img src="/sampul-default.jpg" alt="Sampul" />

Aturannya sederhana: kalau gambarnya kamu import, taruh di src/assets/. Kalau URL-nya harus tetap dan bisa ditebak orang lain, taruh di public/. Isi public/ di portal berita seharusnya hanya robots.txt, favicon.ico, berkas verifikasi domain, dan ads.txt — tidak lebih.

Gambar artikel dari database bukan salah satu dari keduanya. Ia tidak ada saat build, jadi tidak bisa di-import. Ia butuh jalur ketiga: <Image> dengan URL remote plus image.domains di konfigurasi, atau CDN gambar terpisah. Ini dibahas di Fase 8 — dan untuk portal berita, ini keputusan yang berdampak besar ke biaya.

Alias import — pasang sekarang, bukan nanti

{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
// Sebelum — rapuh, dan berubah tiap berkas dipindah
import { db } from "../../../lib/db";

// Sesudah — sama di mana pun berkasnya berada
import { db } from "@/lib/db";

Ini terlihat kosmetik sampai kamu memindahkan satu folder dan harus memperbaiki empat puluh import. Astro dan Vite membaca paths dari tsconfig.json langsung — tidak perlu konfigurasi tambahan di astro.config.mjs.

Batas yang layak ditegakkan sejak awal

Satu aturan yang akan menyelamatkanmu di Fase 5 dan 7: src/pages/ tidak boleh berisi kueri database atau logika bisnis. Halaman memanggil fungsi dari src/lib/, dan fungsi itu yang tahu soal SQL.

---
// BURUK: kueri langsung di halaman
const artikel = await db.selectFrom("artikel")
  .where("slug", "=", Astro.params.slug!)
  .selectAll()
  .executeTakeFirst();
---
---
// BAIK: halaman tidak tahu apa-apa soal SQL
import { ambilArtikelPublik } from "@/lib/artikel";

const artikel = await ambilArtikelPublik(Astro.params.slug!);
---

Alasannya bukan kerapian. Kueri yang tersebar di halaman tidak bisa diuji, tidak bisa di-cache di satu tempat, dan — yang paling mahal — tidak bisa dipastikan selalu menyaring status = 'terbit'. Satu halaman yang lupa menambahkan filter itu cukup untuk menerbitkan draf ke publik. Nama fungsinya sengaja ambilArtikelPublik, bukan ambilArtikel: batasannya ada di nama.

astro.config.mjs — yang perlu kamu kenali sekarang

import { defineConfig } from "astro/config";
import vue from "@astrojs/vue";
import node from "@astrojs/node";

export default defineConfig({
  site: "https://portal.contoh.id",   // wajib untuk sitemap & URL kanonik
  output: "static",                    // diubah di Fase 1
  adapter: node({ mode: "standalone" }),
  integrations: [vue()],
});

site sering dilewatkan dan itu kesalahan. Tanpanya, Astro.site bernilai undefined, sitemap tidak bisa dibuat, dan URL kanonik yang kamu tulis untuk SEO jadi relatif — persis hal yang paling tidak boleh salah saat memigrasikan portal berita yang sudah terindeks.

Latihan: susun kerangka folder di atas di proyekmu, pasang alias @/*, lalu buat src/lib/artikel.ts berisi satu fungsi ambilArtikelPublik(slug) yang untuk sementara mengembalikan data palsu. Panggil dari src/pages/berita/[slug].astro memakai alias. Terakhir: taruh satu gambar di public/ dan satu lagi di src/assets/, render keduanya, jalankan pnpm build, lalu bandingkan nama berkas dan ukurannya di dist/.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.