Automate testing and deployment:
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_DATABASE: api_test
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: root
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
ports:
- 3306:3306
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: mysql, sqlite, pdo_sqlite
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Run tests
run: php artisan test --coverage
- name: Run PHPStan
run: ./vendor/bin/phpstan analyse
- name: Format check
run: ./vendor/bin/pint --test
Docker Deployment
# Dockerfile
FROM php:8.4-fpm-alpine
RUN apk add --no-cache \
mysql-client \
postgresql-client \
redis
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
COPY . .
RUN php artisan optimize
EXPOSE 9000
CMD ["php-fpm"]
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "8000:9000"
environment:
- DB_HOST=mysql
- CACHE_DRIVER=redis
- QUEUE_CONNECTION=redis
depends_on:
- mysql
- redis
- queue
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: api_app
MYSQL_ROOT_PASSWORD: secret
ports:
- "3306:3306"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
queue:
build: .
command: php artisan queue:work
depends_on:
- mysql
- redis
Database Migrations
Version control your database:
php artisan make:migration create_bearers_table
class CreateBearersTable extends Migration
{
public function up(): void
{
Schema::create('bearers', function (Blueprint $table) {
$table->id();
$table->string('token', 64)->unique();
$table->string('name');
$table->text('description')->nullable();
$table->timestamp('expires_at');
$table->json('scopes')->nullable();
$table->boolean('revoked')->default(false);
$table->timestamps();
$table->index('token');
$table->index('expires_at');
});
}
public function down(): void
{
Schema::dropIfExists('bearers');
}
}
// Deploy safely
php artisan migrate --force
Monitoring & Alerting
# Monitor logs in real-time
tail -f storage/logs/laravel.log | grep -i error
# Check queue length
php artisan queue:failed
# View active processes
php artisan tinker
queue()->failed()->count()
Scaling Strategies
- Horizontal Scaling: Run multiple API instances behind load balancer
- Caching: Cache expensive computations (Redis)
- Database Optimization: Index columns, use replicas
- Queue Workers: Scale background job processing independently
- CDN: Cache static responses
Chapter 12 Summary
Advanced features come later. Don't add them until you need them.
Examples:
- Webhooks: Notify external systems when things happen
- Batch operations: Let clients do multiple things in one request
- Search: Full-text search across resources
- Subscriptions: Real-time updates via WebSockets
- Rate limiting by plan: Different limits for different users
These are the features that make your API feel enterprise-grade. But don't let them distract from the fundamentals. Get the basics right first.