Skip to content

Examples

Darius Matulionis edited this page Jan 7, 2026 · 1 revision

L5-Swagger Examples

This page provides comprehensive examples for documenting your Laravel API using OpenAPI 3 with PHP 8 Attributes.

Note: All examples use PHP 8 Attributes syntax. If you're using L5-Swagger v9.x or earlier with DocBlock annotations, see the Migration Guide.

Table of Contents


Basic Setup

First, ensure you have the OpenAPI Attributes namespace imported at the top of your files:

<?php

namespace App\Http\Controllers;

use OpenApi\Attributes as OA;

API Information

Define your API's basic information. This typically goes in your base Controller.php or a dedicated info class:

<?php

namespace App\Http\Controllers;

use OpenApi\Attributes as OA;

#[OA\Info(
    version: "1.0.0",
    title: "My Application API",
    description: "API documentation for My Application",
    contact: new OA\Contact(
        name: "API Support",
        email: "[email protected]",
        url: "https://example.com/support"
    ),
    license: new OA\License(
        name: "Apache 2.0",
        url: "https://www.apache.org/licenses/LICENSE-2.0.html"
    )
)]
#[OA\Server(
    url: "http://localhost:8000",
    description: "Local development server"
)]
#[OA\Server(
    url: "https://api.example.com",
    description: "Production server"
)]
class Controller
{
    // Your controller code
}

Routes/Endpoints

GET Request

Simple GET endpoint returning a list:

#[OA\Get(
    path: "/api/users",
    summary: "Get list of users",
    description: "Returns a paginated list of all users",
    tags: ["Users"],
    responses: [
        new OA\Response(
            response: 200,
            description: "Successful operation",
            content: new OA\JsonContent(
                type: "array",
                items: new OA\Items(ref: "#/components/schemas/User")
            )
        ),
        new OA\Response(
            response: 401,
            description: "Unauthenticated"
        )
    ]
)]
public function index()
{
    // Your code
}

GET endpoint with path parameter:

#[OA\Get(
    path: "/api/users/{id}",
    summary: "Get user by ID",
    description: "Returns a single user",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "id",
            description: "User ID",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer")
        )
    ],
    responses: [
        new OA\Response(
            response: 200,
            description: "Successful operation",
            content: new OA\JsonContent(ref: "#/components/schemas/User")
        ),
        new OA\Response(
            response: 404,
            description: "User not found"
        )
    ]
)]
public function show($id)
{
    // Your code
}

POST Request

Create a new resource:

#[OA\Post(
    path: "/api/users",
    summary: "Create a new user",
    description: "Creates a new user and returns the created user object",
    tags: ["Users"],
    requestBody: new OA\RequestBody(
        required: true,
        description: "User object that needs to be created",
        content: new OA\JsonContent(
            required: ["name", "email", "password"],
            properties: [
                new OA\Property(property: "name", type: "string", example: "John Doe"),
                new OA\Property(property: "email", type: "string", format: "email", example: "[email protected]"),
                new OA\Property(property: "password", type: "string", format: "password", example: "secret123")
            ]
        )
    ),
    responses: [
        new OA\Response(
            response: 201,
            description: "User created successfully",
            content: new OA\JsonContent(ref: "#/components/schemas/User")
        ),
        new OA\Response(
            response: 422,
            description: "Validation error"
        )
    ]
)]
public function store(Request $request)
{
    // Your code
}

PUT Request

Update an existing resource:

#[OA\Put(
    path: "/api/users/{id}",
    summary: "Update an existing user",
    description: "Updates user data",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "id",
            description: "User ID",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer")
        )
    ],
    requestBody: new OA\RequestBody(
        required: true,
        content: new OA\JsonContent(
            properties: [
                new OA\Property(property: "name", type: "string", example: "Jane Doe"),
                new OA\Property(property: "email", type: "string", format: "email", example: "[email protected]")
            ]
        )
    ),
    responses: [
        new OA\Response(
            response: 200,
            description: "User updated successfully",
            content: new OA\JsonContent(ref: "#/components/schemas/User")
        ),
        new OA\Response(
            response: 404,
            description: "User not found"
        ),
        new OA\Response(
            response: 422,
            description: "Validation error"
        )
    ]
)]
public function update(Request $request, $id)
{
    // Your code
}

DELETE Request

Delete a resource:

#[OA\Delete(
    path: "/api/users/{id}",
    summary: "Delete a user",
    description: "Deletes a user by ID",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "id",
            description: "User ID",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer")
        )
    ],
    responses: [
        new OA\Response(
            response: 204,
            description: "User deleted successfully"
        ),
        new OA\Response(
            response: 404,
            description: "User not found"
        )
    ]
)]
public function destroy($id)
{
    // Your code
}

Parameters

Path Parameters

Parameters that are part of the URL path:

#[OA\Get(
    path: "/api/posts/{postId}/comments/{commentId}",
    summary: "Get a specific comment on a post",
    tags: ["Comments"],
    parameters: [
        new OA\Parameter(
            name: "postId",
            description: "ID of the post",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer", format: "int64")
        ),
        new OA\Parameter(
            name: "commentId",
            description: "ID of the comment",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer", format: "int64")
        )
    ],
    responses: [
        new OA\Response(response: 200, description: "Success")
    ]
)]
public function getComment($postId, $commentId)
{
    // Your code
}

Query Parameters

Parameters passed in the query string:

#[OA\Get(
    path: "/api/users",
    summary: "Get users with filtering and pagination",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "page",
            description: "Page number",
            in: "query",
            required: false,
            schema: new OA\Schema(type: "integer", default: 1)
        ),
        new OA\Parameter(
            name: "per_page",
            description: "Items per page",
            in: "query",
            required: false,
            schema: new OA\Schema(type: "integer", default: 15, maximum: 100)
        ),
        new OA\Parameter(
            name: "status",
            description: "Filter by status",
            in: "query",
            required: false,
            schema: new OA\Schema(type: "string", enum: ["active", "inactive", "pending"])
        ),
        new OA\Parameter(
            name: "search",
            description: "Search term",
            in: "query",
            required: false,
            schema: new OA\Schema(type: "string")
        )
    ],
    responses: [
        new OA\Response(response: 200, description: "Success")
    ]
)]
public function index(Request $request)
{
    // Your code
}

Header Parameters

Custom headers:

#[OA\Get(
    path: "/api/users",
    summary: "Get users",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "X-Api-Version",
            description: "API version",
            in: "header",
            required: false,
            schema: new OA\Schema(type: "string", default: "v1")
        ),
        new OA\Parameter(
            name: "Accept-Language",
            description: "Preferred language",
            in: "header",
            required: false,
            schema: new OA\Schema(type: "string", enum: ["en", "es", "fr"], default: "en")
        )
    ],
    responses: [
        new OA\Response(response: 200, description: "Success")
    ]
)]
public function index(Request $request)
{
    // Your code
}

Request Bodies

Simple JSON Request Body

#[OA\Post(
    path: "/api/posts",
    summary: "Create a new post",
    tags: ["Posts"],
    requestBody: new OA\RequestBody(
        required: true,
        content: new OA\JsonContent(
            required: ["title", "content"],
            properties: [
                new OA\Property(property: "title", type: "string", example: "My First Post"),
                new OA\Property(property: "content", type: "string", example: "This is the post content"),
                new OA\Property(property: "published", type: "boolean", example: false)
            ]
        )
    ),
    responses: [
        new OA\Response(response: 201, description: "Post created")
    ]
)]
public function store(Request $request)
{
    // Your code
}

Request Body with Schema Reference

#[OA\Post(
    path: "/api/posts",
    summary: "Create a new post",
    tags: ["Posts"],
    requestBody: new OA\RequestBody(
        required: true,
        description: "Post object",
        content: new OA\JsonContent(ref: "#/components/schemas/PostRequest")
    ),
    responses: [
        new OA\Response(response: 201, description: "Post created")
    ]
)]
public function store(Request $request)
{
    // Your code
}

File Upload

#[OA\Post(
    path: "/api/users/{id}/avatar",
    summary: "Upload user avatar",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "id",
            in: "path",
            required: true,
            schema: new OA\Schema(type: "integer")
        )
    ],
    requestBody: new OA\RequestBody(
        required: true,
        content: new OA\MediaType(
            mediaType: "multipart/form-data",
            schema: new OA\Schema(
                required: ["avatar"],
                properties: [
                    new OA\Property(
                        property: "avatar",
                        type: "string",
                        format: "binary",
                        description: "Avatar image file"
                    )
                ]
            )
        )
    ),
    responses: [
        new OA\Response(response: 200, description: "Avatar uploaded successfully")
    ]
)]
public function uploadAvatar(Request $request, $id)
{
    // Your code
}

Responses

Multiple Response Examples

#[OA\Get(
    path: "/api/users/{id}",
    summary: "Get user by ID",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(
            name: "id",
            in: "path",
            required: true,
            examples: [
                new OA\Examples(
                    example: "int",
                    summary: "Integer ID",
                    value: 1
                ),
                new OA\Examples(
                    example: "uuid",
                    summary: "UUID",
                    value: "0006faf6-7a61-426c-9034-579f2cfcfa83"
                )
            ],
            schema: new OA\Schema(
                oneOf: [
                    new OA\Schema(type: "integer"),
                    new OA\Schema(type: "string", format: "uuid")
                ]
            )
        )
    ],
    responses: [
        new OA\Response(
            response: 200,
            description: "Success",
            content: new OA\JsonContent(
                examples: [
                    new OA\Examples(
                        example: "user",
                        summary: "User object",
                        value: [
                            "id" => 1,
                            "name" => "John Doe",
                            "email" => "[email protected]"
                        ]
                    )
                ]
            )
        ),
        new OA\Response(response: 404, description: "User not found")
    ]
)]
public function show($id)
{
    // Your code
}

Response with Multiple Content Types

#[OA\Get(
    path: "/api/users/{id}",
    summary: "Get user",
    tags: ["Users"],
    parameters: [
        new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer"))
    ],
    responses: [
        new OA\Response(
            response: 200,
            description: "Success",
            content: [
                new OA\JsonContent(ref: "#/components/schemas/User"),
                new OA\XmlContent(ref: "#/components/schemas/User")
            ]
        )
    ]
)]
public function show($id)
{
    // Your code
}

Flexible Response Types (oneOf)

#[OA\Post(
    path: "/api/process",
    summary: "Process data",
    tags: ["Processing"],
    requestBody: new OA\RequestBody(
        content: new OA\JsonContent(
            properties: [
                new OA\Property(
                    property: "phone",
                    oneOf: [
                        new OA\Schema(type: "string"),
                        new OA\Schema(type: "integer")
                    ],
                    example: "12345678"
                )
            ]
        )
    ),
    responses: [
        new OA\Response(
            response: 200,
            description: "Success",
            content: new OA\JsonContent(
                oneOf: [
                    new OA\Schema(ref: "#/components/schemas/SuccessResult"),
                    new OA\Schema(type: "boolean")
                ],
                examples: [
                    new OA\Examples(
                        example: "result",
                        summary: "Result object",
                        value: ["success" => true]
                    ),
                    new OA\Examples(
                        example: "boolean",
                        summary: "Boolean value",
                        value: false
                    )
                ]
            )
        )
    ]
)]
public function process(Request $request)
{
    // Your code
}

Schemas/Models

Define reusable schemas for your data models. These are typically defined on your Model classes or dedicated DTO classes:

Basic Schema

<?php

namespace App\Models;

use OpenApi\Attributes as OA;

#[OA\Schema(
    schema: "User",
    title: "User",
    description: "User model",
    required: ["id", "name", "email"],
    properties: [
        new OA\Property(property: "id", type: "integer", example: 1),
        new OA\Property(property: "name", type: "string", example: "John Doe"),
        new OA\Property(property: "email", type: "string", format: "email", example: "[email protected]"),
        new OA\Property(property: "created_at", type: "string", format: "date-time", example: "2024-01-01T00:00:00Z"),
        new OA\Property(property: "updated_at", type: "string", format: "date-time", example: "2024-01-01T00:00:00Z")
    ]
)]
class User
{
    // Your model code
}

Schema with Nested Objects

#[OA\Schema(
    schema: "Post",
    required: ["id", "title", "author"],
    properties: [
        new OA\Property(property: "id", type: "integer", example: 1),
        new OA\Property(property: "title", type: "string", example: "My Post"),
        new OA\Property(property: "content", type: "string", example: "Post content here"),
        new OA\Property(
            property: "author",
            ref: "#/components/schemas/User",
            description: "Post author"
        ),
        new OA\Property(
            property: "tags",
            type: "array",
            items: new OA\Items(type: "string"),
            example: ["laravel", "php"]
        ),
        new OA\Property(
            property: "comments",
            type: "array",
            items: new OA\Items(ref: "#/components/schemas/Comment")
        )
    ]
)]
class Post
{
    // Your model code
}

Schema with Enums

#[OA\Schema(
    schema: "Order",
    required: ["id", "status"],
    properties: [
        new OA\Property(property: "id", type: "integer", example: 1),
        new OA\Property(
            property: "status",
            type: "string",
            enum: ["pending", "processing", "completed", "cancelled"],
            example: "pending"
        ),
        new OA\Property(property: "total", type: "number", format: "float", example: 99.99)
    ]
)]
class Order
{
    // Your model code
}

Request Schema

#[OA\Schema(
    schema: "PostRequest",
    required: ["title", "content"],
    properties: [
        new OA\Property(property: "title", type: "string", maxLength: 255, example: "My Post Title"),
        new OA\Property(property: "content", type: "string", example: "Post content"),
        new OA\Property(property: "published", type: "boolean", default: false),
        new OA\Property(property: "category_id", type: "integer", nullable: true, example: 1)
    ]
)]
class PostRequest
{
    // Your DTO/Request class
}

Polymorphic Schema (anyOf/oneOf)

#[OA\Schema(
    schema: "Pet",
    required: ["name", "type"],
    properties: [
        new OA\Property(property: "name", type: "string", example: "Buddy"),
        new OA\Property(property: "type", type: "string", enum: ["dog", "cat"])
    ],
    discriminator: new OA\Discriminator(propertyName: "type")
)]
class Pet {}

#[OA\Schema(
    schema: "Dog",
    allOf: [
        new OA\Schema(ref: "#/components/schemas/Pet"),
        new OA\Schema(
            properties: [
                new OA\Property(property: "breed", type: "string", example: "Golden Retriever")
            ]
        )
    ]
)]
class Dog extends Pet {}

#[OA\Schema(
    schema: "Cat",
    allOf: [
        new OA\Schema(ref: "#/components/schemas/Pet"),
        new OA\Schema(
            properties: [
                new OA\Property(property: "indoor", type: "boolean", example: true)
            ]
        )
    ]
)]
class Cat extends Pet {}

Security

API Key Authentication

Define the security scheme in your base controller:

#[OA\SecurityScheme(
    securityScheme: "apiKey",
    type: "apiKey",
    description: "API Key Authentication",
    name: "X-API-Key",
    in: "header"
)]
class Controller
{
    // Your controller code
}

Apply it to endpoints:

#[OA\Get(
    path: "/api/users",
    summary: "Get users",
    tags: ["Users"],
    security: [["apiKey" => []]],
    responses: [
        new OA\Response(response: 200, description: "Success")
    ]
)]
public function index()
{
    // Your code
}

Bearer Token (JWT)

#[OA\SecurityScheme(
    securityScheme: "bearerAuth",
    type: "http",
    description: "Enter JWT Bearer token",
    scheme: "bearer",
    bearerFormat: "JWT"
)]
class Controller {}

Usage:

#[OA\Get(
    path: "/api/profile",
    summary: "Get user profile",
    tags: ["Profile"],
    security: [["bearerAuth" => []]],
    responses: [
        new OA\Response(response: 200, description: "Success"),
        new OA\Response(response: 401, description: "Unauthenticated")
    ]
)]
public function profile()
{
    // Your code
}

OAuth2 Authentication

#[OA\SecurityScheme(
    securityScheme: "oauth2",
    type: "oauth2",
    description: "OAuth2 password grant",
    flows: [
        new OA\Flow(
            flow: "password",
            tokenUrl: "/oauth/token",
            refreshUrl: "/oauth/token/refresh",
            scopes: [
                "read:users" => "Read user information",
                "write:users" => "Modify user information",
                "admin" => "Administrative access"
            ]
        )
    ]
)]
class Controller {}

Usage with specific scopes:

#[OA\Put(
    path: "/api/users/{id}",
    summary: "Update user",
    tags: ["Users"],
    security: [["oauth2" => ["write:users"]]],
    parameters: [
        new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer"))
    ],
    responses: [
        new OA\Response(response: 200, description: "Success"),
        new OA\Response(response: 403, description: "Forbidden")
    ]
)]
public function update($id)
{
    // Your code
}

Laravel Sanctum

#[OA\SecurityScheme(
    securityScheme: "sanctum",
    type: "apiKey",
    description: "Enter token in format (Bearer <token>)",
    name: "Authorization",
    in: "header"
)]
class Controller {}

Advanced Examples

Pagination Response

#[OA\Schema(
    schema: "PaginatedUsers",
    properties: [
        new OA\Property(
            property: "data",
            type: "array",
            items: new OA\Items(ref: "#/components/schemas/User")
        ),
        new OA\Property(
            property: "meta",
            properties: [
                new OA\Property(property: "current_page", type: "integer", example: 1),
                new OA\Property(property: "last_page", type: "integer", example: 10),
                new OA\Property(property: "per_page", type: "integer", example: 15),
                new OA\Property(property: "total", type: "integer", example: 150)
            ],
            type: "object"
        ),
        new OA\Property(
            property: "links",
            properties: [
                new OA\Property(property: "first", type: "string", example: "http://api.example.com/users?page=1"),
                new OA\Property(property: "last", type: "string", example: "http://api.example.com/users?page=10"),
                new OA\Property(property: "prev", type: "string", nullable: true, example: null),
                new OA\Property(property: "next", type: "string", example: "http://api.example.com/users?page=2")
            ],
            type: "object"
        )
    ]
)]
class PaginatedUsers {}

Error Response Schema

#[OA\Schema(
    schema: "Error",
    properties: [
        new OA\Property(property: "message", type: "string", example: "The given data was invalid."),
        new OA\Property(
            property: "errors",
            type: "object",
            example: [
                "email" => ["The email field is required."],
                "password" => ["The password must be at least 8 characters."]
            ]
        )
    ]
)]
class Error {}

Tags

Group your endpoints with tags:

#[OA\Tag(
    name: "Users",
    description: "User management endpoints"
)]
#[OA\Tag(
    name: "Posts",
    description: "Blog post management",
    externalDocs: new OA\ExternalDocumentation(
        description: "Find out more",
        url: "https://example.com/docs/posts"
    )
)]
class Controller {}

Custom Headers in Response

#[OA\Get(
    path: "/api/download/{fileId}",
    summary: "Download file",
    tags: ["Files"],
    parameters: [
        new OA\Parameter(name: "fileId", in: "path", required: true, schema: new OA\Schema(type: "string"))
    ],
    responses: [
        new OA\Response(
            response: 200,
            description: "File content",
            headers: [
                new OA\Header(
                    header: "Content-Disposition",
                    description: "Attachment filename",
                    schema: new OA\Schema(type: "string"),
                    example: "attachment; filename=document.pdf"
                ),
                new OA\Header(
                    header: "Content-Type",
                    schema: new OA\Schema(type: "string"),
                    example: "application/pdf"
                )
            ],
            content: new OA\MediaType(
                mediaType: "application/octet-stream",
                schema: new OA\Schema(type: "string", format: "binary")
            )
        )
    ]
)]
public function download($fileId)
{
    // Your code
}

Additional Resources


Tips

  1. Use Schema References: Define schemas once and reference them with ref: "#/components/schemas/SchemaName" to avoid repetition.

  2. Organize by Tags: Use tags to group related endpoints for better documentation structure.

  3. Be Specific with Types: Always specify property types, formats, and examples for better API documentation.

  4. Document All Responses: Include all possible response codes (200, 400, 401, 404, 422, 500, etc.).

  5. Use Examples: Provide realistic examples for all parameters, request bodies, and responses.

  6. Security: Document all security requirements at both the global and endpoint levels.

  7. Validation Rules: Mirror your Laravel validation rules in the OpenAPI schema (required, maxLength, pattern, etc.).

  8. Keep It DRY: Use schema references and avoid duplicating definitions.