Tech Stack Review: Zakiego-v4
Zakiego
@zakiego
Pendahuluan
Situs ini merupakan situs pribadi ke-4 (https://v4.zakiego.com) yang saya gunakan. Melalui tulisan ini, saya akan mengulas secara singkat teknologi apa saja yang digunakan untuk membuat website ini.
Pembahasan
Tailwind UI
Bagi pengguna Tailwind UI, tentu tidak asing ketika mengunjungi situs ini, karena memang, situs ini dibuat menggunakan template dari Tailwind UI. Nama templatenya adalah Spotlight (https://tailwindui.com/templates/spotlight). Sebab itu, untuk repository project ini akan dibuat tetap private.
Selingan, jika memang hasil karya orang lain, sebutkan siapa pembuat aslinya. Kredibilitas itu tentang harga diri.
Meski menggunakan template, banyak hal yang kemudian perlu saya kustomisasi, sehingga memakan waktu dua minggu.
Kenapa menggunakan template?
Alasan utamanya adalah karena tidak sempat, waktu sehari-hari sudah cukup terkuras untuk menyelesaikan kerjaan. Tentu, jika harus membuat dari nol, saya hanya bisa menggunakan waktu akhir pekan.
Sayangnya, waktu akhir pekan sudah saya khususkan untuk beristirahat dan mengeksplorasi hal-hal baru. Akhirnya, template adalah jalan keluarnya. Saya ingin fokus lebih fokus mengurus business logic, sedangkan dari sisi tampilan, serahkan pada Tailwind UI. 😉
Mungkin ada yang bertanya, apakah Tailwind UI worth it? Berapa dan bagaimana membelinya? Akan saya bahas pada lain waktu. Tolong diingatkan. 😜
Keystatic
Library ini hampir pasti masih terdengar asing, karena umurnya masih sangat muda. Saya memutuskan menggunakan Keystatic sebagai CMS karena:
- Malas mengurus database. Karena ini hanya project pribadi dan sederhana, lumayan terasa berat menanggung biaya database bulanan. Jika memakai database gratis, seperti Supabase, rasanya Supabase saya sudah berantakan, biarlah itu menjadi laboratorium eksperimen. 🤦🏻
- Malas menulis dalam mentahan markdown. Saya rasa terlalu ribet hanya untuk mengedit satu huruf atau menambah sebuah properti, harus mengedit markdown manual.
Berangkat dari 2 alasan tersebut, Keystatic mengatasinya dengan:
- Semua file disimpan di repository yang sama, tidak perlu database.
Dapat dilihat, satu konten bisa memuat dua file, yaitu json
untuk property, dan mdoc
untuk isi dari konten (--biasanya dalam bentuk md
, tapi entah kenapa Keystatic menggunakan mdoc
).
// index.json
{
"title": "Kilas Balik 2021",
"draft": false,
"image": "/images/articles/2021/image.webp",
"category": "contemplation",
"date": "2021-12-30",
"summary": "Satu kata yang paling menggambarkan tahun ini dan kata yang paling terngiang-ngiang di kepala saya. Dalam tulisan ini, saya akan mencoba merangkum apa-apa saja yang saya pelajari sepanjang tahun ini."
}
# content.mdoc
**Bertumbuh.**
Satu kata yang paling menggambarkan tahun ini dan kata yang paling terngiang-ngiang di kepala saya. Dalam tulisan ini, saya akan mencoba merangkum apa-apa saja yang saya pelajari sepanjang tahun ini. Penyusunan bukan berdasarkan urutan waktu, tapi dari dimulai dari yang menurut saya paling penting, kemudian setelahnya.
- Seperti yang disinggung sebelumnya, sejatinya sama saja, kita akan menulis dalam bentuk markdown, hanya saja, proses menulis ini dibantu editor, bukan dalam bentuk mentahan markdown.
Selain dua hal di atas, kita juga bisa mengedit konten di production setelah dideploy. Nantinya, hasil editan tersebut akan dipush ke repository GitHub kita (– proses ini dihandle oleh Keystatic). Tentu, perlu waktu waktu untuk melakukan proses build ulang.
Tak ada gading yang tak retak, Keystatic juga memiliki kekurangan. Sejauh ini, semua konten yang dihandle oleh Keystatic hanya bisa di-get saat proses build. Artinya, web akan statis sepenuhnya. Atau jika menggunakan Next.js, hanya bisa menggunakan getStaticProps
, tanpa revalidate, dan tentunya tidak bisa getServerSideProps
.
Tapi bagi saya, trade off tersebut bukanlah masalah, karena blog tidak harus secepat itu untuk update. Waktu untuk proses build hanya memakan waktu sekitar satu menitan.
tRPC dan Zod
Bisa dikatakan, penggunaan tRPC di web ini sebenarnya adalah over-engineering. But, that's fine, karena memang project pribadi fungsinya untuk eksplorasi banyak hal. 😋
Tanpa tRPC, function untuk membaca data dari Keystatic bisa dibuat sederhana, hanya memerlukan satu function kunci:
const keystaticReader = createReader(process.cwd(), keystaticConfig);
Fungsi keystaticReader
tersebut kemudian bisa digunakan langsung.
Tapi, dengan mempertimbangkan banyak re-strukturisasi data, saya memilih menggunakan tRPC agar semua pemrosesan tersebut dilakukan di satu tempat. Contohnya adalah dua router di bawah ini.
const keystaticRouter = createTRPCRouter({
profile: publicProcedure.query(async () => {
const data = await keystaticReader.singletons.profile.read();
const parsed = keystaticValidation.profile.parse(data);
return parsed;
}),
highlightArticles: publicProcedure.query(async () => {
const { articles } =
await keystaticReader.singletons.highlightArticles.readOrThrow();
const getArticle = articles.map(async (slug) => {
const data = await keystaticReader.collections.articles.readOrThrow(
slug as string,
);
const content = await data.content();
const render = {
...data,
slug,
content,
};
const parsed = keystaticValidation.articles.parse(render);
return parsed;
});
}),
});
Route dari keystaticRouter.profile
masih sederhana, karena hanya memuat proses pembacaan dan validasi menggunakan Zod. Omong-omong, berikut merupakan schema validasi menggunakan Zod pada keystaticValidation.profile
:
profile: z.object({
avatar: z.string(),
name: z.string(),
headline: z.string(),
biography: z.string(),
images: z.array(
z.object({
title: z.string(),
image: z.string(),
}),
),
}),
Lanjut, keystaticRouter.highlightArticles
lebih rumit, karena terdapat dua fungsi, yaitu mendapatkan list dari artikel yang di-highlight dan mendapatkan artikel itu sendiri. Setelah artikel didapatkan, ada proses mengekstrak konten, re-strukturisasi data, dan terakhir melakukan validasi.
Sebenarnya, Keystatic sendiri sudah memiliki fitur validasi, namun saya merasa validasi dari Keystatic kurang strict, akhirnya diputuskan untuk melakukan validasi ulang dengan Zod. Validasi ini dilakukan agar ketika terdapat data yang kurang, misalkan tanggal yang belum diisi, saya bisa langsung mendapatkan peringatan error.
Schema validasi tersebut ditempatkan di satu file sebagai single source of truth.
Penutup
Sebagai penutup, kiranya izinkan saya mengutip tweet dari Mas @azamuddin91 ini 🤣.