These are a set of recommended ways to handle things, they do not enforce anything.
We suggest you read: http://www.meteor-tuts.com/chapters/3/persistence-layer.html first.
- Store queries inside
/imports/api
under their own module and proper path (eg:/imports/api/users/queries/getAllUsers.js
) - Store
links.js
files inside/imports/db
close to their collections definitions. (eg:/imports/db/users/links.js
) - Create a an
/imports/db/index.js
that imports and exports all your collections - In
/imports/db/links.js
which imports all links from collections (eg:import ./posts/links.js
) - In that
/imports/db/index.js
alsoimports './links'
after you imported all collections. - Make sure you import
/imports/db/index.js
in both client and server environments. - For Named Queries, keep
query.js
andquery.expose.js
separated. - Create an
/imports/api/exposures.js
that imports all.expose.js
files, and import that server-side. - When you import your queries suffix their with
Query
- Always
.clone()
modular queries before you use them client and server-side - Store reducers inside
links.js
, if the file becomes too large (> 100 lines), separate them. - Store server-side reducers inside
/imports/api
- as they may contain business logic
If you respect the patterns above you will avoid having the most common pitfalls with Grapher:
Reducer/Links not working?
Make sure they are imported in the environment you use them client/server.
My link is not saved in the database?
Make sure you added it correctly to your SimpleSchema, if you have that.
You will find yourself requiring often same fields for users, such as email
, fullName
, and maybe avatar
.
For that let's create some fragments:
// file: /imports/db/fragments/UserPublic.js
export default {
fullName: 1,
avatar: {
path: 1,
},
email: 1,
}
// file: /imports/db/fragments/index.js
export {default as UserPublicFragment} from './UserPublicFields';
Now use it:
import {UserPublicFragment} from '/imports/db/fragments';
Invoices.createQuery({
number: 1,
total: 1,
user: {
...UserPublicFragment,
billingInfo: {
// etc
}
}
})
You can also compose certain fragments:
import {compose} from 'meteor/cultofcoders:grapher';
import {
UserPublicFragment,
UserBillingFragment,
} from '/imports/db/fragments';
Invoices.createQuery({
number: 1,
total: 1,
user: {
...compose(
UserPublicFragment,
UserBillingFragment
)
}
})
compose()
uses deep extension, so it works how you expect it to work, especially if some fragments have shared bodies.
Do not use special properties inside fragments, such as $filters
, $options
, etc.
If you want to have highly scalable reactive data graphs, think about moving from tailing MongoDB oplog to RedisOplog: https://github.com/cult-of-coders/redis-oplog
Grapher is fully compatible with it. You can configure $options, inside the $filter() on to allow namespaced watchers.
Sample:
export default Messages.createQuery('messagesForThread', {
$filter({filters, options, params}) {
filters.threadId = params.threadId;
options.namespace = `thread::${params.threadId}`;
},
text: 1,
})
Or, if you don't want to expose that, embody the $filter()
server-side:
// query.expose.js
query.expose({
embody: {
$filter({options, filters, params}) {
filters.threadId = params.threadId;
options.namespace = `thread::${params.threadId}`;
}
}
})
Note that embody
can also be a function:
// query.expose.js
query.expose({
embody(body, params) {
// Modify body here
}
})
Using some simple techniques we can make our code much easier to read, and we can make use of a scalable data graph using redis-oplog