@salvobee/laravel-api-client
v0.1.2
Published
Framework-agnostic CRUD client builder with Laravel-style route fallback.
Maintainers
Readme
Laravel API Client
Framework-agnostic CRUD client builder for resource-based backends.
It accepts an AxiosInstance and a route resolver. If you don't pass a resolver, it defaults to a Laravel-style resolver via @salvobee/laravel-resource-route.
Install
npm i @salvobee/api-resource-core @salvobee/laravel-resource-route axiosQuick start (TypeScript)
import axios from "axios";
import { createApiResourceClient } from "@salvobee/api-resource-core";
type Photo = { id: number; title: string };
type PhotoList = { data: Photo[]; meta: any }; // shape is up to your API
const api = createApiResourceClient<Photo, PhotoList>({
resourceKey: "photos",
resourceRouteParam: "photo",
client: axios.create({ baseURL: "https://api.example.com" }),
// routeFn: yourCustomResolver, // optional; defaults to Laravel-style
});
await api.list({ page: 2 });
await api.get(42);
await api.store({ title: "New photo" });
await api.update(42, { title: "Updated" });
await api.destroy(42);With custom resolver (e.g., Ziggy)
import route from "ziggy-js";
const api = createApiResourceClient({
resourceKey: "photos",
resourceRouteParam: "photo",
client: axiosInstance,
routeFn: (name, params) => route(name, params),
});Error handling
By default the core throws normalized errors:
ValidationError(Laravel 422{ message, errors: { field: [msg] } })HttpError(responses with a status code but not 422)ApiError(network/unknown)
You can customize mapping via errorAdapter (client-wide) or per call:
import { createApiResourceClient, ValidationError } from "@salvobee/api-resource-core";
const api = createApiResourceClient({
resourceKey: "posts",
resourceRouteParam: "post",
client: axiosInstance,
errorAdapter: ({ raw }) => {
// Example: forward Laravel 419 as AuthError, etc.
const r: any = raw;
const status = r?.response?.status;
if (status === 419) throw new Error("CSRF expired");
// fallback to default:
throw raw; // will be rethrown; or reimplement the default logic
},
});
try {
await api.store({ title: "" });
} catch (e) {
if (e instanceof ValidationError) {
console.log(e.errors); // { title: ["The title field is required."] }
}
}Notes
- list() returns the entire response.data (common for index endpoints).
- get/store/update() return response.data.data when present, else response.data.
- You can pass additionalRouteParams to keep nested segments (e.g., { user: 7 } for /users/7/photos/...).
- Optional per-call Axios config via the second arg (e.g., { signal }).
License
MIT © Salvo Bee
