Dalam era digital saat ini, membuat blog telah menjadi semakin mudah dengan adanya banyak platform yang tersedia. Salah satu platform yang bisa digunakan untuk membuat blog adalah PayloadCMS. PayloadCMS adalah sebuah CMS (Content Management System) open-source yang menyediakan berbagai fitur untuk mengelola konten, seperti blog posts, halaman, dan gambar. Saya sudah membahas tentang PayloadCMS di postingan sebelumnya. Dalam artikel ini, saya akan menjelaskan bagaimana cara membuat blog dengan PayloadCMS dan menjadwalkan postingan dengan Agenda.js.
Instalasi PayloadCMS
Langkah pertama untuk membuat blog dengan PayloadCMS adalah melakukan instalasi PayloadCMS terlebih dahulu. PayloadCMS dapat diunduh secara gratis melalui npmjs.com yang tercantum di situs resmi PayloadCMS. Setelah diunduh, langkah selanjutnya adalah melakukan instalasi PayloadCMS di server web Kalian. PayloadCMS memiliki dokumentasi yang lengkap dan mudah diikuti, sehingga Kalian tidak akan kesulitan dalam melakukan instalasi. Untuk memulai kita bisa menjalankannya di lokal komputer kita namun kalian harus menginstall Node.js, mongodb, dan Yarn atau NPM dulu. Kemudian jalankan perintah ini di Terminal atau command Prompt:
npx create-payload-app
Ikuti pertanyaan yang muncul di terminal. Contoh di lokal komputer saya seperti ini:
Pertanyaan-pertanyaan yang tampil antara lain (dalam bahasa indonesia):
- Apakah kita bersedia mengunduh create-payload-app. Ketik: y
- Pilih Nama proyek. Ketik: sesuai keinginan Kalian saja 😊. Saya menamakan
payloadcms-lanjutkoding
- Pilih proyek template. ketik: pilih blog
- Masukkan koneksi MongoDB. ketik: alamat URI dari lokal atau remote MongoDB Kalian.
- Tunggu beberapa saat dan 🎉 Kita berhasil membuat blog dengan PayloadCMS.
Kemudian masuk ke direktori yang barus aja dibuat sekaligus menjalankannya.
cd payloadcms-lanjutkoding // sesuaikan dengan nama yang kalian buat npm run dev
Beginilah struktur proyek dari blog template PayloadCMS:
Bisa Kita lihat tempat nanti kita kerja ada di direktori src/
dan perlu dicatat bahwa PayloadCMS itu adalah Universal atau Isomorphic Platform maksudnya kode kita berjalan di server (Node.js) maupun di client (browser). Jadi hati-hati kalau menulis kode yang tidak bisa berjalan di browser, begitupun sebaliknya. Namun, ada 1 file yang khusus berjalan di server yaitu server.ts
. Berikut struktur awal dari file tersebut:
// server.ts import express from 'express'; import payload from 'payload'; require('dotenv').config(); const app = express(); // Redirect root to Admin panel app.get('/', (_, res) => { res.redirect('/admin'); }); const start = async () => { // Initialize Payload await payload.init({ secret: process.env.PAYLOAD_SECRET, mongoURL: process.env.MONGODB_URI, express: app, onInit: async () => { payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`) }, }) // Add your own express routes here app.listen(3000); } start();
Membuat Blog Post di PayloadCMS
Setelah PayloadCMS terinstal dengan baik, langkah selanjutnya adalah membuat blog post di PayloadCMS. Kalian harus membuat beberapa blog posts terlebih dahulu dan disimpan sebagai draft. PayloadCMS menyediakan editor yang mudah digunakan untuk membuat blog posts. Kalian bisa menambahkan gambar, video, dan teks pada blog posts Kalian. Selain itu, Kalian juga bisa menambahkan tag dan kategori untuk setiap blog post Kalian. Berikut tampilan form blog post bawaan dari PayloadCMS:
Hasil dari struktur awal post collection adalah seperti ini:
// Posts.ts - post collection import { CollectionConfig } from 'payload/types'; const Posts: CollectionConfig = { slug: 'posts', admin: { defaultColumns: ['title', 'author', 'category', 'tags', 'status'], useAsTitle: 'title', }, access: { read: () => true, }, fields: [ { name: 'title', type: 'text', }, { name: 'author', type: 'relationship', relationTo: 'users', }, { name: 'publishedDate', type: 'date', }, { name: 'category', type: 'relationship', relationTo: 'categories' }, { name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true, }, { name: 'content', type: 'richText' }, { name: 'status', type: 'select', options: [ { value: 'draft', label: 'Draft', }, { value: 'published', label: 'Published', }, ], defaultValue: 'draft', admin: { position: 'sidebar', } } ], } export default Posts;
Mengatur Jadwal Posting dengan Agenda.js
Setelah membuat blog posts, langkah selanjutnya adalah mengatur jadwal posting blog posts. PayloadCMS tidak memiliki fitur bawaan untuk mengatur jadwal posting, namun Kalian bisa menggunakan library JavaScript seperti Agenda.js untuk melakukan itu.
Agenda.js
Agenda.js adalah sebuah library JavaScript yang digunakan untuk mengatur jadwal pekerjaan (job scheduling) di Node.js. Dengan menggunakan Agenda.js, Kalian bisa membuat sebuah pekerjaan (job) untuk memposting blog posts pada waktu tertentu. Berikut adalah langkah-langkah yang perlu Kalian lakukan untuk mengatur jadwal posting blog posts dengan Agenda.js:
- Instalasi Agenda.js
Langkah pertama adalah melakukan instalasi Agenda.js. Kalian bisa melakukan instalasi Agenda.js menggunakan npm dengan mengetikkan perintah berikut di terminal:
npm install agenda
- Konfigurasi Agenda.js
Langkah selanjutnya adalah melakukan konfigurasi Agenda.js. Kalian perlu mengatur database untuk Agenda.js, misalnya MongoDB atau Redis. Kalian juga perlu mengatur job processor yang akan digunakan untuk mengeksekusi pekerjaan yang telah dijadwalkan. Berikut adalah contoh konfigurasi Agenda.js:
- Definisikan job processornya. buat direktori dan file baru didalam
src/
yaitujobs/definitions.ts
// jobs/definitions.ts import type Agenda from 'agenda'; import { type Payload } from 'payload'; const publishPostDefinition = (agenda: Agenda, payload: Payload) => { agenda.define('publish-post', {shouldSaveResult: true}, async (job) => { payload.logger.info(job.attrs, 'agenda.define publish-post job'); // TODO: extract data & update post status from `draft` to `published` }) } const allDefinitions = [publishPostDefinition]; export function allJobs(agenda: Agenda, payload: Payload) { allDefinitions.forEach(function(definition) { definition(agenda, payload); }) }
Pada contoh kode di atas, kita membuat sebuah pekerjaan dengan nama 'publish-post'
yang akan mengeksekusi sebuah fungsi untuk menerbitkan blog posts. Fungsi ini akan menerima sebuah parameter dengan nama job
yang akan diekstrak dan digunakan untuk memposting sebuah blog post nantinya.
Init konfigurasi Agenda.js di server.ts
// server.ts import Agenda from 'agenda'; import express from 'express'; import payload from 'payload'; import { allJobs } from './jobs/definitions'; require('dotenv').config(); const app = express(); // Redirect root to Admin panel app.get('/', (_, res) => { res.redirect('/admin'); }); const start = async () => { // Initialize Payload await payload.init({ secret: process.env.PAYLOAD_SECRET, mongoURL: process.env.MONGODB_URI, express: app, onInit: async (p) => { p.logger.info(`Payload Admin URL: ${p.getAdminURL()}`) const agenda = new Agenda({ db: { address: p.mongoURL as string, collection: 'agenda_jobs', }, maxConcurrency: 8, }) agenda.on('ready', async () => { allJobs(agenda, p) await agenda.start(); p.logger.info('Agenda has been Started') }) .on('error', (err) => { p.logger.info(err, "Agenda connection error") }) app.set('agenda', agenda) }, }) // Add your own express routes here app.listen(3000); } start();
Pada server.ts
, Kita menginisiasi Agenda.js dengan MongoDB URI yang sama dengan PayloadCMS didalam fungsi onInit
dari PayloadCMS, dan memanggil job processor yang sudah kita buat ketika agenda sudah ready
sekaligus start
Agenda.js. Kemudian memasukkannya ke app
setter dan getter dari express.js
untuk memastikan Agenda.js hanya berjalan di server.
- Menjadwalkan Pekerjaan
Setelah melakukan konfigurasi Agenda.js, selanjutnya adalah menjadwalkan pekerjaan untuk memposting blog posts. Langkah selanjutnya Kita perlu ubah sedikit struktur Posts collection, seperti ini:
// Posts.ts Collection const Posts: CollectionConfig = { slug: 'posts', admin: { defaultColumns: ['title', 'author', 'category', 'tags', 'status'], useAsTitle: 'title', }, access: { read: () => true, }, fields: [ { name: 'title', type: 'text', }, { name: 'author', type: 'relationship', relationTo: 'users', }, { name: 'publishedDate', type: 'date', admin: { hidden: true, } }, { name: 'publishedSchedule', type: 'date', required: false, admin: { position: 'sidebar', date: { pickerAppearance: "dayAndTime", timeIntervals: 5, } } }, { name: 'category', type: 'relationship', relationTo: 'categories' }, { name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true, }, { name: 'content', type: 'richText' }, { name: 'status', type: 'select', options: [ { value: 'draft', label: 'Draft', }, { value: 'published', label: 'Published', }, ], defaultValue: 'draft', admin: { position: 'sidebar', } } ], } export default Posts;
Kode diatas kita menambahkan property publishedSchedule
yang akan kita gunakan untuk menjadwalkan di Agenda.js dan menyembunyikan publishedDate
dari tampilan di admin dashboard karena akan otomatis terisi nanti ketika sudah diterbitkan. Karena Kita ubah struktur perlu menjalankan perintah ini untuk membuat typescript definitions yang berguna bagi kita kedepannya.
npm run generate:types // optional npm run generate:graphQLSchema
Kita cek, apa perubahan kita sudah berhasil:
Selanjutnya, tambahkan penjadwalan pekerjaan di dalam collection hook. Berikut adalah contoh kode untuk menjadwalkan pekerjaan:
// Posts.ts Collection import type Agenda from 'agenda'; import { Post } from 'payload/generated-types'; import { compareAsc } from 'date-fns'; import { CollectionAfterChangeHook, CollectionBeforeChangeHook, CollectionConfig, } from 'payload/types'; const afterChangeHook: CollectionAfterChangeHook<Post> = async ({req, operation, doc, previousDoc}) => { switch (operation) { case 'create': if (doc.status === 'draft' && doc.publishedSchedule && compareAsc(new Date(doc.publishedSchedule), new Date()) === 1) { const agenda = req.app.get('agenda') as Agenda; await agenda.schedule(new Date(doc.publishedSchedule), 'publish-post', { id: doc.id, title: doc.title }) } break; case 'update': if (doc.status === 'draft' && doc.publishedSchedule && compareAsc(new Date(doc.publishedSchedule), new Date()) === 1) { const agenda = req.app.get('agenda') as Agenda; let agendaJobs = await agenda.jobs({ name: 'publish-post', 'data.id': doc.id, }, {_id: 1}, 1); if (agendaJobs.length === 1) { await agendaJobs[0].remove(); } await agenda.schedule(new Date(doc.publishedSchedule), 'publish-post', { id: doc.id, title: doc.title }) } break; default: break; } return doc; } const beforeChangeHook: CollectionBeforeChangeHook<Post> = async ({data, req, operation, originalDoc}) => { if (data.status === 'published') { data.publishedDate = new Date().toISOString(); } return data; } const Posts: CollectionConfig = { slug: 'posts', admin: { defaultColumns: ['title', 'author', 'category', 'tags', 'status'], useAsTitle: 'title', }, access: { read: () => true, }, hooks: { beforeChange: [beforeChangeHook], afterChange: [afterChangeHook] }, fields: [ { name: 'title', type: 'text', }, { name: 'author', type: 'relationship', relationTo: 'users', }, { name: 'publishedDate', type: 'date', admin: { hidden: true, } }, { name: 'publishedSchedule', type: 'date', required: false, admin: { position: 'sidebar', date: { pickerAppearance: "dayAndTime", timeIntervals: 5, } } }, { name: 'category', type: 'relationship', relationTo: 'categories' }, { name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true, }, { name: 'content', type: 'richText' }, { name: 'status', type: 'select', options: [ { value: 'draft', label: 'Draft', }, { value: 'published', label: 'Published', }, ], defaultValue: 'draft', admin: { position: 'sidebar', } } ], } export default Posts;
Pada contoh kode di atas, KIta menggunakan 2 built-in function hooks dari PayloadCMS.
- Fungsi
afterChangeHook
adalah fungsi asynchronous yang dipicu setelah ada perubahan pada sebuah postingan. Fungsi ini memeriksa apakah postingan tersebut masih berupa draft dan memiliki jadwal publikasi di masa depan. Jika ya, maka fungsi ini menggunakan paketAgenda
untuk menjadwalkan postingan untuk dipublikasikan pada tanggal dan waktu yang ditentukan dalam properti `publishedSchedule
`. Kita juga menyertakan ID dari blog post yang akan diterbitkan sebagai data pekerjaan - Fungsi
beforeChangeHook
adalah fungsi asynchronous yang dipicu sebelum ada perubahan pada sebuah postingan. Fungsi ini memeriksa apakah postingan tersebut akan dipublikasikan dan menetapkan nilai pada fieldpublishedDate
menjadi tanggal dan waktu saat ini.
Terakhir Kita perlu perbarui job processor atau definisi pekerjaan yang ada di file jobs/definitions.ts
agar Agenda.js bisa mengubah data post ketika sudah masuk waktu jadwal yang ditentukan. Contoh kodenya sebagai berikut:
// jobs/definitions.ts import type Agenda from 'agenda'; import { type Payload } from 'payload'; const publishPostDefinition = (agenda: Agenda, payload: Payload) => { agenda.define('publish-post', {shouldSaveResult: true}, async (job) => { payload.logger.info(job.attrs, 'agenda.define publish-post job'); const { attrs: { data: { id } } } = job; let post = await payload.findByID({collection: 'posts', id, depth: 2}); payload.logger.info({post}, 'agenda.define publish-post Post'); if (post?.status === 'draft') { await payload.update({collection: 'posts', id, data: {status: 'published'}, depth: 2}); } }) } const allDefinitions = [publishPostDefinition]; export function allJobs(agenda: Agenda, payload: Payload) { allDefinitions.forEach(function(definition) { definition(agenda, payload); }) }
ID post berada di parameter job
dan Kita mengakses nilainya melalui job.attrs.id
. Setiap data yang dimasukkan di parameter ke 3 pada method agenda.schedule
akan tersedia di objek job.attrs
. Barulah Kita perbarui post status
menjadi published
dengan memastikan status sebelumnya adalah draft
.
Dengan menggunakan Agenda.js, Kalian bisa menjadwalkan posting blog posts dengan mudah. Kalian juga bisa membuat lebih dari satu pekerjaan untuk memposting blog posts pada waktu yang berbeda.
Kesimpulan
PayloadCMS adalah sebuah CMS open-source yang menyediakan berbagai fitur untuk mengelola konten, termasuk blog posts. Meskipun PayloadCMS tidak memiliki fitur bawaan untuk mengatur jadwal posting blog posts, Kalian bisa menggunakan library JavaScript seperti Agenda.js untuk melakukan itu. Dengan Agenda.js, Kalian bisa dengan mudah menjadwalkan posting blog posts pada waktu yang diinginkan. Selamat mencoba!
Bagi kalian yang mau lihat langsung repositori proyeknya silakan ke sini. Saya terbuka bagi siapa saja yang ingin bertanya melalui instagram atau telegram saya, bisa lihat ke web ini.