Looking to hire Laravel developers? Try LaraJobs

laravel-base maintained by dantepiazza

Description
Base package for Laravel microservices: inter-service communication, JWT middleware, response helpers and notifications.
Author
Dante Piazza Quiroga
Last update
2026/05/24 04:42 (dev-main)
License
Links
Downloads
0

Comments
comments powered by Disqus

dantepiazza/laravel-base

Base package for Laravel backend projects. Provides a pre-configured bootstrap/app.php, standardized middleware, structured API responses, webhook handling, error tracking, and API documentation — all publishable via a single command.


Requirements

  • PHP 8.3+
  • Laravel 11, 12 or 13
  • dantepiazza/laravel-api-response ^1.0
  • sentry/sentry-laravel ^4.0
  • A configured queue driver (Redis recommended) for webhook processing

Installation

composer require dantepiazza/laravel-base

Publish all stubs at once:

php artisan vendor:publish --tag=laravel-base

Or publish individually:

Tag Publishes
laravel-base Everything below at once
laravel-base-bootstrap bootstrap/app.php
laravel-base-logging config/logging.php
laravel-base-cors config/cors.php
laravel-base-sentry config/sentry.php
laravel-base-controller app/Http/Controllers/Controller.php
laravel-base-env .env.example
laravel-base-docker Dockerfile, docker-compose.yml and all Docker config files

Scribe config is merged from the package by default — no need to publish it unless you need to customize it:

php artisan ms:publish-scribe

# Overwrite without prompt:
php artisan ms:publish-scribe --force

Environment variables

# ─── App ──────────────────────────────────────────────────────────────────────
APP_NAME=my-service
APP_ENV=production
APP_URL=https://my-service.yourdomain.com

# ─── GlitchTip (via Sentry SDK) ───────────────────────────────────────────────
GLITCHTIP_DSN=
GLITCHTIP_TRACES_SAMPLE_RATE=0.1
GLITCHTIP_PROFILES_SAMPLE_RATE=0.1

# ─── Logging ──────────────────────────────────────────────────────────────────
LOG_CHANNEL=stack
LOG_STACK=daily,slack
LOG_LEVEL=error
LOG_SLACK_WEBHOOK_URL=
LOG_SLACK_USERNAME=
LOG_SLACK_LEVEL=critical

# ─── Scribe ───────────────────────────────────────────────────────────────────
SCRIBE_AUTH_KEY=

# ─── CORS ─────────────────────────────────────────────────────────────────────
CORS_ALLOWED_ORIGINS=http://localhost

Features

1. Base Controller — dantepiazza/laravel-api-response

This package requires dantepiazza/laravel-api-response and pre-wires it into the base Controller stub. After publishing, every controller in your project gets ApiResponse injected automatically:

// app/Http/Controllers/Controller.php (published stub)
namespace App\Http\Controllers;

use DantePiazza\LaravelApiResponse\ApiResponse;

abstract class Controller
{
    public function __construct(protected ApiResponse $api) {}
}

Usage in any controller:

class InvoiceController extends Controller
{
    public function index(): JsonResponse
    {
        return $this->api->success('Invoices retrieved.')->data(Invoice::paginate(20))->response();
    }

    public function store(StoreInvoiceRequest $request): JsonResponse
    {
        return $this->api->created('Invoice created.')->data(Invoice::create($request->validated()))->response();
    }

    public function show(Invoice $invoice): JsonResponse
    {
        return $this->api->success()->data($invoice)->response();
    }

    public function destroy(Invoice $invoice): JsonResponse
    {
        $invoice->delete();
        return $this->api->success('Invoice deleted.')->response();
    }
}

2. Middleware

ForceJsonResponse

Forces Accept: application/json on every request so Laravel always returns JSON errors. Register it globally in bootstrap/app.php:

$middleware->api(append: [
    \DantePiazza\LaravelBase\Http\Middleware\ForceJsonResponse::class,
]);

GlobalHeaders

Attaches standard headers to every API response. Register it in bootstrap/app.php:

$middleware->api(append: [
    \DantePiazza\LaravelBase\Http\Middleware\GlobalHeaders::class,
]);

Headers added to every response:

Header Description
X-Maintenance Whether the service is in maintenance mode (app.maintenance)
X-Cache-Last-Update ISO 8601 timestamp of last cache invalidation (app.cache_last_update)
X-Correlation-ID Trace ID — reused from the incoming request or generated as UUID v4

The X-Correlation-ID is also injected back into the request object so any controller or service can read it:

$correlationId = $request->header('X-Correlation-ID');
// or
$correlationId = request()->header('X-Correlation-ID');

3. Webhook handling

The package ships a generic WebhookController that receives webhooks from any source, verifies the signature, and dispatches a Laravel event asynchronously.

Route setup

The controller expects a {source} route parameter that identifies the origin:

use DantePiazza\LaravelBase\Http\Controllers\WebhookController;

Route::post('webhooks/{source}', [WebhookController::class, 'handle']);

This dispatches events named webhook.{source}.{event_type}, so each origin is isolated:

URL called Event dispatched
POST /webhooks/convoy webhook.convoy.invoice.created
POST /webhooks/stripe webhook.stripe.charge.succeeded
POST /webhooks/github webhook.github.push

Signature verification

By default the controller rejects all requests (verifySignature returns false). To enable a source, extend the controller and override the method:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use DantePiazza\LaravelBase\Http\Controllers\WebhookController as BaseWebhookController;

class ConvoyWebhookController extends BaseWebhookController
{
    protected function verifySignature(Request $request, string $source): bool
    {
        return hash_equals(
            hash_hmac('sha256', $request->getContent(), config('services.convoy.secret')),
            (string) $request->header('X-Convoy-Signature')
        );
    }
}

Then point the route to your controller:

Route::post('webhooks/convoy', [ConvoyWebhookController::class, 'handle']);

Listening to webhook events

use Illuminate\Support\Facades\Event;

// Single event
Event::listen('webhook.stripe.charge.succeeded', function (array $payload) {
    $chargeId = $payload['data']['id'];
});

// Wildcard — all events from one source
Event::listen('webhook.convoy.*', ConvoyWebhookListener::class);

// Wildcard — every webhook regardless of source
Event::listen('webhook.*', GeneralWebhookListener::class);

Payload passed to every listener:

[
    'source'     => 'stripe',
    'event_type' => 'charge.succeeded',
    'data'       => [...],   // $body['data'] if present, otherwise full body
    'headers'    => [...],   // all request headers
    'raw'        => [...],   // full decoded request body
]

4. Exception handling (bootstrap/app.php)

The published bootstrap/app.php includes a full exception handler wired to dantepiazza/laravel-api-response:

Exception HTTP status Response
ValidationException 422 Field-level errors via validationError()
AuthenticationException 401 unauthorized()
AccessDeniedHttpException 403 forbidden()
NotFoundHttpException / ModelNotFoundException 404 notFound()
ThrottleRequestsException 429 tooManyRequests()
Throwable (catchall) 500 server_error with message

Rate limiters included out of the box:

Limiter Limit Keyed by
api 120 req/min user ID or IP
auth 10 req/min IP
microservices 600 req/min IP

5. GlitchTip / Sentry error tracking

Uses sentry/sentry-laravel, which is fully compatible with GlitchTip. Publish the config and set the DSN:

php artisan vendor:publish --tag=laravel-base-sentry
GLITCHTIP_DSN=https://key@glitchtip.yourdomain.com/1

The X-Correlation-ID on every request can be used to correlate GlitchTip events with specific API calls.


6. Logging

The published config/logging.php includes a pre-configured stack with daily file rotation and optional Slack alerts for critical errors:

LOG_STACK=daily,slack
LOG_LEVEL=error
LOG_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
LOG_SLACK_LEVEL=critical

7. API Documentation (Scribe)

Scribe is configured out of the box — the package merges its own config/scribe.php automatically, no publishing needed.

To generate docs:

php artisan scribe:generate

To customize the config for a specific project:

php artisan ms:publish-scribe
# Edit config/scribe.php, then regenerate

8. Docker

A production-ready Docker setup is included. Publish it with:

php artisan vendor:publish --tag=laravel-base-docker

Publishes:

Dockerfile
docker-compose.yml
.env.docker
docker/
├── mysql/my.cnf
├── nginx/default.conf
├── nginx/nginx.conf
├── php/php.ini
├── php/php-fpm.conf
├── supervisor/supervisord.conf
└── entrypoint.sh

Package structure

dantepiazza/laravel-base/
├── composer.json
├── README.md
├── src/
│   ├── BaseServiceProvider.php
│   ├── Console/Commands/
│   │   └── PublishScribeConfig.php      # php artisan ms:publish-scribe
│   └── Http/
│       ├── Controllers/
│       │   └── WebhookController.php    # Generic webhook receiver
│       └── Middleware/
│           ├── ForceJsonResponse.php
│           └── GlobalHeaders.php
├── stubs/
│   ├── .env.example
│   ├── app/Http/Controllers/
│   │   └── Controller.php              # Base controller with ApiResponse injected
│   ├── bootstrap/
│   │   └── app.php                     # Pre-configured with exception handling and rate limiters
│   ├── config/
│   │   ├── cors.php
│   │   ├── logging.php
│   │   ├── scribe.php
│   │   └── sentry.php
│   └── routes/
│       └── api.php
└── docker/
    ├── Dockerfile
    ├── docker-compose.yml
    ├── .env.docker
    ├── entrypoint.sh
    ├── mysql/my.cnf
    ├── nginx/default.conf
    ├── nginx/nginx.conf
    ├── php/php.ini
    ├── php/php-fpm.conf
    └── supervisor/supervisord.conf