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.
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.
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.
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.
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.
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.
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.
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.