Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Kit Laravel Package

juststeveking/kit

Kit is a Laravel package from JustSteveKing that bundles practical helpers, utilities and conventions to speed up everyday development. Drop it into any project to cut boilerplate, keep code consistent, and ship features faster with less repetition—a handy toolkit for Laravel developers.

View on GitHub
Deep Wiki
Context7

Getting Started

Kit is a starter kit you fork, not a package you install. There is no composer require juststeveking/kit in an existing app; day-to-day value comes from inheriting a working API substrate — token auth, RFC 9457 problem details, pagination, OpenAPI docs, and five CI gates — so you never re-litigate HTTP semantics per endpoint.

Minimal bootstrap:

git clone https://github.com/JustSteveKing/kit my-api && cd my-api
composer install
cp .env.example .env && php artisan key:generate
php artisan migrate
php artisan serve
php artisan queue:work   # mail and webhooks are queued

Read in this order before touching code:

  1. AGENTS.md — the operating manual. It defines "done" (the five gates) and is written for coding agents and the humans supervising them. Read it first; it will save you a rejected PR.
  2. docs/README.md — per-feature guides (errors, standards, webhooks, localization, security).
  3. routes/api/v1.php plus one controller (LoginController) — shows the endpoint shape and the Scribe attribute style you will copy.
  4. stubs/endpoint/ — what the generator emits before you fill in the blanks.

First use case — one endpoint, end-to-end, through the generator:

php artisan make:endpoint Projects/CreateProject --method=POST --uri=/projects

It writes six files (route entry, Form Request, Payload, invokable Controller, Resource, Pest test), appends the route to routes/api/v1.php, runs Pint, and prints the TODOs it cannot fill for you. Your first day is: finish those TODOs → php artisan scribe:generate --no-interaction → get green on all five gates:

composer lint        # Pint (PER preset + pint.json)
composer rector:check
composer stan        # PHPStan + Larastan, level 10
php artisan test     # Pest, includes OpenAPI contract tests
legible check public/docs/openapi.yaml

If it fails any of these, it is not finished. CI runs all five and builds and boots the production image. That is the single biggest adjustment coming from a vanilla Laravel project — burn it into muscle memory in week one.

Implementation Patterns

The generator-first loop is the whole workflow. Every endpoint is the same six pieces; internalise the shape and Kit becomes a scaffolding DSL:

  • Route in routes/api/v1.php under auth:sanctum + a named throttle + an ability. Name it v1.{domain}.{action}. Path params are constrained by route where* clauses (whereUlid), never by the Form Request.
  • Form Request — final, with authorize() and rules() for body/query only. A GET also implements queryParameters() so Scribe doesn't document its rules as a body.
  • Payload — final readonly class with fromRequest(), reading validated data through typed accessors (Arr::string(), Arr::integer()) and path params via App\Http\Payloads\RouteParameter. Controllers receive the Payload; nothing downstream touches $request->input().
  • Controller — final, invokable, no base class, documented with Scribe attributes (#[Group], #[Endpoint], #[BodyParam], #[ResponseFromApiResource], #[Response] per error status, #[Header] for Idempotency-Key / If-Match / If-None-Match).
  • Resource — a plain JsonResource. IDs as strings, dates as ->toAtomString(). No JSON:API; it was deliberately removed.
  • Test — Pest: success, validation, auth, missing ability, plus standards behaviour. Register the endpoint in OpenApiContractTest's runtime checks so real responses are validated against the spec.

Reach for api-toolbox before hand-rolling anything HTTP-ish. This is the highest-leverage habit Kit teaches — a quick decision table:

Need Use
Retry-safe writes idempotent middleware; idempotent:required to demand the key
Lists CursorPage::of($query->cursorPaginate($perPage), Resource::class)
Cheap re-fetch conditional middleware (ETag / 304)
Lost-update protection model implements HasValidators + route binding → 412 / 428
PATCH semantics merge-patch + consumes:application/merge-patch+json
Retiring an endpoint #[Deprecation] / #[Sunset] on the controller class
Long-running work Async operations (off by default; enabling adds a route to document)
Signed webhooks Message signatures, keys in config only

Errors are problem details, always. Either abort(404, __('api.projects.not_found')) (the message becomes detail, the status picks the apiguide type), or throw a typed problem from php artisan make:api-problem --apiguide=<entry>. Never build error JSON by hand, and never add response-returning render callbacks in bootstrap/app.php — the ones there only write the security audit log. Assert with assertProblem(), assertProblemType(), assertJsonPath('detail', ...).

Webhooks follow a fixed pipeline. Implement App\Webhooks\WebhookEvent (type() dotted and past tense, subject(), data()), register the type in config/webhooks.php, then dispatch inside the writing transaction:

app(Webhooks::class)->dispatch($project->owner, new ProjectCreated($project));

Deliveries queue after commit, so subscribers never see a row that does not exist yet. The service handles CloudEvent construction, signing, public-URL checks on every send, attempt recording, backoff, Retry-After, and disabling dead endpoints. Never send webhooks with Http yourself.

Security and localization are conventions, not afterthoughts.

  • SecurityAudit::log('domain.action.outcome', [...]) for anything security-relevant (IDs and hashEmail() only).
  • Every client-facing string goes through __() with keys in both lang/en/api.php and lang/es/api.php — a parity test enforces it.
  • New abilities go into sanctum.abilities.available, and into sanctum.abilities.default only if every signed-in client needs them — otherwise login-issued tokens cannot call your route.

Fix-loop order matters: composer rector → composer pint → composer stan → tests. Rector changes code; Pint owns formatting and has the last word.

When the generator does not cover your shape (a conditional GET, a merge-patch PATCH, an endpoint that dispatches a webhook), copy the nearest existing endpoint rather than improvising — the six-piece shape plus the middleware table above is your checklist.

Gotchas and Tips

  • Finish the TODOs before running Rector. Generated endpoints intentionally contain placeholders (unused payload, empty constructor, 501 controller, three-sentence TODO description). Rector treats them as dead code and deletes them. Fill them in, then refactor.
  • The docs gate is stricter than it looks. OpenApiContractTest compares every v1/ route against the generated spec — a route Scribe cannot see breaks the build — and legible fails one-sentence descriptions, undocumented error statuses, and descriptions shared by two operations. Regenerate the spec and commit it with every endpoint change.
  • Path parameters belong in route constraints. Validating a path param in a Form Request makes it appear as a body field in the docs, which legible reports as an argument collision.
  • A GET's Form Request without queryParameters() gets documented as taking a request body. Easy to miss, always caught late.
  • Forgetting the ability in sanctum.abilities.default is the classic "auth works, route 403s" trap. Tokens issued at login can only carry abilities listed there.
  • Override consumes: per route, not by removing group middleware. Group-level consumes:application/json only applies to write routes; a route taking something else (e.g. multipart/form-data) declares its own and that replaces the group's.
  • Deprecation/sunset attributes only work on controller classes, not route closures. Kit then answers 410 after the sunset date.
  • data() in a webhook event is public API. Add fields; never rename or remove.
  • Test webhooks with Http::fake() and bind App\Webhooks\HostResolver so tests do not resolve DNS. The public-URL check runs on every send, which can surprise you in CI.
  • Three localization surfaces to keep in sync: both lang/*/api.php files, plus lang/vendor/api-toolbox/es/problems.php — that one is checked by a test after each package upgrade, so pin api-toolbox deliberately and re-read its changelog.
  • Do not remove ProductionSecurityChecks. It refuses to boot in production without HTTPS enforcement, an https:// APP_URL, APP_DEBUG=false, explicit CORS origins, and trusted hosts. Configure the environment; leave the check alone.
  • sanctum.expiration is deliberately off so API keys can outlive sessions. Issue tokens via App\Support\AccessTokens (per-token expires_at, capped by API_KEY_MAX_DAYS) rather than calling Sanctum directly.
  • Octane/FrankenPHP keeps the app in memory. Use scoped() bindings (as api-toolbox does for request ID / trace), resolve request state at request time rather than in boot-time closures, and never stash request data in singletons or statics. Mail and webhooks are queued, so production needs php artisan queue:work alongside the image.
  • Dev packages are not in the production image. Config that references one — config/scribe.php is the example — must return early when it is absent, or the CI image build fails.
  • Laravel Vet gates composer install. vet.json records packages someone has actually reviewed, and releases younger than seven days are held back. New dependency additions will stall until reviewed; that is the point, but budget for it.
  • Never loosen pint.json or phpstan.neon. Level 10 means mixed must be narrowed (config()->string(), config()->integer(), $request->string(), Arr::string()), not cast. @phpstan-ignore requires the identifier plus a comment justifying it. Skip Rector rules with a reason in rector.php rather than letting Rector and Pint disagree.
  • User IDs are ULIDs — treat them as strings in payloads, resources, and route constraints (whereUlid).
  • Ecosystem caveat. Solo-maintained, low-star starter kit with no dependents: the pattern value is high, the upgrade-path support is not. Pin Laravel, api-toolbox, Scribe, and legible versions; treat Laravel major upgrades as a deliberate project rather than a routine composer update. The five-gate CI is your real safety net — run it locally, and never merge with a gate red.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource