Introducing: Hyperstack - The Pragmatic Node.js Web Framework for Builders

Introducing: Hyperstack - The Pragmatic Node.js Web Framework for Builders

Hyperstack is a new open source Node.js web framework for builders. It’s not reinventing the wheel, it’s just wrapping a bunch of great wheels and good parts for you to use — in one nice Rails-like package.

Good software is simple software that solves complex problems. Building simple software usually requires following some flavor of: principles, design patterns and strong opinions that help us navigate the perils of the paradox of choice.

Where you don’t have these principles and best practices to guide simplicity, I’ve seen engineers kick the tires for fun with the year’s trending microservice API framework, and before you know it they’re legs deep in building a microservice architecture for a painfully simple web app. “That’s how we’re supposed to do it, that’s how to build scalable software” they thought.

For many, it’s become easy to build complex software, and hard to start something simple. To build full stack APIs in a simple way, and so building fun projects is hard to keep fun.

Solving simple problems with simple software

A single app, with a single relational database has been perfect for decades for building great software and still is great for starting out the right foot in creating simple software for projects that are fun to build.

Ruby on Rails enabled exactly that. You build a *majestic monolith* that solves simple and complex problems, until you can’t anymore.

Only then, when you’re smarter about everything, when you have all of the use cases sorted out with piles of data and experience — then you start splitting and building microservices.

This approach worked for: Github, Airbnb, Twitter. All of them started as a monolith. Some of them still are a majestic monolith: Gitlab. Most of them still use Ruby on Rails.

Typescript vs. Ruby in a Rails World

Rails is arguably the best web framework that was ever created, and Ruby is the programming language that you have to use to build Rails apps. It most likely influenced everything you’re touching today in software on the Web whether you know it or not.

While you still can use Rails, you might prefer using Typescript and Node.js and not Ruby.

There are plenty of reasons to prefer Typescript and Node.js for backend development instead of Ruby:

  • Prior experience for individuals or team, or prior codebase you’re trying to modernize
  • Code sharing between server and client is a real thing
  • Infrastructure sharing with frontend tooling (there’s so many!) is a big deal: dev tooling, CI, code quality, leading to whole-team effectiveness over time
  • Lastly, Ruby is a great language, but not as popular as typescript anymore

What does it take to create a Rails in typescript world?

  • Models - a strong ActiveRecord abstraction, that magically knows to connect and operate
  • Database handling: migration, seeding - and support for multiple databases to allow for better developer experience
  • Controllers, routing, and authentication and security best practices - to allow for easy API building as well as avoiding common security gotchas
  • Mailers - while you can do emails via 3rd party SaaS, it’s much more productive to build, test, and experiment with emails locally. you need support for that.
  • Background jobs - because using a raw queue service such as SQS isn’t as ergonomic, or test friendly, as using a dedicated background job framework.
  • Tasks - you need to fix records, clean up data, or send ad-hoc emails to users. Build a custom task that is fully testable with access to your live app and invoke it just like you use make
  • Interactive REPL - easily access live objects and information about an app remotely or locally
  • Code generation and scaffolding - build faster by generating models, controllers and more
  • Testing as first-class citizen - every piece of the framework lends itself to clean, easy testing
  • Configuration and operability - built in concerns such as:
    • Logging, log redaction, request-id generation
    • Controlling your app
    • Monitoring in development and production
  • Conventions and opinions - folder structure, naming, linting, formatting, are all taken off your back so you can focus on building products
  • KISS - when facing a choice, always pick a simpler path. For example, ActiveRecord over Data Mapper, or any other entity mapping magic. ActiveRecord lends to simpler code.
  • Views - an automatic way to cut the boilerplate of formatting, serializing responses and error states from the app toward clients

Here are some of the trials and tribulations if you were to try to assemble such a thing from scratch, specifically for Javascript and Node.js. Surely some of it sounds familiar:

  • Building this with express you need to remember about async failures. Yes, Koa and others are great, but Express works for the vast majority of API use cases, it’s also simple and widely adopted, and is what people usually expect to find in a general purpose API project.
  • Picking and wiring up loggers takes can be a mess handling request scoping, process scoping and more, you have to thread the needle through global and local state in an evented world.
  • Building a background jobs infrastructure from scratch will set you up for failure. There’s way too much to know here to build this from scratch or even pick the right queue system and build the right abstraction and infrastructure on top of it.
  • For testing, you need to spawn an Express app, remember to clean up databases, set up a schema, load data, and start your tests. All these are infrastructure you have to build and trial and error until it works well for you.

And that’s just the tip of the iceberg. Luckily, I’ve built Hyperstack so you don’t have to build this yourself.

A Hyperstack app in 5 minutes

Hyperstack is a Typescript web framework, that has Rails as its north star. It follows Rails closely where it benefits developer experience, but deviates radically where it benefits the pragmatism of building a Node.js app in Typescript.

Let’s create our first app:

$ pnpm create hyperstack
$ cd my-hyper-app
$ pnpm install
$ bin/hyperstack start

And we’re done!

Hyperstack have created the database, migrated, set up your working development environment, transpiled Javascript and configured a fancy API service for you with great defaults such as secure headers, static serving, email previews and background workers that are in-process but with configuration become a fully scalable queue based background job processors.

Kill the process CTRL-C and let’s add our blog article:

$ bin/hyperstack g scaffold article title:string body:text

Loaded templates: gen/dist/templates
       added: src/test/requests/articles/sanity.spec.ts
       added: src/app/controllers/articles.ts
       added: src/config/db/migrate/1337_article.js
       added: src/test/models/article.spec.ts
       added: src/app/models/article.ts
      inject: src/app/controllers/index.ts
      inject: src/app/controllers/index.ts
      inject: src/app/models/index.ts
✔      shell: running eslint
       shell: yarn eslint --fix src/app/models/*.ts src/app/controllers/*.ts src/test/models/*.spec.ts src/test/requests/articles/*.spec.ts src/config/db/migrate/*.js

You got a controller, model, migration, and automatically written tests for free.

Run your app again and let’s start making requests:

$ bin/hyperstack start

I'm using xh but you can use httpie which is similar, or anything you like:

List:

$ xh -j -b localhost:5150/articles
{
    "articles": []
}

It’s empty, so let’s create one article:

$ xh -j -b localhost:5150/articles title="hello hyperstack" body="EVH rocks"
{
    "article": {
        "title": "hello hyperstack",
        "body": "EVH rocks"
    }
}

Take a look at the list again:

$ xh -j -b localhost:5150/articles
{
    "articles": [
        {
            "title": "hello hyperstack",
            "body": "EVH rocks"
        }
    ]
}

And we’re done!

Feel free to explore further in the docs. You can play with hyperstack routes and hyperstack portal for the interactive REPL, and much much more.

Remember: complexity always finds its way

We struggle with the complexities and avoid the simplicities. Norman Vincent Peale

You can always choose to learn, and build complete frameworks from scratch. You can also invest time in building scalable architectures from scratch.

But some times, you want to just build a product, or build something fun without subscribing to weeks of set up, infrastructure, and configuration as the preliminary step.

For building products quickly, just use Hyperstack

How to get involved

You can start at the website, or jump into the Github repo to read through the source code. We’re also accepting PRs if you have improvements to make or issues to report.