konnec/vue-eloquent-api
Konnec Vue Eloquent API bridges Laravel Eloquent models and Vue front ends, exposing clean, query-ready JSON endpoints with filtering, pagination, and relation loading out of the box. Scaffold APIs faster and keep your Vue client in sync with your database schema.
Install and wire it up
npm install konnec/vue-eloquent-api # or the published package name in the README
Register the plugin once in your app entry point, pointing it at your Laravel API root and (usually) an Axios instance:
import VueEloquentApi from 'konnec/vue-eloquent-api'
app.use(VueEloquentApi, {
baseURL: import.meta.env.VITE_API_URL + '/api',
// headers / axios instance if the package accepts one
})
Where to look first
Model class in src/ — this is the real contract. Read it end to end before adopting; it defines every available verb, how query state is stored, and what response shape it expects back.examples/ or demo app — if present, it tells you more about intended usage than the prose docs.First use case
Define one model per Laravel resource and use it as a thin, Eloquent-flavoured HTTP client:
// models/Post.js
import { Model } from 'konnec/vue-eloquent-api'
export default class Post extends Model {
// resource endpoint, e.g. '/posts'
static get endpoint() { return '/posts' }
}
const posts = await Post.where('published', true).with('author').get()
const post = await Post.find(1)
Pair it with a standard Laravel Route::apiResource('posts', PostController::class) and an ApiResource / paginated response, and you have the intended happy path working in an afternoon.
One model file per Eloquent model
Mirror your backend structure 1:1 (models/Post.js ↔ App\Models\Post). Keep the endpoint definition in the model, not at the call site, so a route rename is a one-line change.
Centralise transport in your own base class
Subclass the package's Model once and have every domain model extend that:
// models/BaseModel.js
import { Model } from 'konnec/vue-eloquent-api'
export default class BaseModel extends Model {
static get baseURL() { return import.meta.env.VITE_API_URL + '/api' }
// global error normalisation, auth refresh, request logging
}
This is your main insulation layer. If the package stalls (see Gotchas), you only rewrite one file.
Query-chain mirroring
Use the fluent API as the read path and keep writes explicit:
Model.where(...).orderBy(...).with(...).paginate(15)instance.save() / instance.delete() / Model.create(payload)Don't try to build a client-side ORM on top of it. Treat it as a query-string builder with a resource name attached.
Eager loading must match the backend
with('author') only works if your controller forwards ?with= to Eloquent (Post::with(request('with'))). Add that explicitly — the package can send the parameter, but Laravel won't act on it by default. Watch out for unbounded relation names coming from the client.
Pagination and response envelopes
Decide once whether your API returns { data: [...], meta: {...} } (Laravel ResourceCollection) or a flat array, then verify how the package unwraps it. Most friction in these libraries comes from a mismatch here — write a single adapter function and use it everywhere.
State management integration
Keep responses in Pinia/Vuex, not in the model:
const posts = await Post.paginate(1)
store.posts = posts.data
store.meta = posts.meta
This keeps components reactive and makes caching, optimistic updates, and SSR predictable.
Auth
Use a single Axios instance with an interceptor for Sanctum cookies or a bearer token, and pass that instance to the plugin if supported; otherwise set the token on the model before each request. For cookie-based Sanctum SPAs you also need withCredentials and a CSRF cookie fetch — verify the package doesn't overwrite those settings.
Form binding
For edit screens, hydrate a model instance from the API, bind inputs to its attributes, and call save() on submit. Track dirty fields yourself if you need PATCH-instead-of-PUT semantics; don't assume the package diffs attributes.
Maturity is the headline risk
2 stars, 0 dependents, and a single maintainer means:
package.json (no ^)..d.ts for your own models.Read the source before trusting the README
With projects this size, methods get renamed and examples go stale. Grep the source for the method you're about to call.
Naming and casing
Laravel returns snake_case; JS conventions are camelCase. Decide where the conversion happens (API Resource on the server is cleanest) rather than scattering post.created_at throughout components.
Date casting
Values arrive as ISO strings, not Carbon instances. Cast explicitly (new Date(...)) at the boundary, or use an API Resource to format them server-side.
Error handling
Axios rejections won't automatically become Laravel-style validation bags. Wrap calls in a helper that inspects error.response.status and maps 422 errors to your form state:
try { await post.save() }
catch (e) {
if (e.response?.status === 422) form.errors = e.response.data.errors
else throw e
}
Parameter injection and safety
Anything the client puts in where() / with() / orderBy() reaches your controller. Never forward those straight into orderBy($request->sort) — whitelist sortable columns and allowed relations server-side.
Debugging
Open the Network tab first: the package's whole job is producing a URL. If a query returns the wrong set, log the generated request rather than stepping through model internals. Check ?filter[...], ?with=, ?page=, ?per_page= exactly as sent.
Extension points
Override methods on your BaseModel rather than patching node_modules. The usual override targets are: response unwrapping, error normalisation, base URL/headers, and the query-to-params serialiser.
When to walk away
If you find yourself reimplementing pagination, relations, and error handling inside your BaseModel, you've outgrown the package. A 60-line Axios wrapper plus Laravel API Resources gives you the same ergonomics with no third-party risk — worth benchmarking against before you build deeply on it.
How can I help you explore Laravel packages today?