Build a Headless Laravel CMS with GraphQL, Lighthouse & Next.js

Mahmut Sarıkaya 4 dk okuma 5 Görüntülenme 0
Build a Headless Laravel CMS with GraphQL, Lighthouse & Next.js

Why a headless CMS matters

Enterprises are demanding faster time‑to‑market for digital experiences, and a 2023 survey from Contentful shows that 72% of teams consider API‑first content management a strategic advantage. A headless CMS decouples the data layer from presentation, allowing developers to reuse the same content across web, mobile and IoT without rewriting business logic. This separation also enables independent scaling—your Laravel API can handle heavy traffic while the Next.js front‑end caches static pages for near‑instant loads.

Choosing Laravel as the backend engine

Laravel brings a mature ecosystem, expressive ORM (Eloquent), and a vibrant community that makes building a custom CMS straightforward. Because Laravel already ships with authentication scaffolding, queue workers and a robust testing suite, you can focus on content structures rather than boilerplate. Moreover, the framework’s service container makes injecting GraphQL services painless, which is essential when you want a single source of truth for all content queries.

Setting up Laravel and Lighthouse

Start with a fresh Laravel installation on PHP 8.2 or later. System requirements include Composer 2.x, MySQL 5.7+ and Node.js 18 for later front‑end steps. Run the following commands in your terminal:

composer create-project --prefer-dist laravel/laravel cms-backend
cd cms-backend
composer require nuwave/lighthouse

After the package is installed, publish Lighthouse’s default configuration:

php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag="config"

Finally, expose a GraphQL endpoint by adding a route. Laravel’s routing file lives at routes/web.php:

<?php
use Illuminate\Support\Facades\Route;

Route::post('/graphql', [\Nuwave\Lighthouse\Support\Http\Controllers\GraphQLController::class, 'handle']);
?>

With the endpoint live, you can test it using GraphQL Playground or any HTTP client.

Designing the GraphQL schema

Lighthouse reads schema definitions from graphql/schema.graphql. A minimal content model for articles might look like this:

type Article @model {
  id: ID!
  title: String!
  slug: String! @unique
  body: String!
  published_at: DateTime @field(resolver: "App\\GraphQL\\Resolvers\\ArticleResolver@publishedAt")
}

type Query {
  articles: [Article!]! @paginate(model: "App\\Models\\Article")
  article(slug: String!): Article @find
}

The @model directive tells Lighthouse to generate an Eloquent model automatically, while @paginate adds built‑in pagination without extra code. This schema becomes the contract between Laravel and any front‑end client.

Creating content models and resolvers

Run php artisan lighthouse:print-schema to verify the generated types. For custom fields like a formatted publish date, add a resolver class:

<?php
namespace App\GraphQL\Resolvers;

use Carbon\Carbon;
use App\Models\Article;

class ArticleResolver
{
public function publishedAt(Article $article): string
{
return Carbon::parse($article->published_at)->format('F j, Y');
}
}
?>

Register the resolver in config/lighthouse.php under the namespaces key, then you can query { article(slug:"my-first-post") { title, published_at } } from any client.

Connecting Next.js as the front‑end

Next.js 14 introduces the app directory for server‑components, which works perfectly with GraphQL. Install Apollo Client for query handling:

npm install @apollo/client graphql

Create an apollo.js helper in src/lib:

import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';

const client = new ApolloClient({
link: new HttpLink({ uri: process.env.NEXT_PUBLIC_API_URL + '/graphql' }),
cache: new InMemoryCache(),
});

export default client;

Fetch articles inside a server component:

import client from '@/lib/apollo';
import { gql } from '@apollo/client';

const GET_ARTICLES = gql`
query GetArticles {
articles(first: 10) {
data {
id
title
slug
published_at
}
}
}`;

export default async function ArticleList() {
const { data } = await client.query({ query: GET_ARTICLES });
return (
<ul>{data.articles.data.map(a => (<li key={a.id}>{a.title} – {a.published_at}</li>))}</ul>
);
}

The component renders on the server, so the HTML is ready for SEO crawlers, while the client can hydrate for interactive features like infinite scroll.

Deploying and caching strategies

When you push the Laravel API to a platform like Laravel Vapor or Forge, enable Redis as the cache driver and configure Lighthouse’s query cache (set cache.enable: true in config/lighthouse.php). On the Next.js side, leverage Incremental Static Regeneration (ISR) by adding revalidate: 60 to getStaticProps. This combination reduces load on the GraphQL server by serving pre‑rendered pages for up to a minute, while still delivering fresh content after each revalidation cycle.

Conclusion

By pairing Laravel’s expressive backend with Lighthouse’s GraphQL engine and a modern Next.js front‑end, you create a truly headless CMS that scales, remains SEO‑friendly, and lets front‑end teams work independently. The step‑by‑step setup—starting from a clean Laravel install, defining a concise GraphQL schema, writing tiny resolvers, and finally consuming data in Next.js—demonstrates that a production‑ready headless architecture can be assembled in under a day.

Author: Mahmut Sarıkaya — sarikayadev.com

Sources

  • Laravel Official Documentation
  • Lighthouse GraphQL for Laravel Docs
  • Next.js Documentation (vercel.com)
Etiketler: #laravel #headless cms #graphql #lighthouse #next.js
Paylaş:
M

Yazar

Mahmut Sarıkaya

yazılım Geliştirici

Yorumlar

Henüz yorum yok. İlk yorumu siz yapın!

Yorum Bırakın

4 + 7 =