> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/tighten/ziggy/llms.txt
> Use this file to discover all available pages before exploring further.

# API Endpoint Configuration

> Create an API endpoint to serve Ziggy's route configuration dynamically

Instead of generating a static configuration file, you can create an API endpoint that serves Ziggy's configuration dynamically. This approach is particularly useful for SPAs or applications where the frontend is in a separate repository.

## Overview

Serving Ziggy's configuration from an API endpoint provides several benefits:

* **Always up-to-date**: No need to regenerate static files when routes change
* **Dynamic filtering**: Serve different routes based on authentication or user permissions
* **Simplified deployment**: No need to copy config files between projects
* **Cache control**: Implement custom caching strategies

## Creating the Basic Endpoint

<Steps>
  <Step title="Add the API route">
    Create a route that returns a new `Ziggy` instance as JSON:

    ```php theme={null}
    // routes/api.php

    use Tighten\Ziggy\Ziggy;

    Route::get('ziggy', fn () => response()->json(new Ziggy));
    ```

    This endpoint will be available at `/api/ziggy`.
  </Step>

  <Step title="Test the endpoint">
    Visit the endpoint in your browser or use curl:

    ```bash theme={null}
    curl https://yourapp.com/api/ziggy
    ```

    You should see a JSON response like:

    ```json theme={null}
    {
        "url": "https://yourapp.com",
        "port": null,
        "defaults": {},
        "routes": {
            "home": {
                "uri": "/",
                "methods": ["GET", "HEAD"],
                "domain": null
            },
            "posts.index": {
                "uri": "posts",
                "methods": ["GET", "HEAD"],
                "domain": null
            }
        }
    }
    ```
  </Step>

  <Step title="Fetch from your frontend">
    Fetch the configuration when your application initializes:

    ```js theme={null}
    import { route } from 'ziggy-js';

    let ziggyConfig = null;

    async function loadZiggyConfig() {
        const response = await fetch('https://yourapp.com/api/ziggy');
        ziggyConfig = await response.json();
    }

    // Call this when your app starts
    await loadZiggyConfig();

    // Now you can use the route function
    const url = route('posts.show', 1, undefined, ziggyConfig);
    ```
  </Step>
</Steps>

## Advanced Configurations

### Filtering Routes

Serve only specific routes to your frontend:

```php theme={null}
// routes/api.php

use Tighten\Ziggy\Ziggy;

Route::get('ziggy', function () {
    return response()->json(
        new Ziggy(only: ['api.*', 'posts.*'])
    );
});
```

Or exclude certain routes:

```php theme={null}
Route::get('ziggy', function () {
    return response()->json(
        new Ziggy(except: ['admin.*', '_debugbar.*'])
    );
});
```

### Using Groups

Serve different route groups based on user permissions:

```php theme={null}
Route::get('ziggy', function () {
    $groups = ['public'];

    if (auth()->check()) {
        $groups[] = 'authenticated';
    }

    if (auth()->user()?->isAdmin()) {
        $groups[] = 'admin';
    }

    return response()->json(
        new Ziggy(group: $groups)
    );
});
```

Define groups in `config/ziggy.php`:

```php theme={null}
// config/ziggy.php

return [
    'groups' => [
        'public' => ['home', 'posts.index', 'posts.show'],
        'authenticated' => ['dashboard', 'profile.*'],
        'admin' => ['admin.*', 'users.*'],
    ],
];
```

### Adding Cache Headers

Implement caching to reduce server load:

```php theme={null}
Route::get('ziggy', function () {
    $ziggy = new Ziggy;

    return response()
        ->json($ziggy)
        ->header('Cache-Control', 'public, max-age=3600') // Cache for 1 hour
        ->header('ETag', md5(json_encode($ziggy)));
});
```

### Authenticated Endpoint

Protect the endpoint with authentication:

```php theme={null}
Route::middleware('auth:sanctum')->get('ziggy', function () {
    return response()->json(
        new Ziggy(only: ['api.*'])
    );
});
```

## Frontend Integration

### React Hook Example

Create a custom hook to fetch and manage Ziggy config:

```jsx theme={null}
import { useState, useEffect } from 'react';
import { route as ziggyRoute } from 'ziggy-js';

export function useZiggy() {
    const [config, setConfig] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        fetch('/api/ziggy')
            .then(res => res.json())
            .then(data => {
                setConfig(data);
                setLoading(false);
            })
            .catch(err => {
                setError(err);
                setLoading(false);
            });
    }, []);

    const route = (name, params) => {
        if (!config) return '#';
        return ziggyRoute(name, params, undefined, config);
    };

    return { route, loading, error };
}
```

Use in your components:

```jsx theme={null}
import { useZiggy } from './hooks/useZiggy';

function PostsList() {
    const { route, loading } = useZiggy();

    if (loading) return <div>Loading...</div>;

    return (
        <div>
            <a href={route('posts.index')}>All Posts</a>
            <a href={route('posts.create')}>Create Post</a>
        </div>
    );
}
```

### Vue Composable Example

```js theme={null}
// composables/useZiggy.js
import { ref, onMounted } from 'vue';
import { route as ziggyRoute } from 'ziggy-js';

const config = ref(null);
const loading = ref(true);

export function useZiggy() {
    onMounted(async () => {
        if (config.value) return; // Already loaded

        try {
            const response = await fetch('/api/ziggy');
            config.value = await response.json();
        } catch (error) {
            console.error('Failed to load Ziggy config:', error);
        } finally {
            loading.value = false;
        }
    });

    const route = (name, params) => {
        if (!config.value) return '#';
        return ziggyRoute(name, params, undefined, config.value);
    };

    return { route, loading };
}
```

### Global Configuration Strategy

Load the config once and make it globally available:

```js theme={null}
// app.js
import { Ziggy } from 'ziggy-js';

async function initializeApp() {
    // Fetch Ziggy config
    const response = await fetch('/api/ziggy');
    const config = await response.json();

    // Make it globally available
    window.Ziggy = config;

    // Now initialize your app
    // ...
}

initializeApp();
```

Then use it anywhere without passing config:

```js theme={null}
import { route } from 'ziggy-js';

// Config is automatically picked up from window.Ziggy
const url = route('posts.show', 1);
```

## Performance Optimization

### Server-Side Caching

Cache the Ziggy config to avoid regenerating it on every request:

```php theme={null}
use Illuminate\Support\Facades\Cache;
use Tighten\Ziggy\Ziggy;

Route::get('ziggy', function () {
    $config = Cache::remember('ziggy-config', 3600, function () {
        return new Ziggy;
    });

    return response()->json($config);
});
```

Invalidate the cache when routes change:

```php theme={null}
// In your deployment script or AppServiceProvider
Cache::forget('ziggy-config');
```

### Client-Side Caching

Cache the config in localStorage:

```js theme={null}
async function loadZiggyConfig() {
    // Try to load from cache
    const cached = localStorage.getItem('ziggy-config');
    const cacheTime = localStorage.getItem('ziggy-config-time');

    // Use cache if less than 1 hour old
    if (cached && cacheTime && Date.now() - parseInt(cacheTime) < 3600000) {
        return JSON.parse(cached);
    }

    // Fetch fresh config
    const response = await fetch('/api/ziggy');
    const config = await response.json();

    // Store in cache
    localStorage.setItem('ziggy-config', JSON.stringify(config));
    localStorage.setItem('ziggy-config-time', Date.now().toString());

    return config;
}
```

### ETags for Cache Validation

```php theme={null}
Route::get('ziggy', function () {
    $ziggy = new Ziggy;
    $etag = md5(json_encode($ziggy));

    if (request()->header('If-None-Match') === $etag) {
        return response('', 304); // Not Modified
    }

    return response()
        ->json($ziggy)
        ->header('ETag', $etag)
        ->header('Cache-Control', 'must-revalidate');
});
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="404 Not Found">
    Ensure the route is properly defined:

    ```bash theme={null}
    php artisan route:list --path=ziggy
    ```

    Verify the route appears in the list. If not, clear the route cache:

    ```bash theme={null}
    php artisan route:clear
    ```
  </Accordion>

  <Accordion title="CORS Errors">
    If your frontend is on a different domain, configure CORS:

    ```php theme={null}
    // config/cors.php

    return [
        'paths' => ['api/*'],
        'allowed_origins' => ['https://yourfrontend.com'],
        'allowed_methods' => ['GET'],
        'allowed_headers' => ['*'],
    ];
    ```
  </Accordion>

  <Accordion title="Empty Routes Object">
    Check your route filtering configuration:

    ```php theme={null}
    // Make sure filters aren't too restrictive
    return response()->json(
        (new Ziggy)->toArray() // Debug: see all properties
    );
    ```
  </Accordion>

  <Accordion title="Authentication Required">
    If using Sanctum or other authentication:

    ```js theme={null}
    const response = await fetch('/api/ziggy', {
        headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json',
        },
    });
    ```
  </Accordion>
</AccordionGroup>

## Security Considerations

* **Filter sensitive routes**: Never expose admin or internal routes to public endpoints
* **Rate limiting**: Apply rate limiting to prevent abuse
  ```php theme={null}
  Route::middleware('throttle:60,1')->get('ziggy', /* ... */);
  ```
* **Authentication**: Consider requiring authentication for production apps
* **CORS**: Only allow trusted origins in production

## Best Practices

1. **Cache aggressively**: Route configurations rarely change, so cache liberally
2. **Use groups**: Filter routes appropriately for different user types
3. **Version your endpoint**: Consider versioning (e.g., `/api/v1/ziggy`) for future changes
4. **Monitor performance**: Track endpoint response times and cache hit rates
5. **Handle errors gracefully**: Provide fallbacks if config loading fails
