In the modern era of web development, the concept of a Headless CMS has gained immense popularity. By decoupling the backend content management system from the frontend display layer, developers can deliver content to multiple platforms seamlessly. Laravel, a powerful PHP framework, provides an excellent foundation for creating a robust and scalable Headless CMS.
In this guide, I’ll walk through the steps to build one from scratch.
Prerequisites
Before diving in, make sure you have the following:
- PHP (>=8.1)
- Composer
- Laravel Framework (latest version)
- A database (e.g., MySQL, PostgreSQL, or SQLite)
- Basic knowledge of Laravel and REST APIs
Step 1: Setting Up Laravel
- Install Laravel
Create a new Laravel project by running:
composer create-project --prefer-dist laravel/laravel headless-cms
2. Configure Environment
Set up your .env
file with database credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=headless_cms
DB_USERNAME=root
DB_PASSWORD=secret
3. Migrate Default Tables
Run the following command to migrate the default Laravel tables:
php artisan migrate
Step 2: Design Your Content Models
For this example, let’s create a blog system with Post
and Category
models.
- Create Models and Migrations
php artisan make:model Post -m
php artisan make:model Category -m
2. Define Database Structure
Update the migration files:
database/migrations/YYYY_MM_DD_create_posts_table.php
:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
database/migrations/YYYY_MM_DD_create_categories_table.php
:
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
3. Run Migrations
Execute the migrations to create the tables:
php artisan migrate
Step 3: Build API Endpoints
- Set Up API Routes
Open routes/api.php
and define the routes:
use App\Http\Controllers\PostController;
use App\Http\Controllers\CategoryController;
Route::apiResource('posts', PostController::class);
Route::apiResource('categories', CategoryController::class);
2. Create Controllers
php artisan make:controller PostController --api
php artisan make:controller CategoryController --api
3. Implement Controller Logic
In PostController.php
:
public function index()
{
return Post::with('category')->get();
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required',
'category_id' => 'required|exists:categories,id',
]);
return Post::create($validated);
}
public function show(Post $post)
{
return $post->load('category');
}
public function update(Request $request, Post $post)
{
$validated = $request->validate([
'title' => 'sometimes|string|max:255',
'content' => 'sometimes',
'category_id' => 'sometimes|exists:categories,id',
]);
$post->update($validated);
return $post;
}
public function destroy(Post $post)
{
$post->delete();
return response()->noContent();
}
Repeat similar logic for CategoryController
.
4. Test API with Postman or cURL
Ensure endpoints like /api/posts
and /api/categories
are functioning correctly.
Step 4: Add Authentication
Install Laravel Sanctum
composer require laravel/sanctum
Publish and Migrate Sanctum Config
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Protect Routes
Wrap your API routes with the auth:sanctum
middleware:
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('posts', PostController::class);
Route::apiResource('categories', CategoryController::class);
});
Generate Tokens for Users
Add a method in your User
model:
public function generateToken()
{
return $this->createToken('api-token')->plainTextToken;
}
Step 5: Frontend Integration
Since this is a Headless CMS, you can use any frontend framework (React, Vue.js, Angular) to consume the API. Here’s an example of fetching data using React:
import React, { useEffect, useState } from 'react';
function App() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('http://localhost:8000/api/posts')
.then(response => response.json())
.then(data => setPosts(data));
}, []);
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default App;
Conclusion
Congratulations! You’ve built a Headless CMS using Laravel. This system can be extended further by adding features like role-based access control (RBAC), advanced filtering, or GraphQL support. The possibilities are endless when you decouple your frontend and backend.
Happy coding!