laravel-base maintained by dantepiazza
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.0sentry/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