Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/charlietyn/openapi-generator/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers edge cases, extreme scenarios, and troubleshooting strategies for production environments.

Config Caching with Runtime env() Usage

Symptom

Changing .env values (e.g., APP_NAME, APP_URL) does not affect generated documentation output.

Cause

Laravel’s config:cache freezes configuration values. Some package code reads env() directly at runtime instead of relying solely on cached config values. When config is cached, these env() calls return null.

Affected Areas

  • PlaceholderHelper::getProjectName() - Reads APP_NAME via env()
  • EnvironmentGenerator::buildBaseEnvironmentData() - Reads APP_URL via env()

Mitigation

1

Prefer config overrides

Instead of relying on .env values, set config values explicitly in config/openapi.php:
'info' => [
    'title' => 'My API Documentation',  // Instead of env('APP_NAME')
    'version' => '1.0.0',
],

'environments' => [
    'base' => [
        'app_url' => 'https://api.example.com',  // Instead of env('APP_URL')
    ],
],
2

Clear config cache after changes

When changing .env values:
php artisan config:clear
php artisan openapi:generate
3

Avoid config cache in development

Only use config:cache in production. In local/staging, keep config uncached to allow dynamic .env changes.

How to Reproduce

# Cache config
php artisan config:cache

# Change APP_NAME in .env
sed -i 's/APP_NAME=.*/APP_NAME=NewName/' .env

# Generate docs
php artisan openapi:generate

# Observe that title in openapi.json still shows old name
cat storage/app/public/openapi/openapi.json | grep '"title"'

How to Test

# Clear cache
php artisan config:clear

# Regenerate
php artisan openapi:generate

# Verify new values appear
cat storage/app/public/openapi/openapi.json | grep '"title"'
Production ImpactIf you deploy with cached config and rely on env() values, your documentation will show stale or incorrect information. Always set critical values in config files, not .env.

Concurrent Generation Writes to Same Output File

Symptom

Partially written or inconsistent JSON/YAML files in storage/app/public/openapi. Files may contain truncated JSON or mixed content from multiple generations.

Cause

Multiple workers, developers, or CI jobs running openapi:generate simultaneously write to the same output filenames, causing race conditions.

Mitigation

Use --output to write to unique file names per job:
# Include timestamp or job ID
php artisan openapi:generate --output=/tmp/openapi-$(date +%s).json

# In CI: use build number
php artisan openapi:generate --output=/tmp/openapi-build-${CI_BUILD_ID}.json

How to Reproduce

# Terminal 1
php artisan openapi:generate --all &

# Terminal 2 (immediately after)
php artisan openapi:generate --all &

# Wait for both to complete, then inspect output
cat storage/app/public/openapi/openapi.json | jq .
# May show JSON parse errors

How to Test

Add file locking and run the same concurrent test. Verify that one generation waits for the other to complete.

Long-Running Queue Workers and Stale Caches

Symptom

Old or stale documentation when running generation from a long-lived queue worker. New routes or validation rules don’t appear in output.

Cause

The generator caches output when openapi.cache.enabled is true. Long-lived workers reuse cached data even after code changes.

Mitigation

1

Disable cache for queue jobs

class GenerateOpenApiJob implements ShouldQueue
{
    public function handle()
    {
        Artisan::call('openapi:generate', [
            '--all' => true,
            '--no-cache' => true,  // Force fresh generation
        ]);
    }
}
2

Reduce cache TTL

For frequently changing APIs, reduce cache time-to-live:
'cache' => [
    'enabled' => true,
    'ttl' => 300,  // 5 minutes instead of 1 hour
],
3

Clear cache on deployment

Add cache clearing to deployment scripts:
php artisan cache:clear
php artisan config:clear
php artisan openapi:generate --all

How to Reproduce

# Enable cache with long TTL
php artisan config:set openapi.cache.enabled true
php artisan config:set openapi.cache.ttl 3600

# Generate once
php artisan openapi:generate

# Add a new route
echo "Route::get('new-endpoint', fn() => 'test');" >> routes/api.php

# Regenerate (uses cache)
php artisan openapi:generate

# Verify new route is missing from openapi.json

How to Test

# Regenerate with --no-cache
php artisan openapi:generate --no-cache

# Verify new route appears
cat storage/app/public/openapi/openapi.json | grep 'new-endpoint'

Large Route Sets Causing Timeouts

Symptom

HTTP requests to /documentation/openapi.json time out or return 500 errors. Large applications with hundreds of routes cause generation to exceed PHP execution limits.

Cause

On-demand HTTP generation performs heavy introspection:
  • Route extraction
  • FormRequest validation analysis
  • Model schema reflection
  • Template processing
For large apps, this exceeds the default 30-60 second timeout.

Mitigation

Recommended: Generate docs via CLI and serve static files:
# Generate during deployment
php artisan openapi:generate --all

# Symlink to public directory
ln -s storage/app/public/openapi public/docs
Serve from:
https://api.example.com/docs/openapi.json
https://api.example.com/docs/postman-all.json

How to Reproduce

Create a large number of routes:
// routes/api.php
for ($i = 0; $i < 500; $i++) {
    Route::get("test-{$i}", fn() => "test");
}
# Try HTTP generation
curl http://localhost:8000/documentation/openapi.json
# May timeout or return 504 Gateway Timeout

How to Test

Compare response times:
# With filtering
time curl "http://localhost:8000/documentation/openapi.json?api_type=mobile"

# Without filtering
time curl "http://localhost:8000/documentation/openapi.json"
Performance Benchmarks
  • 50 routes: Less than 1 second
  • 200 routes: 2-5 seconds
  • 500+ routes: 10-30 seconds (consider CLI generation)

Multi-Tenant Deployments

Symptom

OpenAPI info title, server URLs, and environments do not reflect tenant-specific settings. All tenants get the same generic documentation.

Cause

Configuration is global. The generator reads app-level config values, not tenant-specific overrides.

Mitigation

1

Generate docs per tenant

foreach (Tenant::all() as $tenant) {
    $tenant->run(function () use ($tenant) {
        // Override config for this tenant
        config([
            'openapi.info.title' => "{$tenant->name} API",
            'openapi.servers' => [
                ['url' => "https://{$tenant->domain}/api"],
            ],
            'openapi.output_path' => storage_path("app/tenants/{$tenant->id}/openapi"),
        ]);
        
        Artisan::call('openapi:generate', ['--all' => true]);
    });
}
2

Store per-tenant output

Use tenant-specific output paths:
'output_path' => storage_path("app/tenants/{$tenantId}/openapi"),
Serve from tenant-specific URLs:
https://tenant1.example.com/docs/openapi.json
https://tenant2.example.com/docs/openapi.json
3

Dynamic HTTP routes

Serve tenant-specific docs via HTTP:
Route::get('documentation/openapi.{format}', function ($format) {
    $tenant = tenant();
    
    $service = app(OpenApiServices::class);
    
    // Override config for current tenant
    config([
        'openapi.info.title' => "{$tenant->name} API",
        'openapi.servers' => [
            ['url' => "https://{$tenant->domain}/api"],
        ],
    ]);
    
    $spec = $service->generate(
        useCache: true,
        apiTypes: null,
        environment: 'production',
        generationType: 'openapi'
    );
    
    return response()->json($spec);
});

How to Reproduce

// Generate for tenant 1
config(['openapi.info.title' => 'Tenant 1 API']);
Artisan::call('openapi:generate');

// Generate for tenant 2 (without override)
Artisan::call('openapi:generate');

// Observe tenant 2 docs show "Tenant 1 API" title

How to Test

// Generate with per-tenant overrides
foreach (['Tenant 1', 'Tenant 2'] as $tenantName) {
    config(['openapi.info.title' => "{$tenantName} API"]);
    Artisan::call('openapi:generate', [
        '--output' => storage_path("app/{$tenantName}/openapi.json"),
    ]);
}

// Verify each file has correct title

Rate Limiting or Auth Middleware Blocking HTTP Docs

Symptom

401/403/429 responses when accessing /documentation/openapi.json.

Cause

The openapi.routes.middleware stack includes authentication, authorization, or rate limiting middleware.

Mitigation

For public documentation:
'routes' => [
    'enabled' => true,
    'prefix' => 'documentation',
    'middleware' => [],  // No auth or throttling
],

How to Reproduce

// config/openapi.php
'routes' => [
    'middleware' => ['auth:sanctum', 'throttle:60,1'],
],
# Without authentication
curl http://localhost:8000/documentation/openapi.json
# {"message":"Unauthenticated."}

How to Test

Remove middleware and verify access:
'routes' => [
    'middleware' => [],
],
curl http://localhost:8000/documentation/openapi.json
# Returns OpenAPI spec

Invalid API Type Filters

Symptom

422 validation error when requesting HTTP documentation with invalid API type.
{
  "message": "The given data was invalid.",
  "errors": {
    "api_type": ["Invalid API types: unknown"]
  }
}

Cause

Requested API type is missing, disabled, or misspelled in openapi.api_types.

Mitigation

1

Verify available types

php artisan tinker
>>> config('openapi.api_types')
Check enabled API types:
[
    'admin' => ['enabled' => true, ...],
    'mobile' => ['enabled' => true, ...],
    'site' => ['enabled' => false, ...],  // Disabled
]
2

Enable the API type

In config/openapi.php:
'api_types' => [
    'site' => [
        'prefix' => 'api',
        'folder_name' => 'API Public',
        'enabled' => true,  // Changed from false
    ],
],
3

Add missing API type

If the type doesn’t exist:
'api_types' => [
    'partner' => [
        'prefix' => 'partner',
        'folder_name' => 'Partner API',
        'enabled' => true,
    ],
],

How to Reproduce

curl "http://localhost:8000/documentation/openapi.json?api_type=unknown"
# {"message":"The given data was invalid.", ...}

How to Test

Add the API type to config and retry:
curl "http://localhost:8000/documentation/openapi.json?api_type=partner"
# Returns filtered OpenAPI spec

Template JSON Parsing Failures

Symptom

Generation fails with JSON template errors:
❌ Failed to generate specification
JSON parse error: Syntax error in /path/to/templates/generic/list.json

Cause

Invalid JSON syntax in resources/openapi/templates or missing required fields.

Mitigation

1

Validate template JSON

Use a JSON validator:
jq . resources/openapi/templates/generic/list.json
Or online: jsonlint.com
2

Enable output validation

In config/openapi-templates.php:
'rendering' => [
    'validate_output' => true,
],
This adds extra validation but slows generation. Use only for debugging.
3

Check for common issues

  • Trailing commas: Not allowed in JSON
  • Unescaped quotes: Must escape " inside strings
  • Missing braces: Ensure all { have matching }

How to Reproduce

Create invalid JSON in a template:
{
  "summary": "List {{entity_plural}}",
  "description": "Missing closing brace"
php artisan openapi:generate
# ❌ Failed to generate specification
# JSON parse error...

How to Test

Fix JSON and regenerate:
{
  "summary": "List {{entity_plural}}",
  "description": "Valid JSON"
}
php artisan openapi:generate
# ✅ OpenAPI specification generated!

General Troubleshooting

Cause: Routes don’t match API type prefixes or are excluded.Solution:
# Check route list
php artisan route:list --path=admin

# Verify API type prefix
config('openapi.api_types.admin.prefix')

# Check exclusion patterns
config('openapi.exclude_routes')
Cause: Template placeholders don’t match available variables.Solution:Available placeholders:
  • {{entity_singular}} - e.g., “user”
  • {{entity_plural}} - e.g., “users”
  • {{api_type}} - e.g., “admin”
Ensure template uses correct placeholder syntax.
Cause: Route doesn’t type-hint FormRequest in controller method.Solution:
// ❌ Rules not extracted
public function store(Request $request) { ... }

// ✅ Rules extracted automatically
public function store(StoreUserRequest $request) { ... }
Cause: Output directory not writable.Solution:
chmod -R 775 storage/app/public/openapi
chown -R www-data:www-data storage/app/public/openapi

Next Steps

API Reference

Explore the programmatic API for custom integrations.

Configuration Reference

Detailed configuration options and defaults.

Build docs developers (and LLMs) love