Allow credentials in `@frontity/wp-source` requests

Description

I need to send the authentication cookies in every request to the REST API made with @frontity/wp-source.

Examples

I want to request /wp-json/wp/v2/users and get a full list of users if I’m authenticated as an admin.

Possible solution

One way to solve this is passing a fetch options object as a parameter to libraries.source.api.get so I can use fetch with { credentials: "include" } to attach cookies to the request. (This solution won’t work with the default handlers, only with those created by users that decide to pass the options object to api.get)

Another possible way is to set a general option in wp-source state like allowCredentials: true and the libraries.source.api.get should use that option to set the credentials value to "include", which is the value that allows the cookies to be shared between subdomains.

I am designing the version 2 of source packages right now and I am actually thinking that we should remove libraries.source.api in favor of a helper like:

import { getApiUrl } from "@frontity/wp-source/helpers";

const myHandler = ({ state, options }) => {
  const apiUrl = getApiUrl({ state, options });
  const response = await fetch(apiUrl);
};

That way you won’t have to build the URL yourself but you get to do the fetch yourself, and have more control over it.

import { getApiUrl } from "@frontity/wp-source/helpers";

const myHandler = ({ state, options }) => {
  const apiUrl = getApiUrl({ state, options });
  const response = await fetch(apiUrl, { credentials: "include" });
};

The apiUrl returned by getApiUrl can be a URL object, which is accepted by fetch. Maybe something like this:

const getApiUrl = ({ state, options, api, endpoint, params }) => {
  // Use options.api or the default.
  const base = api || options.api || state.source.api;
  // Use options endpoint or the default.
  let endpoint = endpoint || options.endpoint || state.wpSource.default.endpoint;
  // Add /wp/v2 to endpoint if needed.
  if (!endpoint.startsWith("/")) endpoint = `/wp/v2/${endpoint}`;
  // Join them and make sure there are no repeated /.
  const api = `${base}/${endpoint}`.replace(/\/+/, "/");
  // Build the URL object.
  const url = new URL(base);
  // Add the params.
  const allParams = ({
    embed: true,
    ...state.wpSource.default.params,
    ...options.params,
    ...params
  })
  Object.keys(allParams).forEach(key =>
    url.searchParams.append(key, allParams[key])
  );

  return url;
};

Other packages like wp-graphql-source will have their own helper to build the API URL.


Two questions:

  • What do you think about this approach?
  • Can you use fetch manually instead of libraries.source.api for now?

I created this topic with the intention of starting a discussion about it, but I have no preferred way to solve this issue.

The helper looks good to me. I was wondering if api.get had to exist in the first place, because we’ll run soon into a situation where we need to extend every HTTP method like api.post and having to maintain that doesn’t make sense I think.

I don’t understand completely the getApiUrl function though. What is options and params? Is options part of a refactorization of the handlers API?

Yes, I wasn’t actually planning on using api.get that much because most of the cases where I want to be authenticated are POST requests, so I already needed to use fetch.

EDIT: I’ve updated the topic title.

Awesome! Thanks for your input @orballo :clap:

Yeah, sorry, I’m thinking already with the new API. I’ll open the Feature Discussion for Source v2 as soon as I can :smile:

Basically, you could configure the handler in the state like this:

export default {
  state: {
    source: {
      handlers: {
        home: {
          pattern: "/",
          options: {
            type: "archive",
            endpoint: "posts"
          }
        },
        category: {
          pattern: "/category/(.*)?/:slug",
          options: {
            type: "taxonomy",
            endpoint: "posts",
            taxonomyEndpoint: "categories"
          }
        },
        ...
      }
    }
  }
}

That way people will be able to configure the handlers with their frontity.settings.js file:

const settings = {
  state: {
    source: {
      handlers: {
        home: {
          pattern: "/blog"
        }
      }
    }
  }
};

Or even create new handlers without writing code:

const settings = {
  state: {
    source: {
      handlers: {
        weirdCategory: {
          pattern: "/weird-category",
          options: {
            type: "archive",
            endpoint: "weird-post-type",
            params: {
              per_page: 50,
              categories: "123,456,789"
            }
          }
        }
      }
    }
  }
};

This is a super brief example of the new Source v2 API, it has much more :sweat_smile:

1 Like