Nullstack is a web framework that makes coding fun again.
Write the backend and frontend of a feature in a single isomorphic component with zero boilerplate or glue code.
class WaifuCounter extends Nullstack {
// runs in the server
static async getWaifus({ database }) {
const sql = "SELECT COUNT(*) FROM WAIFUS";
return database.query(sql);
}
// runs in the client
async countWaifus() {
this.waifus = this.getWaifus()
}
// runs wherever is best
render() {
return (
<button onclick={this.countWaifus}>
Count: {this.waifus}
</button>
);
}
}
On the first render you'll get SEO ready HTML optimized for the first paint of your route in a single request using local functions with zero JavaScript dependencies in the client bundle
After the content is served and the network is idle Nullstack JavaScript is loaded, the state of the application is restored through hydration and it becomes a single page application
Subsequent server functions will fetch JSON from an automatically generated microservice API, deserialize the response, update the aplication state, and rerender the page out of the box
class WaifuCreator extends Nullstack {
// extracted into a microservice
static async insertWaifu({ database, name }) {
const sql = "INSERT INTO waifus (name) VALUES (?)";
return database.query(sql, name);
}
// invokes the microservice
async create() {
this.insertWaifu({ name: "Nulla-Chan" })
}
// render in the DOM
render() {
return (
<button onclick={this.create}>
Create Waifu
</button>
);
}
}
You write features for products. The compiler extracts your front end code and replaces all static async functions with calls to microservices
class Vanilla extends Nullstack {
// variables are just variables
count = 0;
// mutations reflect in the dom
increment() {
this.count++
}
// 'this' is bound by default
render() {
return (
<button onclick={this.increment}>
{this.count}
</button>
)
}
}
Your components are just POJOs. Take advantage of the existing ecosystem while you write JavaScript or TypeScript as it is supposed to be, and see the result reflected in the DOM.
class Modern extends Nullstack {
// params are injected on every function
renderWaifu({ params }) {
return (
<p> Hi i'm {params.name} </p>
)
}
// routes can have dynamic segments
render() {
return (
<div class="not-class-name">
<Waifu route="/waifus/:name" />
<a href="/waifus/Nulla">
NullaChan
</a>
</div>
)
}
}
JSX tags follow the HTML standard, routes are simple attributes you can assign to any tag, and links are just anchor tags. You will find out that Nullstack is just a modern version of your current stack.
class Batteries extends Nullstack {
name = 'Nulla-Chan';
happy = false;
action() {
console.log('event already prevented')
}
render() {
return (
<form onsubmit={this.action}>
{/* two way bindings */}
<input bind={this.name} />
{/* object events */}
<button onclick={{ happy: true }}>
Hug {this.name}
</button>
</form>
)
}
}
Nullstack has a lot of out of the box shortcuts that were extracted out of repeating patterns in real projects instead of architecture books. Give it a try and you will get it!