Create a character voting app using React, Node.js, MongoDB and Socket.io
sahatyalkabov.comChrist on a bike...
I spend a reasonable amount of time developing and maintaining a reasonable sized js based front end to an application, which uses React heavily (40k lines). Looking at this, I can't help but think that isomorphic JS apps make life far more miserable than separate back and front end applications.
To get a decent server running on a linux distro - Nginx, Mariadb, PHP/Python/etc. An apt-get and your updates are handled nicely for you. Dependency management via Gulp is better than nothing, but jeez it still sucks so bad.
Building a nice, RESTful API on a more sensible back end infrastructure is far less complex, has a vastly smaller toolchain (just a small library for routing and db connection handling would do), plus you get the joys of things like Schemas and ACID from a proper RDBMS (because seriously, Mongo? We really still think that's a good idea?).
That's before we even get on to Step 4. ES6 Crash Course. Or, here's things to know about a language that you can't use on the client browser, because it's not actually a thing there, so you can't use all that you'll learn here when you write JS.
It's insane that people think you need to do all this just to get a live updating counter. What's wrong with a tidy back end with ZeroMQ for your pubsub style socket handling to allow real-time event based change publication, and a separate front end?
It feels like you have to do a hell of a lot of work to get JS + your favourite V8-based server to do anything, and none of it easily.
There is just a lot of friction. Want es6, get ready to put gulp in your work flow. Want to sanely include frontend dependencies? Time to add throw in bower. Want those js/css to be injected into the page? Throw in wiredep or bite the bullet and chuck in the wirepack.conf.js. Don't want that and want to use a skeleton? Install yeoman and run the isomorphic js generatior that gives you a 1k+ line project and you haven't even made your app yet.
I made a scaffold [0] that tries to give you a reasonable starting point, without tons of extra cruft. It was important to me that the scaffold handle all 3 basic environments: development, testing, and production.
It's basically a single webpack config (that's heavily commented, btw!) and a set of npm run-scripts. You don't need gulp to build an application! And I would not call bower a sane way to include frontend dependencies... Not by a long shot.
I was looking at this a while ago, and it's a really nice starting point! I too am aiming to remove Gulp from my workflow when possible.
It's like people want complexity in their app, then complain when they get it.
jspm can actually remove a lot of the friction you mentioned:
1. seamless ES6 transpilation at runtime (no build steps needed)
2. load libraries in any package format as ES6 modules (Globals, CommonJS, AMD, ES6) from any source (local repo, Github, NPM)
3. no need for injection (you can import css and html programmatically using extensions)
It does introduce some friction of its own due to it being a relatively new project, but not enough to offset all the benefits it brings, in my experience.
> It's insane that people think you need to do all this just to get a live updating counter.
Dismissing this guy's tutorial because he didn't clone Facebook is kind of short sided. Everything is about trade offs. You don't need to make your React app isomorphic if you don't mind a slower initial load and lack of SEO. You don't need to use Mongo, you can pass data from your RDBMS directly to your components as props when you render server side or have them call your REST API using an isomorphic request library. You don't need to use ES6 if you use something like webpack and babel.
Don't get me wrong, it's a massively comprehensive piece of work. I'm jealous of the author's chops - they know more about this than I do. But after all this time writing applications small and large, doing isomorphic JS dev feels like a massive step back.
I think it isn't needed for 99% of all web-apps.
But if you have heavy competition and need a snappier first load experience, it is an option you have.
Can you elaborate on you stack and toolchain? I'm curious about what's simple and works well with React.
Not the OP but I've really liked working with the Django REST Framework. Top-notch work and I suspect it would scale pretty well to a point.
Django is pretty nice. A little heavy for my liking (I prefer libraries to frameworks), but as frameworks go, it's a solid one.
For sure. The stack is designed to make hiring and maintaining simple, rather than to be completely elegant, so with that in mind:
Server is Nginx. Evaluating HHVM for migration is an ongoing concern, but there's some edge cases we run in to where it does wonky things. That said, within the next ~12 months, I expect we'll be on it.
MariaDB, with query results cached on Memcache. The data we deal with is massively relational, with some very large tables and result outputs, so there's no way around using a tidy RDBMS for us, and consistency is a big thing.
PHP, with a few tiny, high speed libraries for DB connection management and routing. As far as possible everything uses PHP functions that map directly on to underlying language equiv, and we make a lot of use of streaming and generators, which makes memory overhead nicely manageable. When HHVM becomes a thing for us, we'll then start pulling some of this over to Hack.
That's all set up as a bunch of RESTful APIs, which are reasonably HATEOAS in their outputs. Output format is either JSON or HTML, depending on request params.
Front end is a bunch of React bits. We're slowly converting into React, so initially we used HistoryJS to manage URL history, along with React to render stuff. Qwest for XHR handling, and a few little libraries and helper functions (of note: moment.js and numeral.js).
Everything then gets executed on the client. Current roadmap is to build something akin to React router for managing loading of assets and better history management, and to pull the entire front end into a single application. At the moment, it's better thought of as a bunch of distinct SPAs, operating over a common middleware.
Architecturally, the back end services are written MVA, with data going through our own DAL, with RBAC sitting in front of everything. Caching is handled in the DAL, so everything acts as a black box - models don't know where the data came from, adapters don't know where the model got it's data etc...
The front end is written loosely around a Flux style architecture. Data is held in a single area, as are all methods that interface with state. Those parts sit at the top, and pass data as props down to UI components, written to be dumb as to where there data comes from.
IO is mostly via single HTTP requests, but with the option to hook onto ZMQ for real time interaction.
Hope that helps! I'm currently writing some lower level thoughts on React on our blog (https://builtvisible.com/blog/) with some more advanced stuff coming shortly.
Apologies for the various TLAs. If you need anything explaining further, shout.
It's a shame the blog of yours doesn't have a RSS or Atom feed.
Great tutorial showing how many components work together in node/mongo/react.
It also absolutely blows my mind. This just feels wrong. 17 separate packages, some providing really basic stuff like serving favicons and parsing POST requests that must be a part of bigger framework. Each package is of different quality, testing strategy, and depends on a different developer who's completely free to abandon his project tomorrow or introduce some backwards-incompatible change. This trend as a whole looks unreliable, like a wobbly house of cards near a working fan.
> Each package is of different quality, testing strategy, and depends on a different developer who's completely free to abandon his project tomorrow or introduce some backwards-incompatible change
Welcome to OSS! Really though, there's things like forking, community involvement and SemVer.
The project configuration reflects the developer's decision not to use webserver like nginx, otherwise things like serve-favicon and express.static would be unnecessary.
I challenge your assumption that gulp + browserify is more straightforward than webpack. Your setup is not representative of a real application.
I setup a project scaffold [0] with a configuration that supports development, production, and testing environments. It has a heavily commented webpack config.
No tests? No asset cache busting by updating name references? Come on, those are basic requirements for a web project.
On top of that, webpack lets your dependencies be resolved as part of your code, instead of relying on certain files being in certain folders: e.g. your css and images.
Thanks for your project template. It looks really polished.
What is your experience in regard to running tests with Webpack?
* Can you test individual modules that use arbitrary loaders specified in your Webpack config?
* Can you run tests from the command line?
* Can you write your tests in ES6/7 (or anything else supported by Webpack's loaders)?
I was looking into combining jest with Webpack but every solution so far would only compile your module directly with Babel and therefore skipping the power of Webpack.
No huge issues with running tests that depend on webpack, but the developer UX is not outstanding.
If you want to be able to do everything, you'll have to configure more stuff. Honestly, I don't think it's worth the extra effort to try and configure things to work with every possible workflow. Just setup what you need, and then add stuff in as you find yourself wanting / needing it.
My advice is to avoid jest. It's too slow and clunky. If you want to stub modules, there's rewire-webpack. If you're using babel and ES6 modules you'll probably want to check out babel-plugin-rewire. I think there's a few other alternatives, but I haven't had the need for any of these so far.
On to your questions...
> Can you test individual modules that use arbitrary loaders specified in your Webpack config?
Yes. The solution I have is to maintain a single webpack config maker function that has a bunch of conditionals inside, and then I just use that function from all my configs. When you set options.TEST to true, it'll set the entry point to an empty object.
That means that my tests will be configured as close as possible to my production and development. The same loader config will be used for all the environments.
> Can you run tests from the command line?
Sure. In the web-app repo, it uses karma with phantomjs.
If you want to test your code using node... It's a bit more annoying, but also doable. I've previously set it up like this: create a test.js [0] file, set it as the entry-point in the webpack config, and use webpack context to include all the tests you wanna run, and finally point mocha to the output file. If you wanna use it in "live" mode, you can run webpack and mocha both with the watch flag.
If you just wanna run a single test... You can specify a single test as your entry-point, and then point mocha to it. Again, if you want "live" mode, you can set the watch flag on both mocha and webpack.
> Can you write your tests in ES6/7 (or anything else supported by Webpack's loaders)?
Yes. They use the same loaders as the rest of your codebase.
[0] https://www.dropbox.com/s/35ebqijrp9nqyj3/Screenshot%202015-...
Why is modern webdev this hard?
Even the todo-mvcs are all ~500 to 1000+ lines.
It's the future.
This one hurts, deeply.
Funny stuff, but it makes me want to just put my hands over my eyes and say "let a Dev ops guy handle it!"
CircleCI uses Om for its frontend.
I think because we are living in the prehistoric era of modern webdev. Programming in assembler is much easier!
What are you talking about? I've never seen a 2k lines todomvc.
Here's my unscientific LOC count results (that I did in 5 minutes).
I've removed node_modules and bower_components from the items in: https://github.com/tastejs/todomvc/examples
I've also deleted "polymer" and "emberjs_require" since they didn't have any files in them and messes up the `wc -l`
Results: http://pastebin.com/GqF0g3qF$ for x in *; do echo $x; wc -l `find $x/ -name '*js*' -type f | egrep -v 'test|lib|public|vendor|.bundle.js|.cache.js'`; echo; doneObviously one can improve the filtering to get more accurate results but you get the point. To make a TODO mvc you need at least 5+ different files and totalling a lot of lines. The fact that jQuery solution (which is very nice code, not the typical spaghetti) beats the majority of them in line count is pretty ironic.
You included all the spec / unit test files, and in some instances you included the framework.
I've updated with `test|` in the filter now, but the point stands.
Sure, if your only metric is LOC. (Even then the majority is below 500 lines -- code style is an important factor)
Will jQuery suffice if you're creating Asana rather than todoMVC?
"I have yet to find any React boilerplate project with an easy to understand webpack.config.js file."
I actually think that a simple Webpack configuration for a React project is pretty easy to understand (thanks to Babel). Here is one that I use in one of my projects [1]. Adding Less support is like one additional entry.
[1]: https://github.com/renke/perpetually/blob/master/webpack.con...
How long did it take you to write this?
Impressive and awesome. This is perfect to catch up on using build tools. I focus on core programming concepts in my own work, and at work most of that stuff is automated to just a few commands, so I appreciate the gulp.js part of your tutorial most. Thanks for the great work.
If you want to check out an alternative for building applications, go look up Webpack.
I strongly believe you can build web applications without having to pull in Gulp or Grunt. And to back up my claim I setup a project [0] that shows how to do it using webpack + npm run-scripts.
I'd argue it solves a large set of problems while maintaining a reasonable level of complexity.
I can't claim to be a Gulp expert, but it's always shocking to me how tools like Gulp and Grunt become so pervasive without strong tutorials.
I ignored Grunt for years, always implementing my own hackneyed systems in Python, until I eventually discovered this tutorial[1], which at least got me a good introductory "this is how you use this thing that everybody else already knows how to use, dummy" lesson.
Thanks for the resource!
Yeah they did kind of just sneak up about a year ago it seems. I remember getting into the field two years ago I didn't hear about them. I suspect the tipping point was the full migration to front-end frameworks instead of just plain JS/jQuery.
I think Grunt had been around for awhile. That video is December 2013ish, and I remember trying and failing more than a couple of times to integrate it into my workflow. According to whois, gruntjs.com was first registered in April 2012.
Now, to be fair to Grunt, my workflow is usually Python or Go on the backend, and I just wanted something to manage my front-end assets, so it was never a necessity for me, but knowing there's something that does what I want it to do, while being unable to make it do the thing that it does is quite frustrating.
Things like http://yeoman.io/ likely have something to do with it.
I've been curious about React for awhile now, but every time I step in to play around with it, I get really turned off by the syntax.
This is a react render function mentioned in the tutorial (gist with all code here[0]):
render() {
let delta = this.props.delta ? (
<strong className={this.props.delta > 0 ? 'text-success' : 'text-danger'}>
{this.props.delta}
</strong>
) : null;
return (
<div className='card'>
{delta}
{this.props.title}
</div>
);
}
This is approximately that same function written in ERB and Rails syntax. <div class='card'>
<%= content_tag_for(:strong, :class => @delta > 0 ? 'text-success' : 'text-danger' ) do %>
<%= @this_props_title %>
<% end %>
</div>
On just a pure typing basis, the ERB would take half as long to type as the react version. Even writing that same function in backbone templates (which I use frequently) would take less text, and be easier to read. Maybe I haven't given react and other js frameworks enough of a chance, but to a person that hasn't learned them, that render function above is a huge deterrent.[0] https://gist.github.com/alexggordon/820020aab934bf192b81
Your ERB function is actually wrong though....
I'm not a rails developer, so I'm only marginally certain of the correctness of this, and less certain if there's a more verbose way to write it. Likewise, I've only played around with React, so its possible that the React version can be made less verbose.<div class='card'> <% if this_props_delta? %> <%= content_tag_for(:strong, :class => @delta > 0 ? 'text-success' : 'text-danger' ) do %> <%= @this_props_delta %> <% end %> <% end %> <%= @this_props_title %> </div>One could just as easily argue:
Is the same amount of typing. Honestly though, I feel as though this whole argument is a bit of an apples-to-oranges type of argument.<div className='card'> {() => { this.props.delta ? ( <strong className={this.props.delta > 0 ? 'text-success' : 'text-danger'}> {this.props.delta} </strong> ) : null }() } {this.props.title} </div>Here's how I would inline it, and I encourage people to do it this way. Intermediate variable is not necessary. Ternary is not necessary since false evaluates to null in jsx.
render() { return ( <div className='card'> {this.props.delta > 0 && <strong className={this.props.delta > 0 ? 'text-success' : 'text-danger'}> {this.props.delta} </strong> } {this.props.title} </div> ); }
I'm not an expert on either of those, but I don't think that your ERB version does the same thing. If delta is not set, the react version renders something like
If it is set, it renders something like<div class="card"> title </div>
Your version appears to render<div class="card"> <strong class="text-success"> delta </strong> title </div><div class="card"> <strong class="text-success"> title </strong> </div>
Really great tutorial, I looked at your other tutorials and you have some really great content. Thanks for sharing.
Standing ovation to you. That is a lot of work and a precious resource. Kudos.
Swig seems superfluous in that stack given it already has React, one could just use React to prerender the pages.
He provides his rational towards the bottom of the post:
> But do we really need a separate template for this? Why not just render everything inside the App component? Yes, you could do it, as long as you are okay with invalid HTML markup and not being able to include inline script tags like Google Analytics directly in the App component. But having said that, invalid markup is probably not relevant to SEO anymore and there are workarounds to include inline script tags in React components. So it's up to you, but for the purposes of this tutorial we will be using a Swig template.
I'm not clear on the "invalid markup" portion. Aren't `data-*` attributes valid for all tags in HTML5? Also, if you really don't want them you can render with `React.renderToStaticMarkup`.
Personally, the only things I had to do to prerender full pages with React were:
- prepend '<!DOCTYPE html>' to the resulting HTML
- attribute "prefix" in head tag "<title>" wasn't outputted, so until it's fixed in the library, I just use a data attribute "data-replace-prefix" and removed the extra "data-replace-" from the resulting HTML
-------
Also, he could include inline scripts (like Google Analytics) this way:
return <script dangerouslySetInnerHTML={{__html: "THE_JS_CODE"}}></script>;
Looks pretty cool! I like the graphics. I think Meteor.js does this too, but with only one resume buzzword.
It's great that you're sharing a very practical tutorial, but did it occur to you that women (and generally reasonable people) might not be particularly motivated to learn how to build a woman judging app? Seems like your example project could be less alienating.
Really awesome job
Is it really a "full-stack" "app"? Sounds more like a set of scripts:
> "...using React, Node.js, MongoDB and Socket.IO..."
Not a single one of those things are 'scripts' in the colloquial sense of the word