Once your basics are solid, you can add more sophisticated features. Webhooks, batch operations, search, subscriptions.
These aren't necessary for day one. But they're the kind of features that make your API feel production-grade once you're ready to scale.
These chapters covered the fundamentals. Chapters 12 and 13 are the 20% of features that give 80% of the wow factor, once you have the foundation right.
Webhooks
Notify external systems of events:
// Events that happen in your code
class DomainPurchased
{
public function __construct(
public Domain $domain,
public User $user,
) {}
}
// Create webhooks table
Schema::create('webhooks', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->string('event');
$table->json('filters')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
// Register event listener
class SendWebhookOnDomainPurchased
{
public function handle(DomainPurchased $event)
{
$webhooks = Webhook::where('event', 'domain.purchased')
->where('active', true)
->get();
foreach ($webhooks as $webhook) {
DispatchWebhookJob::dispatch($webhook, $event->domain, $event->user);
}
}
}
// Job that sends webhook
class DispatchWebhookJob implements ShouldQueue
{
public function handle(Webhook $webhook, Domain $domain, User $user)
{
$payload = [
'event' => 'domain.purchased',
'timestamp' => now()->toIso8601String(),
'data' => [
'domain' => $domain->domain,
'user_id' => $user->id,
],
];
try {
Http::timeout(30)->post($webhook->url, $payload);
} catch (Exception $e) {
// Retry up to 5 times before giving up
if ($this->attempts() < 5) {
$this->release(exponentialBackoff());
}
}
}
}
Bulk Operations
Handle multiple resources efficiently (advanced pattern):
// Example: Bulk create/update endpoints
Route::post('/api/licenses/bulk', function (Request $request) {
$operations = $request->validate([
'operations' => ['required', 'array', 'max:100'],
'operations.*.op' => ['required', 'in:create,delete'],
'operations.*.name' => ['required', 'string'],
'operations.*.domain' => ['required', 'string'],
]);
$results = [];
DB::transaction(function () use ($operations, &$results) {
foreach ($operations as $operation) {
try {
if ($operation['op'] === 'create') {
$results[] = LicenseFacade::addLicense(
name: $operation['name'],
domain: $operation['domain'],
);
} elseif ($operation['op'] === 'delete') {
Domain::where('domain', $operation['domain'])
->update($operation);
} elseif ($operation['op'] === 'delete') {
Domain::where('domain', $operation['domain'])
->delete();
}
} catch (Exception $e) {
// All or nothing - rollback on any error
throw $e;
}
}
});
return response()->json(['data' => $results]);
});
Real-time Updates (WebSocket)
For live notifications:
composer require beyondcode/laravel-websockets
php artisan websockets:serve
// Broadcast event when domain changes
class DomainUpdated
{
use Dispatchable, InteractsWithBroadcasting;
public function __construct(public Domain $domain) {}
public function broadcastOn(): Channel
{
return new Channel("domains.{$this->domain->id}");
}
public function broadcastAs(): string
{
return 'updated';
}
}
// Client-side (JavaScript)
Echo.channel(`domains.${domainId}`)
.listen('DomainUpdated', (data) => {
console.log('Domain updated:', data.domain);
});
Chapter 13: DevOps & Infrastructure
Your amazing API doesn't matter if it's not in production. Deployment is where theory meets reality.
This chapter is short but important. We cover:
- CI/CD: Automated testing on every push
- Deployment: Getting code from your laptop to the internet
- Monitoring: Knowing when things break
- Scaling: Handling growth
These are the unglamorous but essential parts of shipping software.