Nested Models with separate API calls and separate stores (using custom references) - mobx-state-tree

I'm wondering what's the best practice to do two separate fetches to data that would belong to the same Model. One to get all Users data and a separate one that would request their Traits and add them to each User.
I think I could create a reference in User, to fill the data, but im not sure how to create the custom reference since it should be an array.
export const User = types
.model('User', {
id: types.identifierNumber,
...
traits: types.maybeNull(TraitsbyUserReference),
})
const TraitsbyUserReference = types.maybe(
types.reference(Trait, {
get(identifier: string, parent): {
return (parent as Instance<typeof TraitsStore>).getAllTraits()
},
set(value) {
return value; // this is what doesnt work out because i'm fetching a whole array
},
}),
)
Also, is this a good practice or are there other better ways of getting this result?
Thanks!

In terms of defining the model, you might try switching from maybeNull to an optional array with a default value in your model -
...
traits: types.optional(types.array(Trait), []),
...
As such, the model will always be instantiated with an empty traits collection.
In terms of the TraitsbyUserReference, I am not following what abstraction that you need with the dynamic store look-up. You could create an action (e.g. User.actions(self => ...)) to look-up the traits as a separate api -
getUserTraits(){
/* this will vary based on your implementation of TraitsStore and how it is injected */
const traits = self.TraitsStore.getAllTraits(self.id);
self.traits = traits;
}

Related

GraphQL Schema Language Handle Map Type from Uncontrolled API [duplicate]

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

Why or when to use Laravel resources/collections

I do not understand why or when to use the resources in Laravel https://laravel.com/docs/7.x/eloquent-resources .
See this controller :
public function show(School $school)
{
// return response()->json($school, 200);
return new SchoolResource($school);
}
The both return solutions returned this kind of response :
{
"data": {
"id": "4f390a7b-3c3f-4c23-9e6a-dd4429cf835d",
"name": "school name",
.......,
The data are the results of a query automatically injected (here : $school).
And same question for a collection of resources. Imagine this controller :
public function index(Request $request)
{
try {
$schools = $this->schoolRepository->all($request->all());
} catch (\Exception $e) {
return response()->json(['message' => 'Bad request'], 400);
}
return SchoolResource::collection($schools);
// return response()->json($schools, 200);
}
If I need to add some fields , I can do that either in the model or in the repository.
I often read that this resource notion is important to understand and to use. But for the moment I do not see when or why I should use it. I certainly must not understand something!
There are a couple of primary reasons to use resources to manage your return values even if your resources don't do anything than pass data through today, you may want it to do something different in the future.
A small list of reasons why resources are really useful
1. Data manipulation, specific to clients (i.e. js applications consuming your api)
If you start manipulating data in your models using mutators (getters / setters). Your internal application now has to work with these constraints. Many times its easier to work with the raw data internally and then just allow your resources to manipulate the data for the client.
2. Conforming to an API specification, such as JSON API v1.0
Whilst you will likely need logic in your application to handle schemas like this, your models and controllers should not. The resource has a critical role here to organise the data for your consumer applications in a compliant fashion.
3. The age old mantra, separation of concerns
This goes hand in hand with point 1, it is not a model or a controllers responsibility to map data to what you expect your consumer applications to receive.
Building on your example
You currently have the following in the show route of your resource controller.
public function show(School $school)
{
// return response()->json($school, 200);
return new SchoolResource($school);
}
This need not change, even if your API specification does (reason 1 and 3).
Adding fields, yes, you'll need to add them to your model but lets use this to actually do something meaningful with your resource.
We've created a migration for a new JSON field ratings. Ratings has a data structure like this:
[
{
name: string,
value: float,
}
]
For reasons such as media scrutiny, we never want to expose all the rating data to our publically available front end consumer apps. Instead we want to provide an average score of all ratings.
Now we could do this in the model, but is it the models responsibility to do this? Not really, the model should be handling and dealing in raw / discreetly modified data only. So is it the controllers responsibility? No, the controller coordinates what should be done and is not interested in specific details or the data.
So where do we do this? Enter your resource that was handily already set up.
class School extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'rating' => $this->getRating(),
];
}
/**
* Mean rating rounded to one decimal place
* #return float
*/
protected function getRating()
{
return (round(array_reduce($this->ratings, function($acc, $curr) {
$acc += $curr['value'];
return $acc;
}, 0) / count($this->ratings), 1);
}
}
The resource is where we have manipulated data specifically for our responses and left our internal data modelling un-touched and clean without pollution from specific nuances of our consuming applications.
More importantly, if you just returned return response()->json($school, 200); from your controller, your data structures will not match and you would be exposing some sensitive data to your front end applications.
Additional (24/12/21)
It's worth noting that if, for example, the data that you are manipulating is required by many different views / resources then we have a number of options.
Traits, create a trait that adds the getRating method. Downside, every class that needs this must import and declare the trait. Upside, your code is dry.
Model scopes, add a scope to your model that does the data processing via SQL (or your DB QL of choice). Downside, slight pollution of the model. Upside, super quick.
Append the data to the model (https://laravel.com/docs/8.x/eloquent-serialization#appending-values-to-json) using an accessor that runs the getRating code to set the data on the model. Upside, clean and usable throughout the application. Downsides pollutes the model a little and data only available in the JSON representation of the model.
Decorate the resource. This allows you to intercept and modify/add to the result of the toArray method in the decorated resource. Upsides not many. Downsides, obfuscated and confusing implementation detail. I wouldn't recommend this approach.
Helper function. Rather than have the method on the resource, create a helper that takes the ratings array and returns the result. Upside, simple implementation. Downsides, none that I can thing of.
So after thinking about this alot I think that I would likely do what I originally wrote in this answer. If I need to re-use I would likely create a helper. If I was concerned about performance I would use a model scope to do the calculations in SQL (if possible, remember it's a JSON field). Taking a step further, if many models require this logic, a trait for those models would be my next step (this only applies if you go down the SQL calculation route).

React - defaultProps vs ES6 default params when destructuring (performances issues)

I just came across a question about React performances when settings default values in one of my stateless functional components.
This component had a defaultProps which defined row: false, but I didn't like it because the defaultProps is at the end of the file, which actually makes it harder to see. And thus, we aren't aware of the default property. So I moved it to the function declaration directly and assigned it using ES6 default value for parameters.
const FormField = ({
row = false,
...others,
}) => {
// logic...
};
But then we argued with a coworker about this being a good idea or not. Because doing so may seem trivial, but may also have a great impact upon performances since react is not aware of the default value.
I believe in this case, it's trivial. Because it's a boolean and not an object/array and therefore won't be seen as a different value during reconciliation.
But, let's now see a more advanced use-case:
const FormField = ({
input: { name, value, ...inputRest },
label = capitalize(name),
placeholder = label,
row = false,
meta: { touched, error, warning },
...others,
}) => {
// logic...
};
Here, I base the value of placeholder from label, which itself is based on input.name. Using ES6 destructuring with default values for parameters makes the whole thing quite easy to write/understand and it works like a charm.
But is it a good idea? And if not, then how would you do it properly?
I talked to several people on Discord #reactiflux channel and actually got the answer I was looking for.
There are basically three use-case with React components, and in some of them, destructuring params will impact performances so it is important to understand what's going on under the hood.
Stateless functional component
const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
return (
<div>{displayName}</div>
);
};
This is a stateless, functional component. There is no state, and it is functional because it is not a Class instance, but a simple function.
In this case, there is no life-cycle, you cannot have a componentWillMount or shouldComponentUpdate or constructor there. And because there is no management of the life-cycle, there is no impact on performances whatsoever. This code is perfectly valid. Some may prefer to handle the default displayName value within the function body, but in the end it doesn't really matter, it won't impact performances.
Stateless non-functional component
(Do not do this!)
class MyComponent extends React.Component {
render() {
const { name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() } = this.props;
return (
<div>{displayName}</div>
);
}
}
This is a stateless non-functional component. There is no state, but it is not "functional" since it is a class. And because it is a class, extending React.Component, it means you will have a life-cycle. You can have componentWillMount or shouldComponentUpdate or constructor there.
And, because it has a life-cycle, the way of writing this component is bad. But why?
Simply put, React offers a defaultProps attribute, to deal with default props values. And it is actually better to use it when dealing with non-functional components, because it will be called by all methods that rely on this.props.
The previous code snippet creates new local variables named name and displayName, but the default values are applied for this render method only!. If you want the default values to be applied for every method, such as the ones from the React life-cycle (shouldComponentUpdate, etc.) then you must use the defaultProps instead.
So, the previous code is actually a mistake that may lead to misunderstanding about the default value of name.
Here is how it should be written instead, to get the same behavior:
class MyComponent extends React.Component {
render() {
const { name, displayName = humanize(name), address } = this.props;
return (
<div>{displayName}</div>
);
}
}
MyComponent.defaultProps = {
name: 'John Doe',
address: helper.getDefaultAddress(),
};
This is better. Because name will always be John Doe if it wasn't defined. address default value was also dealt with, but not displayName... Why?
Well, I haven't found a way around that special use-case yet. Because the displayName should be based on the name property, which we cannot access (AFAIK) when defining defaultProps. The only way I see is to deal with it in the render method directly. Maybe there is a better way.
We don't have this issue with the address property since it's not based on the MyComponent properties but rely on something totally independant which doesn't need the props.
Stateful non-functional component
It works exactly the same as "Stateless non-functional component". Because there is still a life-cycle the behavior will be the same. The fact that there is an additional internal state in the component won't change anything.
I hope this helps to understand when using destructuring with components. I really like the functional way, it's much cleaner IMHO (+1 for simplicity).
You may prefer to always use defaultProps, whether working with functional or non-functional components, it's also valid. (+1 for consistency)
Just be aware of the life-cycle with non-functional components which "requires" the use of defaultProps. But in the end the choice is always yours ;)
Edit 10-2019: defaultProps will eventually be removed from React API at some point in the future, see https://stackoverflow.com/a/56443098/2391795 and https://github.com/reactjs/rfcs/pull/107 for the RFC.
One big difference between defaultProps and default function parameters is that the former will be checked against propTypes. The require-default-props rule of eslint-plugin-react explains it very well.
One advantage of defaultProps over custom default logic in your code is that defaultProps are resolved by React before the PropTypes typechecking happens, so typechecking will also apply to your defaultProps. The same also holds true for stateless functional components: default function parameters do not behave the same as defaultProps and thus using defaultProps is still preferred.
Looking at the advanced use-case you have, you're adding unnecessary properties to the component. label and placeholder are dependent on other properties being passed in and in my opinion, should not be listed as a parameter of the component itself.
If I were trying to use <FormField /> in my application and I needed to look to see what dependencies that specific component has, I would be a little bit confused as to why you're creating parameters that are based off of other parameters. I would move the label and placeholder into the function's body so it's clear they are not component dependencies but simply side effects.
As far as performance is concerned here, I'm not sure there would be a significant difference in either way. Stateless components don't really have a 'backing instance' that stateful components do, which means there isn't an in memory object keeping track of the component. I believe it's just a pure function of passing parameters in and returning the view.
On that same note.. adding the PropTypes will help with the type checking.
const FormField = ({
input: { name, value, ...inputRest },
row = false,
meta: { touched, error, warning },
...others,
}) => {
const label = capitalize(name),
const placeholder = label,
return (
// logic
);
};
FormField.propTypes = {
input: PropTypes.shape({
name: PropTypes.string.isRequired,
value: PropTypes.string,
}).isRequired,
meta: PropTypes.shape({
touched: PropTypes.bool.isRequired,
error: PropTypes.bool.isRequired,
warning: PropTypes.bool.isRequired,
}).isRequired,
row: PropTypes.bool.isRequired,
};

In Meteor, where do I model my business rules?

Beginner question : I've worked through the Try Meteor tutorial. I've got fields in my HTML doc, backed by helper functions that reference collections, and BOOM --> the fields are updated when the data changes in the DB.
With the "Hide completed" checkbox, I've also seen data-binding to a session variable. The state of the checkbox is stored in the Session object by an event handler and BOOM --> the list view is updated "automatically" by its helper when this value changes. It seems a little odd to be assigning to a session object in a single page application.
Through all this, my js assigns nothing in global scope, I've created no objects, and I've mostly seen just pipeline code, getting values from one spot to another. The little conditional logic is sprayed about wherever it is needed.
THE QUESTION... Now I want to construct a model of my business data in javascript, modelling my business rules, and then bind html fields to this model. For example, I want to model a user, giving it an isVeryBusy property, and a rule that sets isVeryBusy=true if noTasks > 5. I want the property and the rule to be isolated in a "pure" business object, away from helpers, events, and the meteor user object. I want these business objects available everywhere, so I could make a restriction, say, to not assign tasks to users who are very busy, enforced on the server. I might also want a display rule to only display the first 100 chars of other peoples tasks if a user isVeryBusy. Where is the right place to create this user object, and how do I bind to it from my HTML?
You can (and probably should) use any package which allows you to attach a Schema to your models.
Have a look at:
https://github.com/aldeed/meteor-collection2
https://github.com/aldeed/meteor-simple-schema
By using a schema you can define fields, which are calculated based on other fields, see the autoValue property: https://github.com/aldeed/meteor-collection2#autovalue
Then you can do something like this:
// Schema definition of User
{
...,
isVeryBusy: {
type: Boolean,
autoValue: function() {
return this.tasks.length > 5;
}
},
...
}
For all your basic questions, I can strongly recommend to read the DiscoverMeteor Book (https://www.discovermeteor.com/). You can read it in like 1-2 days and it will explain all those basic questions in a really comprehensible way.
Best Regards,
There is a very good package to implement the solution you are looking for. It is created by David Burles and it's called "meteor-collection-helper". Here it the atmosphere link:
You should check the link to see the examples presented there but according to the description you could implement some of the functionality you mentioned like this:
// Define the collections
Clients = new Mongo.Collection('clients');
Tasks = new Mongo.Collection('tasks');
// Define the Clients collection helpers
Clients.helpers({
isVeryBusy: function(){
return this.tasks.length > 5;
}
});
// Now we can call it either on the client or on the server
if (Meteor.isClient){
var client = Clients.findOne({_id: 123});
if ( client.isVeryBusy() ) runSomeCode();
}
// Of course you can use them inside a Meteor Method.
Meteor.methods({
addTaskToClient: function(id, task){
var client = Clients.findOne({_id: id});
if (!client.isVeryBusy()){
task._client = id;
Tasks.insert(task, function(err, _id){
Clients.update({_id: client._id}, { $addToSet: { tasks: _id } });
});
}
}
});
// You can also refer to other collections inside the helpers
Tasks.helpers({
client: function(){
return Clients.findOne({_id: this._client});
}
});
You can see that inside the helper the context is the document transformed with all the methods you provided. Since Collections are ussually available to both the client and the server, you can access this functionality everywhere.
I hope this helps.

Upon updating, how to compare a model instance with its former state

I'm using Sails.js v0.10.5, but this probably applies to more general MVC lifecycle logics (Ruby on Rails?).
I have two models, say Foo and Baz, linked with a one-to-one association.
Each time that data in a Foo instance changes, some heavy operations must be carried out on a Baz model instance, like the costlyRoutinemethod shown below.
// api/model/Foo.js
module.exports {
attributes: {
name: 'string',
data: 'json',
baz: {
model: 'Baz'
}
},
updateBaz: function(criteria,cb) {
Foo.findOne( criteria, function(err,foo) {
Baz.findOne( foo.baz, function(err,baz) {
baz.data = costlyRoutine( foo.data ); // or whatever
cb();
});
});
}
}
Upon updating an instance of Foo, it therefore makes sense to first test whether data has changed from old object to new. It could be that just name needs to be updated, in which case I'd like to avoid the heavy computation.
When is it best to make that check?
I'm thinking of the beforeUpdate callback, but it will require calling something like Foo.findOne(criteria) to retrieve the current data object. Inefficient? Sub-optimal?
The only optimization I could think of:
You could call costlyRoutine iff relevant fields are being updated.
Apart from that, you could try to cache Foo (using some locality of reference caching, like paging). But that depends on your use case.
Perfect optimization would be really like trying to look into the future :D
You might save a little by using the afterUpdate callback, which would have the Foo object already loaded so you can save a call.
// api/model/Foo.js
module.exports {
attributes: {
...
},
afterUpdate: function(foo,cb) {
Baz.findOne( foo.baz, function(err,baz) {
baz.data = costlyRoutine( foo.data ); // or whatever
cb();
});
}
}
Otherwise as myusuf aswered, if you only need to update based on relevant fields, then you can tap into it in the beforeUpdate callback.
Btw, instance functions should be defined inside attributes prop, lifecycle callbacks outside.

Resources