Learning Node.js and React while building a product

We spent the last month building a new product, Ramp Receipts, using technology that was outside our comfort zone as experienced JavaScript devs. The task was not as easy as we had expected given the size of the application, and we learned a lot in the process.

In the end, considering our background with ASP.NET MVC, we came to the conclusion that Node.js is really good and easy to start using. Developing is fun and Visual Studio (Code) support is great. Npm gives you whatever you need. Configuring a Digital Ocean server and deploying the app was also a surprisingly straightforward experience, mostly thanks to the fantastic guides that are available.

The only real issues we had were with React. It was (and still is, to some degree) frustrating and very counterintuitive for us. But this is to be expected when learning any new framework when you’ve proficient in others.

So what were we building? Ramp Receipts is an API that helps you build a receipts dashboard in your application, including downloading receipts as customizable PDFs, assuming that you are using Stripe.

It has a simple account management application where you can connect with multiple Stripe accounts, configure some account details, and get the keys for the API. The management app uses React on client side.

Server side is done with Node.js, for both API and account management. Login is implemented via third-party login providers. We currently support Google, GitHub and Stripe logins.

We are long time Microsoft .NET developers with 15+ years of experience in desktop and web applications. We are also JavaScript developers from the pre-jQuery era with jQuery, Knockout, Durandal and Aurelia and some limited knowledge of Angular.

Our primary development environment is Windows with limited experience with Linux machines.

Ramp Receipts was an ideal project for trying out some new tools we wanted to explore. It’s small, so we had sufficient time to learn and build with them. Node.js was our chosen server-side platform, but we wanted to get to know React, too. The plan was to go as bare-bones as possible and use a minimum number of libraries, especially on the client side.

The Stack

Node.js

Node.js is a platform of choice for many developers, especially in startups. It’s very easy to get into and it can work everywhere. While we are not entirely sold on the full-stack aspect - having JavaScript work on both the client and the server - it certainly has some benefits. It was easy to pick up since we already had considerable knowledge of JavaScript.

One of the things that make Node.js so powerful is its ecosystem. Whatever you can imagine can be found on npm. We were already familiar with npm for client-side development, and it’s more or less the same for server-side. Many libraries and tools work the same for server-side or client-side, whether you are using moment.js for date manipulation or gulp to automate some tasks. They work seamlessly on both sides of the equation.

Node.js has picked up some steam in the past few years, with shorter release cycles and lots of new features being implemented. We have really enjoyed being able to use ECMAScript 2015+ features that we were already using in client-side code, like let and const, arrow functions, Promises, async/await (from Node.js 7.6.0), etc.

ECMAScript modules are also in the works, but CommonJS modules implementation is not bad, either. All of our code ended up modularized according to single responsibility principle. It was no different than using classes and namespaces in .NET.

Frameworks and libraries

Express is a rough equivalent of ASP.NET MVC or ASP.NET WebAPI, depending on what you are trying to build. It’s the oldest and most widely used web framework for Node.js. Express documentation is really good and there’s a plethora of online tutorials and guides.

We were using express-generator to create an initial application skeleton. Cross-cutting concerns like authorization and error logging were done using custom middleware. Routes were grouped by URL and defined in separate modules.

Building a small RESTful (actually RESTful-like) API was a breeze with Express. The actual implementation of the main features, like loading data from Stripe and mapping the responses to receipts and PDFs, was offloaded to separate modules.

PDFkit was used to generate PDF receipts out of the raw data we get from Stripe. It’s a powerful library that simplifies working with a monster of a format that is PDF. All in all, it’s not too hard to grasp, although we do prefer a more declarative approach when building UI (and one could argue that PDF is UI) against procedural.

Most of the applications we are working on require some kind of a data storage, and Ramp Receipts was no exception. We usually prefer to think of the database as an implementation detail as we adhere to Domain Driven Design.

Knex fits into the DDD mindset like a glove. It’s a simple wrapper, or SQL query builder as it’s been advertised, around your database. It was easy to setup and easy to work with, thanks to its fluent interface and support for Promises.

No matter how big or small your API is (RR API is made of just 5 methods), you’ll need to create some kind of a documentation. Swagger can help you with that through the Swagger UI, but it also provides more benefits, like metadata description of your API.

Overall, it was not too hard to implement. It took us a day or two to figure out the configuration, write down JSDoc comments, and make it work with our build configuration.

In addition to API, we also have React app to connect Stripe accounts and get access tokens for API. App needs to have authentication and for that we chose to use Passport.

Passport is an authentication middleware for Node.js. It can be connected with Express to authenticate users with local username and password, or with 300+ other authentication providers, like Google, Facebook, Twitter, etc. For our app, we set up Google, GitHub and Stripe authentication.

Passport documentation is solid and a lot of in-depth tutorials can be found online. We didn’t have major problems with it.

React

Huh, this was a ride… and still is. The philosophy of React is totally different to anything we have worked on so far. We were in a constant fight with it. One of the problems was finding the correct source of truth. There are so many different articles and tutorials online, some of which are no longer relevant. React documentation is OK-ish, but we didn’t want to invest too much time going over it and opted for a quick start instead.

At the moment we’re somewhere on the first or second valley of Ben Nadel’s chart (made for Angular, it can be used for most frameworks and libraries). There are some things that we do like, such as the ability to use the latest ECMAScript, pushing the developer to componentizing the code, and the theory behind the state as a single source of truth.

But so many other things didn’t sit well with us. This will most certainly change as we learn more and do more apps with it, so read the following as a reflection of where we are right now.

The learning curve. As you might have noticed from the rant above, we couldn’t just jump into it. We really enjoy frameworks and libraries that use conventions over configuration, and pushing the devs into the pit of success. We didn’t feel this is the case with React.

React is a library, not a framework, so there are additional things to do in order to make it work for you. We tried not to go overboard and include too many libraries so we could have a better understanding of the core. One thing that we haven’t done, but should have, is started from create-react-app. It creates the base skeleton for the application and configures everything you need for build and development. We recommend you jump start your apps with it. We did so in the sample app for the API that you can find on GitHub.

JSX. We are pretty indifferent to it. It does have the benefit of being declarative JavaScript that you write in the same file as the rest of the code for the component. It also has good support in editors and IDEs. But it just feels off to have something that looks like HTML within a JS file. In addition, there are some slight differences from HTML that can drive you mad from time to time. className attribute instead of class is one example. Being strict about the closing tags is another. Eventually, you get the mix of JavaScript and HTML-like XML that could be confusing at first.

Another thing that really bothers us are event handlers. They just look like a regular JavaScript handlers where you need to take care of event object, targets, etc. But on top of that, there’s a standard JS problem with this and to avoid it one needs to do a .bind in constructor or in the place of calling or use a modern ECMAScript class property syntax.

While state management seems like a good idea, we run into problems sharing the state between components, especially when many different things need to be set. The lack of understanding of basic concepts should probably be blamed and, yes, we are aware of Redux, MobX and other libs that can be used, but we didn’t want to do that for a simple app with only a few pages.

Tooling

Now let’s talk about the fun part. Tooling in the JavaScript world is actually great once you are familiar with npm and build tools. We ended up using Webpack for building and bundling the client side React app. Gulp was also used for some limited tasks like preparing documentation and linting.

Once you set everything up it just works. You can configure auto rebuild on file change, even hot-reloading in the browser. The modern ECMAScript 2017+ code that you write gets transpiled in the background to JavaScript that today’s browsers can consume.

.NET was always famous for its build tools, IDE and dev environment, but the build-refresh cycle that JavaScript based code has is unmatched. .NET Core improved this a lot, but people still work with the older .NET Framework, especially for desktop applications.

And while we are on IDEs, we could comfortably use our favorite IDE - Visual Studio 2017 with a ReSharper extension. There is a great support for Node.js and client side JavaScript. We have opted to do most of our work in a more lightweight editor/IDE - Visual Studio Code.

Let’s make a statement here. Visual Studio Code is excellent! Microsoft managed to create a great open-source editor in a short amount of time. Feature-wise it’s there with Sublime, Atom and Brackets, but it’s release cycle is shorter. It doesn’t have the capability of a full IDE like Webstorm, but it’s getting there. Support for extensions is great. It’s fast, lightweight and a pleasure to work with.

Deployment

Digital Ocean was chosen as a host for Ramp Receipts and it seems to be an excellent choice. As with most of our work on Windows, we were a little worried if we would be able to configure everything from the terminal using SSH. And guess what, we managed to do it fairly easy thanks to Digital Ocean’s great guides.

In the end, we configured the database, firewall, pm2 server, nginx, HTTPS with Let’s Encrypt certificate, continuous deployment, automated backups, automated certificate renewal, etc. All of that from the command line. We are so proud of ourselves.

Conclusion

Job done, so how do we feel about the experience of working with Node.js and React, a new stack for us?

Aside from some issues we had with React, it was an overall positive experience. Even the problems with React can be put down to a lack of knowledge, and will surely be gone or decrease once we work on more projects.

Although we still have a small preference toward ASP.NET Core rather than Node.js on server side, and a (much bigger) preference toward Aurelia rather than React on the client side, we are looking forward to our next project with Node.js + React stack.



6 comments

Kalin Chernev
6/16/2017 9:28:38 PM
``` Swagger can help you with that through the Swagger UI, but it also provides more benefits, like metadata description of your API. Overall, it was not too hard to implement. It took us a day or two to figure out the configuration, write down JSDoc comments, and make it work with our build configuration. ``` Can you please give some details on this? Really thank for sharing this whole article!

Branko Pejić
6/16/2017 11:16:50 PM
Hi @kalinchernev, It would probably take an article on its own to cover this subject in all details. Right now I can just point you out to two npm libraries: https://www.npmjs.com/package/swagger-jsdoc and https://www.npmjs.com/package/swagger-ui-dist First library will allow you to use jsdoc to define swagger and second library you can use to import dependencies into your React component.

Kalin Chernev
6/17/2017 7:48:46 AM
Thank you @brankopejic333 and @miroslavpopovic it's already useful response! I also use the first one, where as the for the documentation I recently found https://github.com/Rebilly/ReDoc The tool is made by https://apis.guru/ and is pretty good. They also made https://github.com/Rebilly/generator-openapi-repo which provides both the swagger-ui and redoc with a single spec generated by, for example, the swagger-jsdoc :) Thank you again for sharing your knowledge!

Branko Pejić
6/17/2017 11:05:48 AM
Thank you too @kalinchernev for sharing and for your interest.

Miroslav Popovic
6/16/2017 9:42:26 PM
Hey @kalinchernev, thanks for the comment! I'll ask my colleague @brankopejic333 to answer this one since he added Swagger implementation.

Matthias Hagemann
6/21/2017 8:27:30 PM
Was VueJS considered during the evaluation process as a replacement for React? I know it is often considered the quasi-standard at the moment, but I could never get over the lack of “aesthetics” of React.

Miroslav Popovic
6/21/2017 10:14:03 PM
@mhagemann Nope, platform choice was a decision from the upper management. I would like to try Vue at some point.