Cara Menampilkan Snippet Artikel Drupal ke Website Nextjs

17 Juni 2026
Bagikan:
Cara Menampilkan Snippet Artikel Drupal ke Website Nextjs

Menghubungkan CMS tradisional seperti Drupal dengan framework modern seperti Next.js adalah strategi cerdas. Anda tetap mendapatkan kemudahan menulis di Drupal, sekaligus kecepatan tinggi untuk landing page utama di Next.js.

Jika Anda ingin menampilkan potongan ringkasan (snippet) artikel terbaru dari Drupal ke website Next.js, Anda tidak perlu menginstal modul headless pihak ketiga yang rumit. Drupal secara out-of-the-box sudah menyediakan fitur ini.

Pada contoh kasus kali ini sebenarnya saya ingin mengambil snippet artikel dari subdomain website saya yang menggunakan drupal dan menampilkannya pada domain utama yang menggunakan nextjs.

Berikut adalah langkah-langkah mudah untuk menerapkannya:

Langkah 1: Mengaktifkan JSON:API di Drupal

Pertama, kita harus membuka gerbang data Drupal agar bisa dibaca oleh aplikasi luar dalam bentuk format JSON.

  1. Masuk ke Admin Drupal Anda, lalu buka menu Extend.
  2. Cari modul bawaan bernama JSON:API dan Serialization.
  3. Centang kedua modul tersebut dan klik Install.
  4. Buka menu People -> Permissions, lalu pastikan peran Anonymous User diberi izin untuk melihat konten (View published content).

Sekarang, Anda bisa mengecek data mentah artikel Anda melalui URL otomatis ini: https://domain-drupal-anda.com/jsonapi/node/article.

Langkah 2: Menarik Data (Fetching) di Next.js

Di sisi proyek Next.js, buat sebuah fungsi asynchronous untuk mengambil data artikel dari endpoint JSON:API Drupal tadi.

Gunakan parameter query tambahan agar Next.js hanya mengambil beberapa artikel terbaru saja (misalnya 3 artikel) untuk menghemat bandwidth server.

path : /app/api/articles/route.ts

import { NextResponse } from 'next/server';
/**
* Handler untuk mengambil 3 artikel terbaru dari Drupal
* Endpoint: /api/articles
*/
export async function GET() {
   try {
       const res = await fetch(
           'https://websitedrupalkamu.com/jsonapi/node/article?page[limit]=3&sort=-created&include=field_image',
           {
               // Menggunakan Next.js Data Cache dengan revalidasi 1 jam
               next: { revalidate: 3600 },
           }
       );
       if (!res.ok) {
           return NextResponse.json(
               { message: 'Gagal mengambil data dari Drupal' },
               { status: res.status }
           );
       }
       const json = await res.json();
       const included = json.included || [];
       // Transformasi data menjadi format snippet yang lebih sederhana
       const articles = json.data.map((article: any) => {
           // Mencari data gambar di dalam array 'included'
           const imageRel = article.relationships?.field_image?.data;
           let imageUrl = null;
           if (imageRel) {
               const imageFile = included.find(
                   (item: any) => item.type === 'file--file' && item.id === imageRel.id
               );
               if (imageFile?.attributes?.uri?.url) {
                   // Gabungkan dengan base URL Drupal jika path bersifat relatif
                   imageUrl = `https://websitedrupalkamu.com${imageFile.attributes.uri.url}`;
               }
           }
           return {
               id: article.id,
               title: article.attributes.title,
               date: article.attributes.created,
               slug: article.attributes.path?.alias || `/node/${article.attributes.drupal_internal__nid}`,
               image: imageUrl,
               // Membuat snippet: Ambil summary, jika kosong ambil body value dan bersihkan tag HTML, lalu potong
               excerpt: article.attributes.body?.summary ||
                   (article.attributes.body?.value
                       ? article.attributes.body.value.replace(/<[^>]*>?/gm, '').substring(0, 150) + '...'
                       : ''),
           };
       });
       return NextResponse.json(articles);
   } catch (error) {
       console.error('Drupal Fetch Error:', error);
       return NextResponse.json(
           { message: 'Internal Server Error' },
           { status: 500 }
       );
   }
}

Langkah 3: Merender Tampilan Snippet

Setelah data berhasil ditarik, Anda tinggal melakukan perulangan (looping) di komponen Next.js untuk menampilkan judul, cuplikan ringkasan, dan tombol yang mengarah ke artikel utuh di Drupal. Nanti kalian bisa buat component ini terpisah saja misal di path : /components/artikel, initinya supaya lebih modular dan nanti bisa digunakan pada halaman lain

Contoh :

'use client';
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { ArrowRight, Calendar } from 'lucide-react';
import Image from 'next/image';
interface Article {
   id: string;
   title: string;
   date: string;
   slug: string;
   image: string | null;
   excerpt: string;
}
const BlogSection = () => {
   const [articles, setArticles] = useState<Article[]>([]);
   const [loading, setLoading] = useState(true);
   useEffect(() => {
       const fetchArticles = async () => {
           try {
               const res = await fetch('/api/articles');
               if (res.ok) {
                   const data = await res.json();
                   setArticles(data);
               }
           } catch (error) {
               console.error("Error fetching articles:", error);
           } finally {
               setLoading(false);
           }
       };
       fetchArticles();
   }, []);
   if (loading || articles.length === 0) return null;
   return (
       <section className="py-24 bg-white dark:bg-slate-900 transition-colors">
           <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
               <div className="flex justify-between items-end mb-12">
                   <div className="max-w-2xl">
                       <h2 className="text-3xl md:text-4xl font-bold mb-4">Artikel Terbaru</h2>
                       <p className="text-slate-600 dark:text-slate-400">
                           Wawasan terbaru seputar teknologi, solusi bisnis, dan tips dari tim ahli kami.
                       </p>
                   </div>
                   <a
                       href="https://websitedrupalkamu.com"
                       target="_blank"
                       rel="noopener noreferrer"
                       className="hidden md:flex items-center text-blue-600 hover:text-blue-700 font-semibold group"
                   >
                       Lihat Semua Artikel
                       <ArrowRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform" />
                   </a>
               </div>
               <div className="grid md:grid-cols-3 gap-8">
                   {articles.map((article, idx) => (
                       <motion.div
                           key={article.id}
                           initial={{ opacity: 0, y: 20 }}
                           whileInView={{ opacity: 1, y: 0 }}
                           viewport={{ once: true }}
                           transition={{ delay: idx * 0.1 }}
                           className="flex flex-col bg-slate-50 dark:bg-slate-800 rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 border border-slate-100 dark:border-slate-700"
                       >
                           <div className="relative h-52 w-full overflow-hidden">
                               <Image
                                   src={article.image || '/img/pursem.webp'}
                                   alt={article.title}
                                   fill
                                   className="object-cover transition-transform duration-500 hover:scale-105"
                               />
                           </div>
                           <div className="p-6 flex flex-col flex-1">
                               <div className="flex items-center text-xs text-slate-500 dark:text-slate-400 mb-3">
                                   <Calendar className="h-3 w-3 mr-1" />
                                   {new Date(article.date).toLocaleDateString('id-ID', {
                                       day: 'numeric',
                                       month: 'long',
                                       year: 'numeric'
                                   })}
                               </div>
                               <h3 className="text-xl font-bold mb-3 line-clamp-2 text-slate-900 dark:text-white">
                                   {article.title}
                               </h3>
                               <p className="text-slate-600 dark:text-slate-400 text-sm mb-6 flex-1 line-clamp-3">
                                   {article.excerpt}
                               </p>
                               <a
                                   href={`https://websitedrupalkamu.com${article.slug}`}
                                   target="_blank"
                                   rel="noopener noreferrer"
                                   className="inline-flex items-center text-sm font-bold text-blue-600 hover:text-blue-700 mt-auto group"
                               >
                                   Baca Selengkapnya
                                   <ArrowRight className="ml-1 h-3 w-3 group-hover:translate-x-1 transition-transform" />
                               </a>
                           </div>
                       </motion.div>
                   ))}
               </div>
           </div>
       </section>
   );
};
export default BlogSection;

Langkah 4: NextJS Image

Agar gambar external dari drupal bisa dimuat maka pastikan bahwa websitedrupalkamu.com dimasukan kedalam konfigurasi konfigurasi images.domains atau images.remotePatterns di file next.config.js / next.config.ts agar gambar eksternal dari Drupal dapat dimuat

Contoh :

import type { NextConfig } from "next";
const nextConfig: NextConfig = {
 // output: 'export', // Ini akan mengaktifkan mode export statis
 // Opsional: Jika menggunakan tag <Image>, Anda harus menonaktifkan optimasi gambar default
 trailingSlash: true,
 images: {
   unoptimized: true,
   remotePatterns: [
     {
       protocol: 'https',
       hostname: 'websitedrupalkamu.com',
       pathname: '/**',
     },
   ],
 },
};
module.exports = nextConfig;

Penggunaan remotePatterns jauh lebih disarankan daripada domains (yang sekarang sudah deprecated) karena lebih aman dan spesifik. Meskipun saat ini Anda mengatur unoptimized: true (yang biasanya melewati pemeriksaan domain karena gambar tidak diproses oleh server Next.js)

Keterangan :

  • protocol: Kita tentukan https karena URL gambar dari Drupal Anda menggunakan SSL.
    hostname: Kita arahkan ke websitedrupalkamu.com.
  • pathname: Menggunakan wildcard /** agar semua file gambar di dalam folder manapun di domain tersebut dapat dimuat.
  • Catatan tentang unoptimized: Karena saat ini unoptimized: true aktif, Next.js akan menampilkan gambar apa adanya dari Drupal tanpa mengubah ukuran atau formatnya di sisi server. Jika Anda ingin menggunakan fitur optimasi otomatis Next.js (seperti kompresi WebP otomatis), Anda bisa mengubahnya menjadi false

Kesimpulan & Dampak SEO

Pendekatan ini sangat ramah terhadap SEO (SEO Friendly). Karena website Next.js hanya menampilkan cuplikan pendek (snippet) dan menyertakan tautan balik langsung ke artikel asli di Drupal, Google tidak akan menganggapnya sebagai Duplicate Content (konten duplikat). Justru, cara ini membantu menaikkan otoritas domain blog Anda di mata mesin pencari.