Promoting Foreign Key Data Fetching from React to NuclearJS
Because We Don’t Have GraphQL (Yet)
In some halcyon future, we will just use something like GraphQL to dynamically compose data requests from the server, retrieving no more nor less than we need. But that day is not today.
In the meantime, we need to request resources via the same API our customers use, which sometimes includes supporting data and sometimes does not.
Take, for example, our Job endpoint, as represented in Thrift:
As it happens, a request from the API for job data will include arrays of job users and documents, and only reference via foreign keys to job templates. Setting the business context of a Job aside, you can imagine a simple list of these jobs on a side bar, the selection of which becomes the focus on the main panel.
First Iteration: React Passing Props
When we took our first pass at building a UI around this concept, we let React own a lot of the data management, passing data — sometimes quite a lot — down as props to child components.
Let’s continue with the example of a list of jobs (left sidebar above). Here is a relatively noble first pass:
The key things to notices are:
- The parent component receives as props the currently selected job, a list of all jobs, and an “idObjectCache” which is simply a manually-maintained dumping ground of lookup data
- The parent passes to the child all the same information
- Data munging in the form of chained Underscore functions within getRows (Pardon me, Lodash, of course. Sorry, everyone.)
- A silly little lookup function in the child called templateIdFromName
This is a relatively contained example, but the situation gets rapidly out of hand for more complex areas of the app where we have to marry more disparate data resources.
Add to that, it turns out that buried in the job model is the full team model, which we use in the above code (this.props.job.team.name) but never abstract for other components. On the same page view we’re also making an API call for the current job’s team.
Second Iteration: Foreign Key Data Fetching to NuclearJS
It’s no secret. I love the shit out of NuclearJS. Here’s a rough first pass at dumping Alt in favor of my new crush:
The differences are subtle, and the importance may be non-obvious if you haven’t [yet] used React in anger.
- The parent container now leans on Nuclear getters for retrieving Immutable Lists of data with all the filtering and sorting taken care of
- The only props the component receives relates to UI state (e.g. “fetching jobs”), but it manages its own data retrieval internally, which means the component and its parent can be refactored or moved around safely.
- The Job module is a common module which we use in two different apps, but currentJobId is particular to the WebEx (Web Execution — please forgive the internal jargon) application and thus to its own module
- We get away with the much safer getIn() for deep retrieval.
The result is much less verbose, much easier for our designers to consume, style, and adapt. Great, but what about the data fetching?
Manage Foreign Key Data like Jason Bourne Would
The Nuclear docs offer a great strategy to creating API actions model by model. Since the wrapper we wrote around Thrift returns Promises, many of our model implementations are simple assignments:
exports.fetchAll = Thrift.enhanced.JobRole.index;
But in cases where we want to manage the foreign key data of what comes back, we can swiftly dispatch our foes like Jason Bourne would, thusly:
Note: There are a few places in the above code where we could be more succinct, but at least it is abundantly clear for the next engineer (I hope!) what’s going on.
Intercepting the API Response
The first trick is that depending on the params sent by the UI (i.e. a React component), the action uses different API endpoints. Because the Thrift and Nuclear functions both return Promises, we can jump in the middle and pass the resulting jobs array to processForeignKeys.
Requesting Required Foreign Key Data
The second trick is retrieving a unique list of job template IDs from the job list (for 50+ jobs you might have only 3–4 templates). An index call to templates would have to be paged, and so instead of pulling all, we can fire off requests for just what we need. If the index endpoint ever supports receiving an array of IDs, we could ship that over in one call.
Of course, these actions are fire-and-forget. For free, our getters will update React components when the foreign key data arrives. Smoking hot.
Loading Embedded Foreign Key Data
The third trick is that we can take the job users, job roles, and team data across jobs and load it via a simple receiveAll function we created:
For each model, the receiveAll function just pretends it is handling an API fetch request it initiated. The results are the same.
You may also notice above that while processing duplicative foreign key data across jobs, we hijack uniq() to simply compare IDs. This is a lot easier than doing a deep comparison. though it relies on our confidence that the server won’t send us different data with the same IDs in the same request. If we cannot count on that, we have bigger problems.
The Last Leg: Getters
You made it this far. Let’s get the football in the end zone.
The format for Nuclear getters is a bit sugary at first glance, but it’s great.
- The raw job data itself is stored as an Immutable Map of String<jobId> to Map<job>
- The jobList observes job and job template data, performing lookups as templates become available, returning the results as an Immutable List
- The activeJobList and completedJobList exports simply apply filters and sorts like you would see in any language/framework. You just get one-way data binding for free.
In the end, we have…
- All our API code in a Thrift wrapper
- All our API data stored as Immutable objects, managed end-to-end by Nuclear, including foreign key data dependencies
- All our UI in React with data retrieval and binding localized as much as possible
…which is so easy it frees me up to write a Medium post about it all.
If you found this helpful, don’t feel bad about recommending it.
Oh yeah, and we are hiring our third Front End Engineer if you feel like getting paid to get scrappy with React, Nuclear, and ImmutableJS at a Series-A funded startup backed by Andreessen Horowitz, First Round, Kleiner Perkins, and Google Ventures, among others. Ping me.