npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dabapps/redux-api-collections

v0.4.6

Published

Type-safe helpers for dealing with Rest-Framework backed collections in Typescript

Downloads

22

Readme

Redux API Collections

Build Status

Standardised, type-safe ways of interacting with DRF-style APIs.

Redux API Collections aims to simplify and standardise the way in which we access Django Rest Framework endpoints when writing Redux applications in Typescript. It offers reducers for both collection endpoints and individual item endpoints, as well as the necessary tools to shunt data into them.

Installation

Install via NPM:

npm install @dabapps/redux-api-collections --save

Getting Started

This project requires use of Redux-Thunk to correctly dispatch actions. Collection endpoints must follow Django Rest Framework's pagination logic. Item endpoints should return a single item.

To begin with, you will need to provide two interfaces that map given paths to a type or interface that represents a well-formed instance of an item being returned from the server. You will need one collection for collection paths, and one for item paths. If you are lucky, these may be the same interface. These paths are expected to match with your server's /api/[endpoint]/ url and /api/[endpoint]/[id]/ respectively.

type User = Readonly<{
  id: string,
  name: string
}>;

interface Collections {
  users: User  // This is /api/users/ on the server
}

interface Items {
  users: User  // This is /api/users/[id]/ on the server
}

With these two (or one) interfaces defined, you will need to attach them to your store. This is as simple as including the following code:

interface Store {
  collections: CollectionStore<Collections>,
  items: ItemStore<Items>
}

You can, in theory, mount the stores elsewhere, but this is not recommended.

The next step is providing a mapping between paths and functions that will produce instances of our objects from the server. Typically, you'll want to use Immutable's Record type or our own SimpleRecord, but if you trust the server to always give back fully realised objects, the identity function will suffice.

const collectionToRecordMapping = {
  users: (user: User) => user
}

const itemToRecordMapping = {
  users: (user: User) => user
}

Now we have mappings, plus interfaces, we can bring it all together. You are likely to want to also mount the Responses reducer, as this gives visibility to which endpoints are currently in use.

import { Collections, responsesReducer } from '@dabapps/redux-api-collections';

const collections = Collections<Collections, Items>(collectionToRecordMapping, itemToRecordMapping);

// elsewhere
combineReducers({
  collections: collections.reducers.collectionsReducer,
  items: collections.reducers.itemsReducer,

  responses: responsesReducer,
});

The Collections object gives you access to type-safe helper functions for requesting data.

For example, we want to get a specific collection endpoint. We can call collections.actions.getCollection to request data from the server.

const action = collections.actions.getCollection('users', options);
dispatch(action);

// Later

getCollectionByName(store.collections, 'users');
getCollectionResultsByName(store.collections, 'users');

Many functions can also be namespaced by a subgroup field - this will allow you to request the same endpoint from multiple places without overwriting their results.

const action = collections.actions.getCollection('users', options, 'loginPage');

Subpaths

There are often situations where you want to have collections or items at a path with some form of ID in the middle of it. Provide your paths as something path-to-regexp understands:

interface Collections {
  'users/:userId/other-users': User  // This is /api/users/:userId/other_users/ on the server
}

interface Items {
  'users/:userId/other-users': User  // This is /api/users/:userId/other_users/[id]/ on the server
}

With these two paths, we need to be able to specify a separate userId for it to make sense. We can construct helper functions for working with this:

const collectionSubpath = collections.collectionAtSubpath('users/:userId/other-users', {userId: '12345'});
const itemSubpath = collections.itemAtSubpath('users/:userId/other-users', {userId: '23456'});

You pass a dictionary for each of the optional positions in the path.

The two resulting structures contain pre-parameterized sets of action creators, as well as accessor functions.

collectionSubpath.actions.getCollection(options);  // Contains a pre-bound action for each of the normal collection actions
collectionSubpath.getSubpathCollection(store);  // Our collection object
collectionSubpath.getSubpathCollectionResults(store);  // Just the items

itemSubpath.actions.getItem('12345');
itemSubpath.getSubpathItem(store);

I'm stuck!

Help, my API isn't mounted at /api/

You can parameterize the Collections object with different base URLs, for those odd cases where your API is different from the others we use.

import { Collections } from '@dabapps/redux-api-collections';
const collections = Collections<Collections, Items>(collectionToRecordMapping, itemToRecordMapping, { baseUrl: '/another-base-url/' });

Help, I need to run some form of custom step on Collections/Reducers and make it react to a specific action

You can provide custom Reducers that will be called by the built-in ones for post-processing the state when actions come in.

import { Collections } from '@dabapps/redux-api-collections';

function myCustomCollectionReducer(state: CollectionStore<Collections>, action: AnyAction): CollectionStore<Collections> {
  // Usual Reducer stuff
}

function myCustomItemReducer(state: ItemStore<Items>, action: AnyAction): ItemStore<Items> {
  // More reducer stuff
}

const collections = Collections<Collections, Items>(collectionToRecordMapping, itemToRecordMapping, {
  collectionReducerPlugin: myCustomCollectionReducer,
  itemReducerPlugin: myCustomItemReducer
});

Code of conduct

For guidelines regarding the code of conduct when contributing to this repository please review https://www.dabapps.com/open-source/code-of-conduct/