ExampleWasTaken/reactify

Manual API handling

Opened this issue · 0 comments

API Handling

Handling requests to the Spotify API ourselves gives us greater flexibility to handle data received by the API.
This issue serves as a first design outline for manual API request functionality.

This issue is closely related to #10.

Design

The API is represented by a class that is accessible through a context. This means that all components that want to use API hooks need to be wrapped inside a <SpotifyWebAPI /> component. This component wraps its children in a context provider which provides the instance of the API to all children.

The class is responsible for storing API related data such as client id, scopes, etc. It does not store things like access token etc. as that is always stored in localStorage.

Auth

User authorization also needs to be handled by us. This is done through the useAuth() hook.

It provides methods for allowing the user to grant Reactify access to their Spotify account.

useAuth()

interface useAuthReturnType {
  setupAuth: (clientId: string, redirectUrl: string, scopes: string[]) => void; // Is stored in the API Class instance to be accessed by other components.
  requestAuthentication: () => Promise<AuthorizationResponse>;
  requestAccessToken: (authenticationCode: string) => Promise<AccessTokenResponse>;
  getAccessToken: () => AccessToken | null;
  logout: () => void;
}

// For convenience
interface AuthorizationResponse {
  code: string;
  state: string; 
}

interface AccessTokenResponse {
  access_token: string;
  token_type: 'Bearer';
  scope: string;
  expires_in: number;
  refresh_token: string;
}

interface AccessToken {
  token: string;
  expires: number;
  expires_in: number;
  token_type: string;
  refresh_token: string;
}

API

Data is retrieved through custom hooks for each endpoint group of the Spotify Web API. E.g. useAlbums().
Each endpoint hook returns an object with functions to send a request to an endpoint. E.g. getAlbum(id).

The actual request is handled by a useFetch() hook which handles caching, URL formatting etc.

All hooks are named after the endpoint group they handle.

Types

Generic hook structure

All hooks are of the following type:

type EndpointGroupHook = () => { retrieveDataFunction_one, retrieveDataFunction_two, ... }

Generic function structure

All functions returned by endpoint group hooks are of type:

type EndpointGroupHookFunction = (endpointParams: unknown) => {
  staleData: (StaleData<ReturnType> | null),
  freshData: Promise<FreshData<ReturnType>>
}

staleData

This property is returned synchronously and directly from cache to provide the component with something to render. If the cache doesn't contain any data this will be null.

freshData

Property contains the fresh data. If the cached data is still fresh (meaning this and staleData are equal) this will be a resolved promise. Otherwise it is a pending promise that will resolve once the data is fetched.

ReturnType

ReturnType is the actual data like a Playlist object, etc.

Special Hooks

use_internal_fetch()

This hook is used to create requests to the API. It handles the cache as well as URL formatting, etc.
As the name suggests this hook should only be used internally. By other API hooks that is.

use_internal_cache()

This hook is used to manage the caching behavior like automatically flushing content to disk, invalidating stale data, etc.
As the name suggests this hook should only be used internally. By other API hooks that is.

use_internal_spotifyAPIContext()

This hooks is used to get the SpotifyAPIContext. It either returns the context or throws an error if the context is undefined which indicates that the calling hook is executed outside the <SpotifyWebAPI /> wrapper component.
As the name suggests this hook should only be used internally. By other API hooks that is.

useCache()

This hook provides functions to interact with the cache. It is indented to be used by components to clear and flush the cache.

useCurrentUser()

This hook provides simpler access to the Current User endpoints. To be compliant with the documentation all endpoint group hooks also provide functions for their respective Current User endpoints.