Saltar al contenido
MundoProgra JSON:API
Menú

Empezar

Primeros pasos

Un recurso, un controlador y sus rutas: con tres piezas ya tienes un endpoint que filtra, ordena, pagina y carga relaciones.

Por defecto el paquete actúa sobre las rutas que empiezan con api (mode = prefix, path_prefix = api). Todo lo demás responde como siempre. Para que toda la aplicación responda en JSON:API, usa JSON_API_MODE=global en el .env.

1. Un recurso

Un recurso dice qué se muestra de un modelo:

use Illuminate\Http\Request;
use MundoProgra\JsonApi\Resources\Relationship;
use MundoProgra\JsonApi\Resources\ResourceObject;

class ArticleResource extends ResourceObject {
  // El nombre corto de la ruta que muestra un artículo: de ahí sale su `links.self`.
  protected function jsonApiRoute(): ?string {
    return 'articles.show';
  }

  // Los nombres se escriben en snake_case y salen en el estilo configurado (`createdAt` por defecto).
  protected function jsonApiAttributes(Request $request): array {
    return [
      'title' => $this->title,
      'status' => $this->status,
      'created_at' => $this->created_at?->toIso8601String(),
    ];
  }

  // Solo salen las relaciones que están cargadas: el paquete nunca dispara una consulta para completar un documento.
  protected function jsonApiRelationships(Request $request): array {
    return [
      'author' => $this->whenLoaded('author', fn() => Relationship::one($this->author, AuthorResource::class)),
      'tags' => $this->whenLoaded('tags', fn() => Relationship::many($this->tags, TagResource::class)),
    ];
  }
}

El type de un recurso es la tabla del modelo y su id es la clave de ruta, siempre como texto. Se cambian sobrescribiendo type() e id().

2. Un controlador

El controlador aplica la consulta del cliente con listas blancas que escribes tú:

class ArticleController {
  public function index() {
    return ArticleResource::collection(
      Article::query()
        ->jsonApiFilter(exact: ['status', 'author' => 'author_id'], partial: ['title'], scopes: ['published'])
        ->jsonApiSort(['id', 'title', 'created_at'], default: '-id')
        ->jsonApiInclude(['author', 'tags', 'comments.author'])
        ->jsonApiPaginate()
    );
  }

  public function show(string $article) {
    return ArticleResource::make(
      Article::query()->jsonApiInclude(['author', 'tags'])->findOrFail($article)
    );
  }
}

3. Las rutas

Las rutas son las de siempre; el paquete no registra ninguna. Solo hace falta que exista la que nombraste en jsonApiRoute(), porque de ella sale el links.self de cada recurso:

use Illuminate\Support\Facades\Route;

Route::get('articles', [ArticleController::class, 'index'])->name('articles.index');
Route::get('articles/{article}', [ArticleController::class, 'show'])->name('articles.show');

Si tus rutas llevan un prefijo en el nombre (por ejemplo v1.articles.show), indícalo en links.route_name_prefix, como se explica en Configuración.

Qué obtienes

Con eso, esta petición ya funciona de punta a punta:

GET /api/articles?filter[status]=published&sort=-createdAt&include=author&fields[articles]=title,author&page[size]=5

Filtra, ordena, pagina, carga el autor sin consultas de más y devuelve el documento con data, included, links y meta. Cada filtro, orden o include que no esté en tu lista responde 400 con el parámetro que falla.

Los detalles de cada parte están en Recursos, colecciones y relaciones y en Parámetros de consulta.