How I Rebuilt Shaan Puri's Blog with Laravel & Cloudflare to Get Perfect 100 Google Web Vitals Scores
Join me as I rebuild Shaan Puri's blog using Laravel, Nuxt, and Cloudflare, achieving perfect 100 Google Core Web Vitals scores. This post guides you through the entire process, highlighting key optimizations for web performance.
Table of Contents
- Create the Nuxt App and Deploy to Cloudflare Pages
- Create a SQLite Database using Cloudflare's D1 Database
- Create the Laravel Web Server
- Create the Model, Controllers, and API Routes
- Create a Cloudflare Worker for the Cache Layer
- Conclusion
- Stay Updated
Featured Technologies
- Laravel
- Nuxt
- Cloudflare D1
- Cloudflare Pages
- Cloudflare Workers
- Cloudflare KV store
- TailwindCSS
- Vue.js
- REST API
Important Links
- Visit Shaan Puri's Rebuilt Blog
- Nuxt App on Cloudflare Pages code
- Laravel REST API code
- Cloudflare Worker code
- Laravel and Cloudflare D1 connection
Steps for Rebuilding Shaan Puri's Blog
Create the Nuxt App and Deploy to Cloudflare Pages
- Make sure you create a Cloudflare account.
- Log in to your Cloudflare account.
- In your terminal run:
npx wrangler login
- Run:
pnpm create cloudflare@latest blog-app-client
- Select Website or web app.
- Select Nuxt.
- When asked "Do you want to deploy your application?" select yes.
- Change into the newly created directory with
cd blog-app-client
. - You can run the template app with
pnpm run dev
. - And you can deploy any new changes to the app with
pnpm run deploy
. - Initialize git in the directory with
git init
. - Create a new repository in GitHub and then connect and push your local app to the newly created repository.
- Add TailwindCSS to your app:
- Run
npx nuxi@latest module add tailwindcss
- Create your TailwindCSS config file with
npx tailwindcss init --ts
-
Push your latest changes to git with
git commit -m "add tailwind to nuxt app"
. - Now you can style and build your Nuxt app as you choose.
Create a SQLite Database using Cloudflare's D1 Database
- Run
npx wrangler d1 create blog-db
. -
Save the
database_id
found in the terminal for later use in your Laravel web server.
Create the Laravel Web Server
- Ensure you have PHP and Composer installed and running on your local machine.
- Create a default Laravel app:
-
In a new directory run
composer create-project --prefer-dist laravel/laravel="10.*" laravel-api && cd laravel-api
. -
In the directory of your Laravel web server, open the
.env
file and add the following environment variables:CLOUDFLARE_D1_DATABASE_ID
CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_TOKEN
- Create the D1 database connection with the L1 library:
- Run
composer require renoki-co/l1
. -
In
config/database.php
add the following connector:'connections' => [ 'd1' => [ 'driver' => 'd1', 'prefix' => '', 'database' => env('CLOUDFLARE_D1_DATABASE_ID', ''), 'api' => 'https://api.cloudflare.com/client/v4', 'auth' => [ 'token' => env('CLOUDFLARE_TOKEN', ''), 'account_id' => env('CLOUDFLARE_ACCOUNT_ID', ''), ], ], ],
-
Set the Default Database Connection Name to
'd1'
:'default' => 'd1',
Create the Model, Controllers, and API Routes
- Think about the data you want to persist in the D1 database.
-
Create your first model in the
laravel-api
directory:- Run
php artisan make:model articles -m
.
- Run
-
Update the
up
function in the newly created migration file indatabase/migrations/
to match your schema: - Run your first migration against the D1 database:
- Run
php artisan migrate
. - Check your migration in the Cloudflare dashboard.
-
Update the model file in
app/Models/
to define which attributes are assignable:namespace AppModels; use IlluminateDatabaseEloquentFactoriesHasFactory; use IlluminateDatabaseEloquentModel; class articles extends Model { use HasFactory; /** * The attributes that are mass assignable. * * @var array<int, string> */ protected $fillable = [ 'slug', 'title', 'description', 'body', 'author_full_name', 'cover_img_src', 'cover_img_alt', 'is_active', 'published_date', 'read_time', ]; }
- Create the controller:
- Run
php artisan make:controller ArticleController
. - Add public methods for each CRUD action in the controller file.
- Define the API endpoints for your Laravel REST API in
routes/api.php
. - Test your Laravel API server:
- Run
php artisan serve
.
use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('slug')->unique();
$table->string('title');
$table->text('description')->nullable();
$table->longText('body');
$table->string('author_full_name')->nullable();
$table->string('cover_img_src')->nullable();
$table->string('cover_img_alt')->nullable();
$table->boolean('is_active')->default(true);
$table->date('published_date');
$table->string('read_time');
$table->index(['slug', 'is_active']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('articles');
}
};
Create a Cloudflare Worker for the Cache Layer
- Back out of your Laravel API directory and prepare to create your Cloudflare Worker:
- Run
pnpm create cloudflare@latest blog-worker
. -
Follow the prompts in the terminal:
- Select Hello World Worker.
- Select Yes for TypeScript.
- Select Yes to deploy your application.
- Change directory into your new Cloudflare Worker:
- Run
pnpm run start
. - Run
pnpm run deploy
. - Use this worker as a cache layer to reduce latency for visitors to the blog.
Conclusion
By following these steps, you can achieve perfect 100 Google Web Vitals scores for your blog. Leveraging Laravel, Nuxt, and Cloudflare, you can create a fast, efficient, and scalable blog that provides an excellent user experience.