This page provides my answers to some of the questions about Meteor asked by students during Spring 20191.
No. The settings.development.json file provides an example to developers of how to define admin accounts on startup. As you will learn in the deployment module, you will define a different file named settings.production.json in which you store “real” account credentials for initial startup. For more details on settings files in Meteor, please see The Meteor Chef: Making use of settings.json.
More generally, you should always make repositories public unless there is a compelling reason to make them private. You should make as much of your professional work visible as possible: even “draft” versions of systems provide evidence to graduate schools and future employers that you are actually attempting to do development.
In general, use .jsx whenever you use (React) components in the file and .js otherwise.
Functions can be in any file that is import
ed where the function is used. However, if a function is only used on one file it makes sense to place the function in that one file. addData
is only used in initializing the seed data for the Stuffs
collection, so it is located in the file that handles the Stuffs
collection on the server side.
The documentation for this.userId
is here. If the user is logged in, this.userId
exists and is a truthy value. If the user is not logged in, this.userId
is undefined
, which is a falsy value.
See the Meteor Guide for documentation on why this.userId
works.
As with the previous question, Meteor.settings.defaultData
will be a truthy value if it exists and a falsy value otherwise.
You have already done something like this; note though that the template basically includes everything that you would need on a site. Most websites that are complex enough to merit using Meteor will have some sort of user accounts and data associated with those accounts.
That said, it is typically not in your best interest to start a Meteor project without using some sort of boilerplate template. This is because templates help ensure that as your application grows in complexity, the organization of the system remains stable. Templates also implement a “design pattern”: a commonly recognized idiom for software development. meteor-application-template-react is not the only template available for Meteor, but it’s the one that will best serve you for developing your final project.
At the top-level, yes. Meteor has rules for where to place file in order for them to be automatically loaded. Essentially, anything covered in Example directory layout should be exactly as is; you have some flexibility in naming anything else.
Directory names are also useful as documentation: the files in /app/imports/startup should have something to do with what happens when the server starts up, /client directories are only visible on the client side, /server directories contain files only visible on the server side, etc. Something like the stuff directory for /app/imports/api/stuff/stuff.js could be named differently though; however, we name it stuff in accordance with convention and general common sense.
Yes.
Technically yes, but this is a design-level error. All of your content should appear in the imports/ directory.
No. In this class, we distinguish between the landing page, which is the page that all users see when they navigate to the site, and the home page, which is the page that users see immediately after successfully logging in.
So, the home page shows private content specific to a user, while the landing page shows public content to any visitor to the site.
/app/client/main.html is the base of the HTML rendered in the browser. Although this main.html file does not directly load the main.js or style.css files, Meteor automatically loads everything in /app/client, and it is /app/client/main.js that imports all files in /app/imports/startup/client, including /app/imports/startup/client/startup.jsx. This startup.jsx file then import
s and render
s /app/imports/ui/layouts/App.jsx.
/app/client/main.js and /app/server/main.js are defined as special files that load other files for the client and server sides respectively; see the relevant documentation here. The /app/imports directory is not directly loaded into the application by default, making it necessary to import
files within those directories in /app/client/main.js and /app/server/main.js.
Because server-side code has complete access to the database, and you almost never want every user to have access to all of the data (such as a list of all registered users and their email addresses).
When you use a computer, you have to save your work before shutting down the computer. That data is stored in a file and you can open that file later to resume your work. Databases serve the same purpose: your application stores data in the database so the data is preserved even after the application has closed.
The technically correct answer is /app/.meteor/local/db, though that is probably not of direct use to you since the data is not stored in a human-readable format. Aside from a tired attempt at sarcasm, this does show that the data is in the project directory, so you do not have to worry about data from one project intermingling with data from another project2.
The more useful answer is that you do not have to worry about where the data is stored: MongoDB takes care of preserving the data for you.
If we look at the users
collection in the MongoDB console, we see:
meteor:PRIMARY> db.users.find()
{ "_id" : "RvkbtmXXDoRDYeohc", "createdAt" : ISODate("2019-03-11T01:48:08.812Z"), "services" : { "password" : { "bcrypt" : "$2a$10$mS4VO9apDZC.Ul6U1YtfHOtgsY8JwVlCNq7muej4GgCTGTRyiW9K6" } }, "username" : "admin@foo.com", "emails" : [ { "address" : "admin@foo.com", "verified" : false } ], "roles" : [ "admin" ] }
{ "_id" : "Bf2XizdLC5fRq4BCk", "createdAt" : ISODate("2019-03-11T01:48:09.014Z"), "services" : { "password" : { "bcrypt" : "$2a$10$OZh60hVYwNNBVJ.SZ4q6oegTEHdFRS4SSpDKbNMTxSZIa7y/pnE7." }, "resume" : { "loginTokens" : [ { "when" : ISODate("2019-03-11T02:24:40.264Z"), "hashedToken" : "Oz02OPH48WBQeyIJ3M6nIztlYHfok6V/uWBG9VDPuSM=" } ] } }, "username" : "john@foo.com", "emails" : [ { "address" : "john@foo.com", "verified" : false } ] }
In particular, note that the password
field for the first user has a value of { "bcrypt" : "$2a$10$mS4VO9apDZC.Ul6U1YtfHOtgsY8JwVlCNq7muej4GgCTGTRyiW9K6" }
. This is very clearly not the default password provided in /config/settings.development.json. Storing passwords as plain text is very much a bad idea; instead, we apply a hash function to the password and store that hashed value. When someone attempts to log in as the user, we then use the same hash function. If the hashed value of the input matches the value stored in the database, then we are reasonably certain that the password is correct5; otherwise we know we can reject the login.
Per the documentation for meteor/accounts-base
, “by default, the current user’s username
, emails
and profile
are published to the client”; our users
have no profile
field and we add in roles
, but the password
field is not included in the list of published fields and therefore will not appear in the developer tools.
bcrypt allows us to hash passwords.
Yes, but it is probably not worth the time. As noted on the course website, the performance issues with bcrypt will only occur on logins and those are relatively uncommon. One might even argue that slower logins are a security feature as it forces an attacker to wait longer between attempts to break in, and after a certain point said attacker may well decide that your site is not a time-efficient target.
We use the seed data in /config/settings.development.json to populate the database the first time that we start the application. This gives us enough data to test the application: the seed data for meteor-application-template-react allows us to immediately log in and interact with the website without having to touch the MongoDB console.
Yes; see the attachSchema
documentation.
Yes, but this is not recommended practice as the Meteor.users
collection has a number of specialized behaviors that might interact negatively with your design goals. A more simple approach is to define a new collection called something like UserProfiles
, and provide a field in that collection that stores the userID associated with that document.
See the Simple Schema documentation; min
is easy enough for the minimum length, though you may have to write a separate function to perform custom validation or use regular expressions.
See the documentation for Accounts.changePassword
.
See the Meteor notes on case sensitivity. In other systems that do not provide helper methods to take care of this, you might convert the usernames or email addresses to lowercase before saving them or comparing them to values in the database.
This indicates that the email address has not been verified yet: it may be a valid email address but we do not know that it actually exists and belongs to the user. Accounts.sendVerificationEmail
allows us to start that verification process.
Tracker
listens for changes on your data sources; see the Simple Schema documentation for notes on how this is integrated with Simple Schema.
No, by that point the values from "defaultData"
are already in the database and removing them from the seed data will have no impact on the database.
No, both Stuff
and StuffAdmin
are publications: they contain data from the database but are not databases themselves.
The withTracker
function provides access to the necessary publications from the database through setting up subscriptions; see more details in the official documentation.
It is possible in MongoDB to define collection-level access control that prevents users from modifying the contents of the database. This effectively results in immutable data.
However, since the MongoDB admin defines the access control, the data is still not completely immutable: admin users can still modify it. This was pretty much a fundamental fact of life for databases—that admins can alter data—until the invention of the blockchain. Indeed, one way to look at the motivation for the development of blockchain technology was to prevent database admins from being able to alter the contents of a database!
Yes, through GraphQL.
Use Collection#remove
.
import
is not analagous to the #include
in C and C++: it does not insert the entire imported file. Instead, you must export whatever you want to import in a different file. This may seem cumbersome at first, but this system allows for more flexibility and efficiency since you only import what you actually need. For example, you only import the Semantic UI React components that you actually use in the file, not the entire semantic-ui-react
library.
See the next question.
Use export default
when there is only a single entity in a file that must be made visible to other files. When there are multiple entities in a file that must be made visible to other files, then use export
.
The choice of export
vs. export default
impacts on the syntax of the import statement. Take a look at the following code:
import { Stuffs } from '/imports/api/stuff/stuff';
import StuffItem from '/imports/ui/components/StuffItem';
In general, you use curly braces in your import statement when the associated file exported multiple entities. You don’t use curly braces when the associated file exported a default entity. So, the stuff.js
file exported multiple entities (one of which was named Stuffs
) while the StuffItem.jsx file exported (by default) a single item.
Note that the name referenced when importing a default export is irrelevant. The export
statement in /app/imports/ui/components/NavBar.jsx is:
export default withRouter(NavBarContainer);
However, the import
statement in /app/imports/ui/layouts/App.jsx is:
import NavBar from '../components/NavBar';
Even though the value exported from NavBar.jsx is the return value from calling withRouter on NavBarContainer, we can refer to it as NavBar
in App.jsx.
‘publish’ defines a set of data on the server. The subscriptions you create on the client side with Meteor.subscribe
retrieve that data from the server to use in your components and pages. Unlike SQL views, MongoDB will automatically update these subscriptions if the original collection is modified.
You already are; everything in /app/imports/ui extends React.Component
. You could thus make a component that extends React.Component
and have other components that inherit from the first component.
Both ProtectedRoute
and AdminProtectedRoute
are defined in /app/imports/ui/layouts/App.jsx; ProtectedRoute
is a Route
with additional logic to determine whether the user is logged in (the const isLogged = Meteor.userId() !== null
).
An exact path must be an exact match. The Landing
page requires that we be at ”/”, not some other path such as “/signin” that merely contains a /.
withTracker
is actually called in the export
itself; note the presence of parentheses for the withTracker
function.
Yes; see /app/imports/ui/components/NavBar.jsx for an example.
Yes. You would have to make changes to the user interface (i.e. the ui/ directory), the database (i.e. the api/ directory), and to the default initialization data (i.e. settings.development.json).
Without the subscription to Stuff
, the ListStuff page would not have access to the Stuffs
to display. Try it and see what happens.
No. Look through the rest of the file: Route
is imported from react-router-dom
and both ProtectedRoute
and AdminProtectedRoute
are defined in App.jsx.
ProtectedRoute
is a function that returns a Route
. This Route
has all of the properties that were passed to the ProtectedRoute
.
The publication on the server side specifies a subset of the data in the database; the subscription on the client side requests that this data be mirrored in the client-side MiniMongo database. By analogy, Dr. Johnson publishes a screencast and you subscribe to it (in the form of watching the video).
The this.ready()
call signals that the data transfer is complete. Without this, any subscriptions on the publications would continue to wait for data indefinitely.
Yes, though the question itself hints at why this is a bad idea: you would have to include the header and footer in each page in your application. Placing the header and footer in /layouts/App.jsx makes the header and footer appear on every page.
Yes, though you would have to &&
the clauses together yourself.
The :_id
is replaced with the _id
of the Stuff
being edited.
Switch
is very much like the switch
statement that you are familiar with: it directs program flow based on a single value. This is a very specific switch
that decides which page to render based on the path
of the current page. If the path
for a Route
within the Switch
is equal to the current path in the browser, then Meteor will render the component
for that Route
. The last Route
in the Switch
does not include a path
, making it behave much as the default
case would: if none of the above paths match, the NotFound
component is rendered.
You will note the lack of any break
statements for the Switch
. break
is necessary for general-purpose switch
statements because they must support fall-through. In this case though, we know that there is a one-to-one relationship between paths and pages: each path corresponds to a single page and vice versa, so there is no need for fall-through here.
Yes, you would just have to write conditional logic to prevent admins from seeing those pages.
The render
method of a React.Component
returns the JSX code to display for that component.
The URLs are defined in /app/imports/ui/layouts/App.jsx; these paths can be whatever you want them to be. For example, if we wanted to create a page for the user to edit his or her account information, we cannot reuse the path "/edit/:_id"
because that is already the path for the EditStuff
page. However, we could instead create a new ProtectedRoute
with a path "/user/edit/:_id"
.
You used a single .js file for your React projects because those applications only had a single page (ex. the home page for Island Snow). From a pedagogical standpoint, this let you focus on learning about React instead of having to figure out how various files interact with one another. From a software engineering standpoint, a single web page does not provide much opportunity for code reuse outside of the CSS, so the benefits of splitting the code across multiple files are not as apparent.
At this point in your development, neither of those motives for using a single file still hold true. You are already familiar with React, at least enough to use the Semantic UI React documentation as a reference. You are now working on increasingly complex projects with multiple pages, all of which share the same header and footer along with some smaller components.
One might argue that it is still possible to have multiple components in the same file. However, it is much easier to import components when they are in their own files: if NavBar
was in /app/imports/ui/pages/Landing.jsx you would have to somehow remember that every time you wanted to use or modify that NavBar
. With NavBar
defined within a file named NavBar.jsx, there should be no confusion as to where that component is located.
ProtectedRoute
requires a user who is logged in whereas AdminProtectedRoute
requires an admin user who is logged in.
Not really; they both restrict access in some way, but protected
has to do with class inheritance whereas ProtectedRoute
is defined in the template as a route requiring the user to be logged in.
Yes; see the Meteor documentation on subscription readiness.
Meteor’s publish/subscribe technology is an implementation of the publish-subscribe design pattern which is in wide use across the industry. For example, any technology stack employing Redis for data caching or ZeroMQ for distributed messaging is using the publish-subscribe model. So, it’s extremely likely that Google, Amazon, Facebook and all the rest are using publish-subscribe at some point in their tech stack.
Facebook has open sourced one of its primary data retrieval technologies, which is called GraphQL (https://graphql.org/). As noted above, it is simple to use GraphQL in Meteor applications in conjunction with or to entirely replace publish-subscribe.
This course uses Uniforms in conjunction with Simple Schema to accomplish this; see the documentation for those tools for more details. Essentially, Uniforms uses the schema created with Simple Schema to generate and format the form displayed on the page.
See the Bert documentation, especially the API & Defaults and Customization sections.
The Semantic UI developers did not want you to confuse the Loader
component with Spinners.
These are not mutually exclusive options: you can provide the Reset Password link while still tracking the number of unsuccessful login attempts. You could add an integer field to the users
collection to store the number of consecutive failed logins and update that in handleSubmit
in the Signin
page.
To verify that the string is a valid email address, use SimpleSchema.RegEx.Email
or SimpleSchema.RegEx.EmailWithTLD
. Checking if the email address actually exists is more difficult and your teaching assistant does not have an answer to that beyond sending an email to that address.
Yes, and you have already done something like this.
This is absolutely possible; you would just have to modify the relevant .jsx files. Whether you should do this is a philosophical question. On one hand, combining all of the possible actions on a collection into a single page reduces the amount of navigation necessary, and seeing what is already in the collection may be useful when adding new items (ex. ensuring that you are not creating a duplicate of something already in the database). On the other hand, doing this will put a lot of data on one page, resulting in a potentially confusing and cluttered user interface.
It is certainly possible to combine the conditional logic so the Stuff
subscription returns all Stuffs
for admins and only the items that the user owns for non-admins. The template application separates the publications because it displays the Stuffs
differently on different pages: an admin may want to see all Stuffs
or just the Stuffs
that he or she is directly associated with.
See the documentation and assigned reading for Uniforms; in particular, note that you are not restricted to only text fields and dropdowns. You may also use /app/client/style.css (or any other .css file that you import) to style the elements in the form.
Try moving the const owner
line from submit
to render
and setting the value
of the HiddenField
to { owner }
.
The methods are used as follows:
<AutoForm ref={(ref) => { this.formRef = ref; }} schema={StuffSchema} onSubmit={this.submit}>
Stuffs.insert({ name, quantity, condition, owner }, this.insertCallback);
In both cases, the method is not actually called at that point: this.submit
is set as the handler for the onSubmit
event and this.insertCallback
is passed as an argument to Stuffs.insert
. As you will recall from the functional programming module earlier in the semester, functions are data in JavaScript and so you can store them in variables, pass them as arguments to other functions, etc. The methods are actually called at some other time within the code for AutoForm
and Collection#insert
respectively.
You are restricted only by your imagination.
The code referenced is:
(error) => (error ?
Bert.alert({ type: 'danger', message: `Update failed: ${error.message}` }) :
Bert.alert({ type: 'success', message: 'Update succeeded' }))
This is an arrow function, so error
is the parameter for the function defined here. The function is not being called, only defined. For a complete look at how this function is used:
submit(data) {
const { name, quantity, condition, _id } = data;
Stuffs.update(_id, { $set: { name, quantity, condition } }, (error) => (error ?
Bert.alert({ type: 'danger', message: `Update failed: ${error.message}` }) :
Bert.alert({ type: 'success', message: 'Update succeeded' })));
}
So the arrow function (with error
as its parameter) is passed as an argument to Stuffs.update
.
If you want to restrict the range of inputs, it is best to design the form to only allow those specific inputs rather than waiting until the user submits the form to perform validation. If the input is a string, use a dropdown; if the input is a number, use a number field, etc.
The onSubmit
attribute for the AutoForm
component indicates the code to execute when the form is submitted. In both /app/imports/ui/pages/AddStuff.jsx and /app/imports/ui/pages/EditStuff.jsx, onSubmit
is set to this.submit
, referring to a submit
method defined within the class. This submit
function takes care of the database operations.
Yes, though this would likely require you to use regular expressions (and not just the constants referenced in the Simple Schema documentation).
No, though the type of vulnerability you are referring to would be attacked through a different route.
Although the pages look the same, they serve different roles. Add Stuff creates an entirely new Stuff
whereas Edit Stuff modifies an existing Stuff
. One uses insert
while the other uses update
. If you made a single page for both tasks, you would have to perform some conditional logic to set up the form for the correct operation and that would be more complex than just having two separate pages/
Separating the add and edit processes also matches the CRUD operations.
With that being written, it is perfectly valid to argue that while the pages may have different purposes the forms within those pages are nearly identical. It would then be reasonable to extract the form fields into a new component that you could then use in both AddStuff.jsx and EditStuff.jsx.
You could do this with another field in the document and a corresponding input
in the form.
The model
contains the data to display in the form.
One example of this is in /app/imports/ui/components/Navbar.jsx:
{Roles.userIsInRole(Meteor.userId(), 'admin') ? (
<Menu.Item as={NavLink} activeClassName="active" exact to="/admin" key='admin'>Admin</Menu.Item>
) : ''}
This uses the ternary operator to display the Admin menu item if the user is an admin and ''
otherwise. The condition here is Roles.userIsInRole(Meteor.userId(), 'admin')
; Roles.userIsInRole
takes the ID of the user to examine and the role to search for as its arguments. For more details, see the documentation for Roles.userIsInRole
.
In the template you are using, the roles
property is set to ['admin']
if the user is an admin user; if that is not the case, the only other option is that the user is a standard user.
You can add the “admin” role to an existing user through a MongoDB command, ex. db.users.update({username: "john@foo.com"}, { $set: { roles: ["admin"] }});
.
However, note that this must be done on the server-side.
The template only provides these two roles; these roles are ultimately just strings stored in an array field in the users
collection though, so all you need to do in order to create new roles is decide on a name for the role and add conditional logic to perform certain actions for that role. For more information, see the documentation for meteor-roles
.
After a while your machine would probably run out of memory, but other than that there are no limits on the number of documents in a collection.
Creating data does not inherently protect it from modification by others; if you want to ensure that users can only modify their own data, you would have to add in the logic for that yourself. For example, you can publish to the client-side only documents owned by the logged-in user.
Absolutely; we have an Ethics in Software Engineering module later in the semester and you might also consider ICS 390 for further discussion of ethical issues in Computer Science.
Mostly the fact that not even admins can make new admins in the template application. If you add that functionality in, you can use AdminProtectedRoute
from /app/imports/ui/layouts/App.jsx to prevent standard users from accessing pages that only admins should have access to.
Yes, though it is more accurate to write that meteor/alanning:roles
provides this; documentation is available here.
/app/imports/ui/layouts/App.jsx uses AdminProtectedRoute
for the admin page. AdminProtectdRoute
redirects the user to the signin page if the current user is not an admin (the isLogged && isAdmin
). Consequently, the current user must be an admin to access the page even if the user has typed in the URL of the admin page manually.
Yes, the ListStuffAdmin page displays everything in the Stuffs
collection.
Yes.
Although neither ProtectedRoute
or AdminProtectedRoute
extend React.Component
, they are functions that return a Route
, which is a component we can use in the render
method.
No, not to my knowledge. The source code for the website should only be available on GitHub, so without access to the repository there should not be any means of viewing or modifying the source code.
Yes.
The answer to this depends on how much the IT staff likes you.
This would depend on the application.
There are almost always alternative libraries for anything you might use; meteor/accounts-base
and meteor/alanning:roles
are the libraries used in the offical Meteor documentation though.
See the answers to this Stack Overflow question.
One way to support multi-factor authentication is through CAS. For example, the RadGrad application uses CAS authentication, which can be multi-factor.
For reCAPTCHA, see the Meteor-reCAPTCHA project (with the caveat that I have not tested this code myself).
Meteor is one of the top 10 most used web application frameworks. Also see the Meteor showcase, though the most important example for you is clearly RadGrad.
See the answers to this Stack Overflow question; this should not be confused with hooking.
See the Semantic UI React documentation.
There is no direct relationship between React and Meteor; Meteor supports React but is not inherently tied to React in order to work. Recall that one of the early steps in the Meteor React Tutorial (specifically https://www.meteor.com/tutorials/react/components) was to install React, so Meteor is not dependent on React. In turn, you used React before learning about Meteor, so React does not require Meteor either.
Not difficult. Meteor is entirely decoupled from the UI framework.
Yes, of course. Interestingly, real-time communication is one area in which Meteor excels. For example, it is almost trivial to create a real-time chat app in Meteor—for example, Make a live chat app in under 2 hours with Meteor. Meteor’s publish-subscribe technology makes it possible to create real-time apps with significantly less code and less opportunity for errors than traditional web application frameworks, which require HTML hacks such as AJAX to do real-time updates.
That said, as the amount of data to be manipulated grows, the developer must become increasingly thoughtful about how to manage it, and that is just as true for a Meteor-based app as it is for a non-Meteor-based app. Note that the publish-subscribe technology is not the only way to manage communication of data between client and server in Meteor. Another approach is Meteor Methods, which is the Meteor implementation of remote procedure calls.
It is the only one named Meteor.
More seriously, here are some interesting features of Meteor:
It has very well designed, easy-to-use, high performance deployment services in Galaxy.
It provides a layer of abstraction over a duck soup of underlying JS technologies. For example, you don’t need to deal with the horror that is webpack: Meteor takes care of it for you.
It provides built-in accounts management. This is a huge win and time-saver for getting a useful web app up and running with a minimum of coding.
It has extremely tight and well designed integration with MongoDB, which also reduces the time required to get an app up and running.
It is Javascript all the way down. Many other web application frameworks require users to learn Javascript plus another language. For example, Ruby on Rails requires Ruby + Javascript. Laravel requires PHP + Javascript. Spring requires Java + Javascript. It’s easier to have all the code in a single language.
The above features combine to make Meteor one of the easiest web framework for newbies to learn and use to develop interesting applications in a short time. Philip has experimented with a variety of web frameworks for ICS 314 over the years, including Stripes, Wicket, and Play. The applications that students build in a few short weeks with Meteor are generally more sophisticated and featureful than those developed with these prior technologies. What this implies is that while there is a heavy learning curve associated with Meteor, there is an even heavier learning curve associated with more traditional technologies!
Meteor is one of the top 10 most used web application frameworks.
This section contains questions I haven’t yet answered or am still working on.