LaravelPackages.net
Acme Inc.
Toggle sidebar
romdh4ne/laravel-querycraft

Find and fix Laravel performance issues automatically

37
22
v1.1.0
About romdh4ne/laravel-querycraft

romdh4ne/laravel-querycraft is a Laravel package for find and fix laravel performance issues automatically. It currently has 22 GitHub stars and 37 downloads on Packagist (latest version v1.1.0). Install it with composer require romdh4ne/laravel-querycraft. Discover more Laravel packages by romdh4ne or browse all Laravel packages to compare alternatives.

Last updated

QueryCraft Logo

A Laravel performance analysis dashboard for detecting N+1 queries, slow queries, missing indexes, and duplicate queries β€” in real time.

Laravel PHP License Packagist Downloads


✨ Features

  • πŸ” N+1 Detection β€” catches repeated query patterns caused by missing eager loading
  • 🐒 Slow Query Detection β€” flags queries exceeding your configured time limit
  • πŸ—‚ Missing Index Detection β€” identifies full table scans using MySQL EXPLAIN
  • πŸ“‹ Duplicate Query Detection β€” finds identical queries (including bindings) fired multiple times
  • πŸ“ Source Location β€” shows the exact file and line number in your app that triggered the issue
  • πŸ’― Performance Score β€” grades your endpoint from 0–100 with a letter grade (A–F)
  • πŸ›  Live Config Panel β€” toggle detectors and adjust thresholds from the dashboard UI
  • πŸŒ™ Dark Mode β€” built-in dark/light mode toggle
  • 🚨 500 Error Inspector β€” displays full exception details with app-only stack trace
  • ⌨️ Artisan Command β€” analyze endpoints directly from your terminal with full body and header support

πŸ“¦ Installation

1. Require the package via Composer

composer require romdh4ne/laravel-querycraft

2. Publish the config file

php artisan vendor:publish --tag=querycraft-config

3. Publish the assets (logo, favicon)

php artisan vendor:publish --tag=querycraft-assets

4. Publish the views (optional β€” only if you want to customize the UI)

php artisan vendor:publish --tag=querycraft-views

5. Clear caches

php artisan config:clear
php artisan view:clear

6. Visit the dashboard

http://your-app.test/querycraft

πŸ–₯ Web Dashboard

Opening the dashboard

http://your-app.test/querycraft

QueryCraft Dashboard

Or with a custom route prefix set in .env:

http://your-app.test/your-custom-prefix

Analyzing an endpoint

  1. Enter your endpoint URL (e.g. /api/users)
  2. Select the HTTP method (GET, POST, PUT, PATCH, DELETE)
  3. Optionally add custom headers (e.g. Authorization: Bearer token)
  4. Optionally add a JSON request body for POST/PUT requests
  5. Click Analyze Request

QueryCraft fires an internal request to your endpoint, collects all queries, runs them through all detectors, and displays the results instantly.

Reading the results

| Element | Description | |---|---| | Score card | 0–100 performance grade with letter (A–F) and emoji indicator | | Query count | Total number of queries executed by the endpoint | | Total time | Combined execution time of all queries in milliseconds | | Issue cards | Each problem with severity, stats, source location and fix suggestion | | Source Location | Exact file path and line number in your app (vendor files filtered out) | | All Queries | Collapsible list of every query fired with individual execution time |

500 Error Inspector

When your endpoint crashes, QueryCraft catches it and displays:

  • Exception class (e.g. ErrorException, QueryException)
  • Error message
  • Exact file and line number in your app where it crashed
  • Stack trace showing only your app files β€” no vendor noise
  • Number of queries captured before the crash

Set APP_DEBUG=true in your .env for full exception details.

Client Error Display

When your endpoint returns a 4xx response (e.g. 422 validation error, 404), QueryCraft shows:

  • The HTTP status code
  • The error message returned by your API
  • Suggestions for similar routes if 404

Config Panel

Click the βš™οΈ icon in the top-right header to open the config panel:

  • Toggle each detector on/off individually
  • Adjust thresholds using sliders
  • Tune score weights (must total 100%)
  • Click Save β€” changes are written to your .env immediately
  • Click Reset to restore all defaults

⌨️ Artisan Command

Analyze endpoints directly from your terminal without opening a browser.

Signature

php artisan querycraft:analyze
    {--url=         : The endpoint URL to analyze}
    {--method=GET   : HTTP method (GET, POST, PUT, PATCH, DELETE)}
    {--user=        : Authenticate as a specific user ID}
    {--show-queries : Print all executed queries in the output}
    {--body=        : JSON body as an inline string}
    {--body-file=   : Path to a JSON file to use as the request body}
    {--header=*     : Custom headers in Key:Value format (repeatable)}

Examples

# Simple GET
php artisan querycraft:analyze --url=/users

# With authentication
php artisan querycraft:analyze --url=/dashboard --user=1

# POST with inline JSON body
php artisan querycraft:analyze --url=/api/posts --method=POST \
  --body='{"title":"Hello","body":"World","category_id":1}'

# POST with many fields β€” use --body-file to keep it clean
php artisan querycraft:analyze --url=/api/orders --method=POST \
  --body-file=./payload.json

# PUT with body and auth
php artisan querycraft:analyze --url=/api/users/1 --method=PUT --user=1 \
  --body='{"name":"John","email":"[email protected]"}'

# With custom headers
php artisan querycraft:analyze --url=/api/secret \
  --header="Authorization:Bearer your-token" \
  --header="X-Team-Id:42"

# Everything combined
php artisan querycraft:analyze --url=/api/orders --method=POST --user=1 \
  --body-file=./payload.json \
  --header="X-Source:querycraft" \
  --show-queries

Sending a large body with --body-file

When your request has many fields, create a payload.json file instead of cramming everything inline:

{
  "customer_id": 5,
  "shipping_address": {
    "street": "123 Main St",
    "city": "Paris",
    "zip": "75001"
  },
  "items": [
    { "product_id": 1, "qty": 2, "price": 29.99 },
    { "product_id": 3, "qty": 1, "price": 49.99 }
  ],
  "coupon": "SAVE10",
  "notes": "Leave at door"
}

Then run:

php artisan querycraft:analyze --url=/api/orders --method=POST \
  --body-file=./payload.json --user=1 --show-queries

Use --body for small payloads. Use --body-file for large or complex payloads β€” it avoids shell escaping issues and is much easier to read and reuse.

Example output

πŸ” Analyzing: GET /users

βœ… Response: 200

πŸ“Š Summary:
+----------------+----------+
| Metric         | Value    |
+----------------+----------+
| Total Queries  | 23       |
| Total Time     | 182.5 ms |
| Avg Query Time | 7.93 ms  |
| Response Status| 200      |
+----------------+----------+

⚠️  Found 2 issue(s):

πŸ”΄ Issue #1: N+1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Severity: HIGH
Occurrences: 20
Total Time: 140ms
Location: /app/Http/Controllers/UserController.php:45

Query:
  select * from `companies` where `id` = ?

πŸ’‘ Suggestion:
  Add eager loading: ->with('company')

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚑ Performance Score: 62/100 (Grade: D) 🟠
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🎯 Top Improvements:
  β€’ Too many queries (+25 points)
  β€’ 2 performance issues detected (+20 points)

If your endpoint requires authentication, pass --user=1 to run as a specific user. QueryCraft calls auth()->login($user) before firing the request.


βš™οΈ Configuration

After publishing, the config file is at config/querycraft.php. All values can be overridden via .env:

# Enable or disable the package entirely QUERY_DEBUGGER_ENABLED=true # Detectors β€” toggle individually QUERYCRAFT_DETECTOR_N1=true QUERYCRAFT_DETECTOR_SLOW_QUERY=true QUERYCRAFT_DETECTOR_MISSING_INDEX=true QUERYCRAFT_DETECTOR_DUPLICATE_QUERY=true # Thresholds QUERY_DEBUGGER_N1_THRESHOLD=5 # Flag N+1 after this many repetitions (default: 5) QUERY_DEBUGGER_SLOW_THRESHOLD=100 # Flag queries slower than this in ms (default: 100) QUERYCRAFT_DUPLICATE_COUNT=2 # Flag duplicates after this many repeats (default: 2) # Score weights β€” must total 100 QUERYCRAFT_WEIGHT_QUERY_COUNT=40 QUERYCRAFT_WEIGHT_QUERY_TIME=30 QUERYCRAFT_WEIGHT_ISSUES=30 # Dashboard route prefix (default: querycraft) QUERYCRAFT_DASHBOARD_ROUTE=querycraft

Full config reference

// config/querycraft.php
return [
    'enabled' => env('QUERY_DEBUGGER_ENABLED', true),

    'detectors' => [
        'n1'             => env('QUERYCRAFT_DETECTOR_N1', true),
        'slow_query'     => env('QUERYCRAFT_DETECTOR_SLOW_QUERY', true),
        'missing_index'  => env('QUERYCRAFT_DETECTOR_MISSING_INDEX', true),
        'duplicate_query'=> env('QUERYCRAFT_DETECTOR_DUPLICATE_QUERY', true),
    ],

    'thresholds' => [
        'n1_count'        => env('QUERY_DEBUGGER_N1_THRESHOLD', 5),
        'slow_query_ms'   => env('QUERY_DEBUGGER_SLOW_THRESHOLD', 100),
        'duplicate_count' => env('QUERYCRAFT_DUPLICATE_COUNT', 2),
    ],

    'weights' => [
        'query_count' => env('QUERYCRAFT_WEIGHT_QUERY_COUNT', 40),
        'query_time'  => env('QUERYCRAFT_WEIGHT_QUERY_TIME', 30),
        'issues'      => env('QUERYCRAFT_WEIGHT_ISSUES', 30),
    ],
];

Tip: All settings can also be changed from the dashboard βš™οΈ config panel β€” changes are saved directly to your .env.


πŸ”¬ How Detectors Work

πŸ” N+1 Detection

Normalizes every query (replaces values with ?) and groups them by pattern. If the same pattern fires more than n1_count times, it's flagged. The exact file and line in your app that triggered the repeated query is shown.

Example:

// ❌ N+1 β€” fires one query per user
$users = User::all();
foreach ($users as $user) {
    echo $user->company->name;
}

// βœ… Fix β€” one query total
$users = User::with('company')->get();

| Count | Severity | |---|---| | 5–10Γ— | low | | 10–20Γ— | medium | | 20–50Γ— | high | | 50Γ—+ | critical |


🐒 Slow Query Detection

Flags any query exceeding slow_query_ms milliseconds (default: 100ms). Automatically suggests a fix based on the query structure.

| Time | Severity | |---|---| | > 200ms | low | | > 500ms | medium | | > 1000ms | high | | > 1000ms | critical |

Suggestions shown:

  • SELECT * β†’ use specific columns
  • ORDER BY without LIMIT β†’ add a limit
  • LIKE queries β†’ consider full-text search
  • COUNT(*) on large tables β†’ consider caching

πŸ—‚ Missing Index Detection

Runs MySQL EXPLAIN on each query and flags full table scans, filesorts, and temporary table usage.

Triggers:

  • type = ALL with more than 1,000 rows examined
  • Extra contains Using filesort
  • Extra contains Using temporary

Example:

User::where('email', $email)->first(); // no index on email

// Fix β€” in a migration:
$table->index('email');
$table->index(['status', 'created_at']); // composite

| Rows examined | Severity | |---|---| | > 1,000 | low | | > 10,000 | medium | | > 100,000 | high | | > 100,000 | critical |


πŸ“‹ Duplicate Query Detection

Creates an md5 fingerprint of sql + bindings. If the exact same query with the same parameter values runs more than duplicate_count times (default: 2), it's flagged.

Example:

// ❌ Duplicate β€” same query + same values fired twice
$settings = Setting::all();
// ... somewhere else in the same request ...
$settings = Setting::all();

// βœ… Fix
$settings = Cache::remember('settings', 3600, fn() => Setting::all());

πŸ’― Performance Score

Calculated as a weighted average across three dimensions:

| Dimension | Default Weight | Description | |---|---|---| | Query Count | 40% | Fewer queries = higher score | | Query Time | 30% | Faster total time = higher score | | Issues Found | 30% | Fewer/lower severity issues = higher score |

| Score | Grade | Status | |---|---|---| | 90–100 | A 🟒 | Excellent | | 80–89 | B 🟑 | Good | | 70–79 | C 🟑 | Acceptable | | 60–69 | D 🟠 | Below average | | 0–59 | F πŸ”΄ | Critical issues |

Weights are configurable from the dashboard config panel or via .env.


πŸ”’ Security

QueryCraft is intended for local development only. Always disable it in production:

# .env.production QUERY_DEBUGGER_ENABLED=false

πŸ”„ Updating

composer update romdh4ne/laravel-querycraft
php artisan vendor:publish --tag=querycraft-views --force
php artisan vendor:publish --tag=querycraft-assets --force
php artisan config:clear
php artisan view:clear

πŸ—‘ Uninstalling

composer remove romdh4ne/laravel-querycraft
rm config/querycraft.php
rm -rf resources/views/vendor/querycraft
rm -rf public/vendor/querycraft
# Remove QUERYCRAFT_* and QUERY_DEBUGGER_* lines from your .env

🀝 Contributing

Setup

git clone https://github.com/YOUR_USERNAME/laravel-querycraft.git
cd laravel-querycraft
composer install

Local test app (separate project)

In a separate Laravel app, add to composer.json:

"repositories": [
    {
        "type": "path",
        "url": "../laravel-querycraft",
        "options": { "symlink": true }
    }
]

Then:

composer require romdh4ne/laravel-querycraft:@dev
php artisan vendor:publish --tag=querycraft-config
php artisan vendor:publish --tag=querycraft-assets
php artisan config:clear
php artisan serve

Visit http://localhost:8000/querycraft. Any change you make in the package reflects instantly thanks to the symlink.

πŸ“„ License

QueryCraft is open-source software licensed under the MIT license.


πŸ‘¨β€πŸ’» Author

Made by Romdh4ne

If this package helps you, give it a ⭐ on GitHub!

Comments