d34dman / drupal-jsonapi-params

A package to manage json-api params
ISC License
59 stars 8 forks source link

drupal approved pagination #40

Closed AtRiskMedia closed 7 months ago

AtRiskMedia commented 7 months ago

Hi, thanks so much for this amazing library!

The drupal docs are very clear: "If you take only one thing away from this guide, it should be that you should not construct your own pagination URLs", https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/pagination

I see there's a module to allow override: https://www.drupal.org/project/jsonapi_page_limit but i'm hoping to avoid this

Is there any way to access the "links" next and count on the response from Drupal? I'd like to run successive fetches in a recursive loop but could only do this using .addPageOffset

Is there a better pattern to follow?

AtRiskMedia commented 7 months ago

this what's working for me (in astro):

import { Jsona } from "jsona";
import { DrupalJsonApiParams } from "drupal-jsonapi-params";
import type { DrupalFile, DrupalNode } from "../types.ts";
import type { TJsonApiBody, TJsonaModel } from "jsona/lib/JsonaTypes";

interface DrupalApiBody extends TJsonApiBody {
  /* eslint-disable @typescript-eslint/no-explicit-any */
  links?: any;
}

export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;

export const fetchUrl = async (url: string): Promise<any> => {
  let data: TJsonaModel = [];
  let more = true;
  while (more) {
    const request: Response = await fetch(url);
    const json: string | DrupalApiBody = await request.json();
    const dataFormatter: Jsona = new Jsona();
    const thisData = dataFormatter.deserialize(json);
    if (thisData.length) data = data.concat(thisData);
    else data.push(thisData);
    url = typeof json !== `string` ? json?.links?.next?.href : null;
    if (typeof url === `undefined`) more = false;
  }
  return data;
};

export const getFiles = async (): Promise<DrupalFile[]> => {
  const params: DrupalJsonApiParams = new DrupalJsonApiParams();
  params.addFields("file--file", ["type", "id"]).addFilter("status", "1");
  const path: string = params.getQueryString();

  return await fetchUrl(baseUrl + "/jsonapi/file/file?" + path);
};

export default fetchUrl;
d34dman commented 7 months ago

@AtRiskMedia am glad that you found this module helpful and thanks for sharing your concern regarding pagination.

ATM, the module only provide a helper method generate your query. It is independent of result. It works with no Feedback. Keeping it independent of how the actual query is executed (fetch, axios, XMLHttpRequest...) helps keep it framework agnostic.

If we have to continue on this, then to get dynamic next links based on results, we would have to feed the current result into DrupalJsonApiParams and call some method like next get the url , fire the query, get the result feed it back to DrupalJsonApiParams and repeat ... I don't think it as a nice developer experience over what you have achieved in your sample code.

What you have done is a really a nice pattern to follow none the less. Alternatively, we can add this to project documentation (in README.md) so that people can benefit from this.

What do you think?

AtRiskMedia commented 7 months ago

Awesome. Thanks @d34dman ... yes, even a quick note in the docs suggesting this as a pattern may be helpful for others.

Using DrupalJsonApiParams to craft the endpoint then follow the payload for subsequent urls to adhere to Drupal pagination: It's best of both worlds.