Node.js I Choose You!
January 27th, 2023The Trading Card Game Maker Creation is underway! And to make our maker we have to decide what languages and frameworks we will be making the calls to our maker to make the cards to make the decks to make the games to play and build in the TCG Maker (I promised run-on making of the maker statements in my last post, so you were warned!). One of my major tasks this sprint has been provisioning and standing up the skeleton of our Backend API. In the discussions with our team around what languages to use for both Frontend and Backend, as well as what sort of databases and frameworks to employ, I got to thinking about all my experience working with various technologies for building RESTful API’s. Spoiler alert, we chose to use React for our frontend, and after some debate settled on Node.js and Express for building the backend API (hosting on Google Cloud and using Firebase for our Database). I wanted to take this blog post to wax poetical on this decision and other Backend API architectures I have worked with. Before I can dive into that, let me offer a brief summary of REST API’s and my experience building them.
First and foremost – I love a good REST API. I like the idea of API calls grounded in the “Nouns” of our craft rather than the verbs. So quick breakdown: an API (Application Programming Interface) is any of a multitude of tools for communicating between pieces of software. An API is how one program asks another to do something. In our project the User Interface will need an “API” to ask the database to store new card games, to generate the information needed to play the games and countless other little interactions between the information and functionality and the actual user interface. REST is an approach to building API’s around a language of HTTP requests that use (and this is of course a simplification and summary skipping many important caveats) a short list of Actions combined with a target Entity. The core set of actions are GET, POST, PUT, PATCH and DELETE. Combining these simple actions with a complex matrix of Entities allows us to do an incredible amount of stuff. The second part of a RESTful HTTP call is the address the verb targets, which is always something like “/decks” or “/users” and never* “/save_a_card” or “/generate_opponent”. If we want to save a card, we can POST to “/games/[name_of_our_game]/cards” and magically our actions mesh seemlessly into our data models. There is so much more that could be said about this topic, but let’s keep moving toward the great tech stack debates.
The final thing I’ll say by way of introduction is that I am more than a novice but by no means an advanced student of the API arts. I’ve taken OSU’s course on cloud based API’s where I built REST API’s with both Node/Javascript/Express systems and Flask/Python systems, and used both of these in other course work and pet projects. I work for a company that sells cars online and I have built backend API’s for them as well, all using Kotlin/Spring Boot though some in the CoRoutine style and others in the more classic Reactive approach. At the end of the day, I’ve put together programs that receive HTTP Requests in a variety of ways. I’ve also built these systems using, among others, MongoDB, Datastore, Firebase, MySQL, and PostgreSQL. Here’s my thoughts:
For enterprise level API’s, there is something to be said for the legendary through-put of Reactive Spring based systems (usually written in Java, or its evolved form, Kotlin), but at the end of the day, when writing an API you want to be able to define functionality for endpoints. All the extra bells and whistles, not to mention the nested function chain headaches of Reactive frameworks obscures the simple, beautiful core of what we do with RESTful API’s. CoRoutines is to Reactor what Async/Await is to Promises in Javascript. It solves some of the problem, but it all comes short of the simple beauty of Flask. I love Flask. Let’s just show the simplicity telescoping of say, writing a GET all games endpoint in each of the systems I’ve listed.
- (most complicated) Kotlin/Spring Boot with WebFlux/Reactive would look like two files at a minimum. One containing a function defining routers like (if you want to imagine this in Java just add 4 extra words or symbols to each line in your mind’s eye):
“`@Bean
fun tcgRoutes(: RouterFunction<ServerREsponse = router {
GET(“/games”, tcgHandler::getGames)
}
And then somewhere else a function that has to deserialize ServerRequest objects and serialize into ServerResponse objects and so on. Sure, it runs fast, but this is json bodies containing amounts of data orders of magnitude smaller than the simplest real-time computer game. We don’t need that efficiency. We need code that is easy to read.
2. (medium complicated) In Express/Node we get a simpler sort of architecture. We have to define a bunch of “requires” objects and whatnot at the top, tell our app to start listening on a port with a function call but at least we can right queries of our database inline right bellow the route. It looks something like:
app.get(“/games”, (req, res) => {
let dbQuery = Firestore.collection(“games”);
let allGames = {};
let queryResult = await dbQuery.get()
queryResult.forEach(element => allGames[element.id] = element.data())
res.json(allGames);
};
Cleaner, leaner, and very digestable. This is the system we chose, and we chose it partially because it has the simplicity and readability one would like, and also because as a team our composite experience in Flask vs Express was strongly leaning toward Express.
3. (simple beauty) But just for those of you who haven’t given up on this blog post yet, what would the same code be in Flask? Let me show you:
@app.route(‘/games’, methods=[‘GET’])
def allGamesRoute():
allGames = Firestore.get(‘games’)
return json.dumps(allGames), 200
And that’s it. Why do I like this so much, in many ways it is not that different from express. But, it does provide a simple way to group your processing for a given endpoint with the methods array. That could be methods=[‘GET’,’POST’] and then our allGamesRoute function could have a simple conditional (programming 101 level stuff) for the different Verbs. No need for async/await gymnastics and best of all, you just return the body of your response as a classic return statement. To put the cherry on top you can return a tuple with your response code as well, making it super simple to have natural feeling code with simple linear conditionals determining whether to throw a 400 or return some info and a 200. In the end, I suspect Flask is the least “effecient” in terms of cpu usage, with minimal use of threading potential and whatnot. But for writing a REST API, that beautiful system so simple in its building blocks and complex in its possibilities, it allows for easy to read and easy to write implementations. I think Express offers a fine runner up, but outside the benefit of consistent tech-stacks (since we use react for the frontend it’s nice to have a JS/Express based BE as well) and familiarity to the team at large, I think Flask would have been a great option as well.
PS. A brief statement about databases. I love them, and formal Schemas with well though out architectures are lovely. That’s SQL. But when it comes to Agile development and ever changing projects NoSQL DB’s are the way to go. Firebase, Datastore, MongoDB…. doesn’t really matter which you use, they are all optimized at this point. But the freedom of these systems where additional columns can be incorporated without having to re-provision the whole database really lend themselves to dynamic development process. Fini.
Read the post...