-
Notifications
You must be signed in to change notification settings - Fork 0
Auto Resolve
The package automatically detects and resolves request/response structures from your code.
When no ApiBody attribute is provided, the package extracts fields from the FormRequest's rules() method.
// app/Http/Requests/CreateUserRequest.php
class CreateUserRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
'age' => 'nullable|integer|min:18',
'phone' => 'required|string',
'avatar_url' => 'nullable|url',
'is_active' => 'boolean',
];
}
}
// app/Http/Controllers/UserController.php
class UserController extends Controller
{
// Body auto-resolved from CreateUserRequest
public function store(CreateUserRequest $request): JsonResponse
{
// ...
}
}{
"name": "Example Name",
"email": "user@example.com",
"password": "password123",
"age": 25,
"phone": "+905551234567",
"avatar_url": "https://example.com/avatar.jpg",
"is_active": true
}| Field Pattern | Generated Value |
|---|---|
*_id, id
|
1 |
*uuid* |
550e8400-e29b-41d4-a716-446655440000 |
*email* |
user@example.com |
*phone* |
+905551234567 |
*name* |
Example Name |
*url*, *link*
|
https://example.com |
*image*, *avatar*, *photo*
|
https://example.com/image.jpg |
*token* |
example_token_string |
*password* |
password123 |
*_at, *date*, *time*
|
2024-01-15T10:30:00Z |
is_*, has_*, can_*
|
true |
*status* |
active |
*type* |
default |
*count*, *total*, *amount*
|
10 |
*price*, *cost*, *fee*
|
99.99 |
Fields ending in s (plurals) |
[] |
| Other | example_value |
Use merge: true to combine auto-resolved fields with custom values:
#[ApiBody(
data: ['status' => 'pending', 'priority' => 'high'],
merge: true,
except: ['created_at', 'updated_at']
)]
public function store(CreateOrderRequest $request): JsonResponseResult:
- Auto-resolve fields from
CreateOrderRequest - Remove
created_atandupdated_at - Override/add
statusandpriorityfromdata
For GET and DELETE requests, the package automatically extracts query parameters from the FormRequest's rules() method.
// app/Http/Requests/ListUsersRequest.php
class ListUsersRequest extends FormRequest
{
public function rules(): array
{
return [
'page' => 'nullable|integer|min:1',
'per_page' => 'nullable|integer|min:1|max:100',
'search' => 'nullable|string|max:255',
'status' => 'nullable|in:active,inactive,pending',
'sort_by' => 'nullable|string',
];
}
}
// app/Http/Controllers/UserController.php
class UserController extends Controller
{
// Query params auto-resolved from ListUsersRequest
public function index(ListUsersRequest $request): JsonResponse
{
// ...
}
}| Key | Value | Description | Disabled |
|---|---|---|---|
page |
1 |
Optional | true |
per_page |
10 |
Optional | true |
search |
search term |
Optional | true |
status |
active |
Optional | true |
sort_by |
value |
Optional | true |
| Field Pattern | Generated Value |
|---|---|
page |
1 |
per_page, limit
|
10 |
*_id, id, count, quantity
|
1 |
query, search, q
|
search term |
*email* |
user@example.com |
*phone* |
+905551234567 |
*name* |
John Doe |
*token* |
abc123token |
*uuid* |
550e8400-e29b-41d4-a716-446655440000 |
*lat*, *latitude*
|
41.0082 |
*lng*, *lon*, *longitude*
|
28.9784 |
*price*, *amount*, *cost*
|
99.99 |
boolean rule |
true |
integer rule |
1 |
date rule |
2024-01-15 |
in:opt1,opt2 rule |
First option (opt1) |
| Other | value |
If you define ApiQueryParam attributes manually, auto-resolution is skipped:
#[ApiQueryParam('page', '1', description: 'Page number')]
#[ApiQueryParam('limit', '25', description: 'Items per page')]
public function index(ListUsersRequest $request): JsonResponseThe package auto-detects return statements and resolves response structure from Resource classes.
// Direct Resource return
return new UserResource($user);
return UserResource::make($user);
return UserResource::collection($users);
// With response helper
return response()->json(['key' => 'value']);// app/Http/Resources/UserResource.php
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'avatar_url' => $this->avatar_url,
'is_verified' => $this->is_verified,
'created_at' => $this->created_at->toIso8601String(),
];
}
}
// Controller
public function show(User $user): JsonResponse
{
return new UserResource($user); // Auto-detected
}{
"id": 1,
"name": "Example Name",
"email": "user@example.com",
"phone": "+905551234567",
"avatar_url": "https://example.com/image.jpg",
"is_verified": true,
"created_at": "2024-01-15T10:30:00Z"
}Nested resources are resolved recursively:
// OrderResource.php
class OrderResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'status' => $this->status,
'customer' => CustomerResource::make($this->customer),
'items' => OrderItemResource::collection($this->items),
];
}
}Generated:
{
"id": 1,
"status": "active",
"customer": {
"id": 1,
"name": "Example Name",
"email": "user@example.com"
},
"items": []
}$this->when() conditions are also analyzed:
return [
'id' => $this->id,
'secret' => $this->when($this->isAdmin(), $this->secret),
'profile' => $this->when($this->profile, ProfileResource::make($this->profile)),
];For explicit control:
#[ApiResource(UserResource::class)]
public function show(User $user): JsonResponse
#[ApiResource(UserResource::class, wrapped: true, status: 200)]
public function profile(): JsonResponseResource resolution also works in YAML definitions via the resource field:
requests:
- name: Get User
method: GET
uri: /v1/users/{id}
resource: App\Http\Resources\UserResource
resource_status: 200
resource_wrapped: true
- name: List Users
method: GET
uri: /v1/users
resource: App\Http\Resources\UserResource
resource_collection: true| Field | Type | Default | Description |
|---|---|---|---|
resource |
string |
null |
Fully qualified Resource class name |
resource_status |
int |
200 |
HTTP status code for the response |
resource_wrapped |
bool |
auto-detected | Wrap in standard API response format |
resource_collection |
bool |
auto-detected | Return as array/collection |
The package detects authentication requirements from middleware.
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/profile', [UserController::class, 'profile']);
});
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});Both auth:sanctum and auth middleware trigger automatic Bearer token authentication in Postman.
#[ApiAuth(type: 'noauth')] // Explicitly no auth
public function publicEndpoint(): JsonResponse
#[ApiAuth(type: 'apikey', apiKey: '{{API_KEY}}')] // Different auth type
public function webhookEndpoint(): JsonResponseURI parameters are automatically converted to Postman path variables.
Route::get('/users/{user}', [UserController::class, 'show']);
Route::get('/orders/{order}/items/{item}', [OrderController::class, 'showItem']);
Route::get('/posts/{post?}', [PostController::class, 'show']); // Optional{{API_URL}}/users/:user
{{API_URL}}/orders/:order/items/:item
{{API_URL}}/posts/:post
Path variables appear in Postman's URL params section for easy editing.
For POST/PUT/PATCH requests, the package converts Laravel validation rules to proper OpenAPI schema definitions. This produces more accurate schemas than simple data inference.
class CreateUserRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required|string|min:8|max:64',
'age' => 'nullable|integer|min:0|max:150',
'role' => 'required|in:admin,editor,viewer',
'avatar' => 'required|image',
'website' => 'url',
];
}
}type: object
required:
- email
- password
- role
- avatar
properties:
email:
type: string
format: email
example: user@example.com
password:
type: string
format: password
minLength: 8
maxLength: 64
example: password123
age:
type: integer
nullable: true
minimum: 0
maximum: 150
example: 25
role:
type: string
enum: [admin, editor, viewer]
example: admin
avatar:
type: string
format: binary
example: (binary)
website:
type: string
format: uri
example: https://example.com| Laravel Rule | OpenAPI Schema |
|---|---|
string |
type: string |
integer, int
|
type: integer |
numeric |
type: number |
boolean, bool
|
type: boolean |
array |
type: array |
email |
format: email |
url, active_url
|
format: uri |
uuid |
format: uuid |
date |
format: date |
date_format (with time) |
format: date-time |
ip, ipv4
|
format: ipv4 |
ipv6 |
format: ipv6 |
file, image
|
format: binary |
mimes:, mimetypes:
|
format: binary |
required |
added to required array |
nullable |
nullable: true |
min:N |
minLength (string) / minimum (number) |
max:N |
maxLength (string) / maximum (number) |
between:min,max |
both min and max constraints |
size:N |
exact size constraint |
in:a,b,c |
enum: [a, b, c] |
regex:/pattern/ |
pattern: pattern |
digits:N |
pattern: ^\d{N}$ |
digits_between:N,M |
pattern: ^\d{N,M}$ |
| password field name | format: password |
PHP BackedEnum classes used in validation rules are automatically converted to OpenAPI enum definitions.
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rule;
// String-based in: rule
'status' => 'required|in:pending,active,completed'
// Rule::in() with values
'role' => ['required', Rule::in(['admin', 'editor', 'viewer'])]
// PHP Enum with Enum rule
'status' => ['required', new Enum(OrderStatus::class)]enum OrderStatus: string
{
case Pending = 'pending';
case Active = 'active';
case Completed = 'completed';
}
// In FormRequest
'status' => ['required', new Enum(OrderStatus::class)]Generated schema:
status:
type: string
enum: [pending, active, completed]
example: pendingWhen validation rules contain file, image, or mimes: rules, the package automatically:
- Sets the body mode to
multipart/form-data - Uses
format: binaryfor file fields in OpenAPI schema - Marks file fields as
type: filein Postman formdata
class UploadRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'avatar' => 'required|image|max:2048',
'document' => 'file|mimes:pdf,doc',
];
}
}requestBody:
content:
multipart/form-data:
schema:
type: object
required: [title, avatar]
properties:
title:
type: string
maxLength: 255
avatar:
type: string
format: binary
document:
type: string
format: binary{
"mode": "formdata",
"formdata": [
{ "key": "title", "value": "Example Title", "type": "text" },
{ "key": "avatar", "value": "", "type": "file" },
{ "key": "document", "value": "", "type": "file" }
]
}The package detects paginated responses by analyzing method source code for paginate(), simplePaginate(), or cursorPaginate() calls.
// LengthAwarePaginator
return UserResource::collection($query->paginate(15));
// SimplePaginator
return UserResource::collection($query->simplePaginate(15));
// CursorPaginator
return UserResource::collection($query->cursorPaginate(15));When pagination is detected, the response includes standard Laravel pagination fields:
{
"data": [
{ "id": 1, "name": "Example Name", "email": "user@example.com" }
],
"links": {
"first": "https://example.com/api/resource?page=1",
"last": "https://example.com/api/resource?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://example.com/api/resource",
"per_page": 15,
"to": 1,
"total": 1
}
}The package resolves Resource class namespaces in this order:
- Use statements - Imports at top of controller file
- Same namespace - Same directory as controller
-
Common namespaces:
App\Http\ResourcesApp\Http\Resources\ApiApp\Http\Resources\Api\V1App\Http\Resources\Api\V1\Customer
// If controller has:
use App\Http\Resources\V2\UserResource;
// This will resolve correctly:
return UserResource::make($user);-
Complex logic - Only simple
returnstatements are detected - Dynamic returns - Conditional returns based on runtime values aren't detected
- Abstract resources - Base resource classes aren't analyzed
- Closures - Inline closures in routes aren't supported
For complex cases, use explicit attributes:
#[ApiBody(['complex' => 'structure'])]
#[ApiResource(CustomResource::class)]
#[ApiResponse(name: 'Success', status: 200, body: ['custom' => 'response'])]
public function complexEndpoint(): JsonResponse