In a world where bespoke backend software is easily generated, Estate is here to help you build backends at a higher abstraction, and faster than it would take you to prompt GPT to write it for you using traditional backend tooling.
Estate is an open-source TypeScript object database with an embedded AppServer that may be all you need to write powerful real-time SaaS backends and efficient Line-of-Business app backends.
Estate is lightning fast: most backend requests complete in under 200ฮผs (Not including network round-trip).
This demo is an example full-stack SaaS app that allows users to track the exercises they do in a shared real-time list. The backend runs in Google Cloud (us-central) and the front-end client (React+Vite) is running in this StackBlitz sandbox.
๐ค Curiously Concise Code, where's the boilerplate?
๐ Strongly typed data persistence, end-to-end TypeScript shared type system.
โก๏ธ Easy-mode real-time server-to-client messaging, update all clients in real-time!
๐โ๐จ Data is remotely observable, adding reactive views!
- Login to the default Estate cluster (aka "Sandbox")
$ estate login
This opens a new browser tab. Click "Continue as Guest" to login anonymously.
- Initialize the backend service code directory:
$ cd service
$ estate init .
Enter exercise-tracker
when it asks for a name.
This makes the service code directory a Service that can be deployed and connected to by clients/front-ends.
- Deploy the backend service:
$ estate deploy .
Each time you make changes to your service backend code, you'll need to rerun this command.
- Inspect:
- The backend database source code is located in the
service/index.ts
file. - It's only 115 lines of code, yet is an entire backend for this application.
- How:
- An Estate Service is a collection of one or more TypeScript POJO classes that extend Worker, Data, or Message. Workers run stateful code on the backend and can be called via autogenerated client-side proxies. Data elements define your app's data model, contain business logic, and are persisted to the database. Messages provide real-time backend-to-client events.
- With these three types, you can build very powerful backend business logic.
๐ค Curiously concise backend code
- Generate code to connect to the service in the front-end/client NPM project:
$ cd ..
$ estate connect . exercise-tracker
Select "pnpm" (press enter) when asked to update node_modules. It will only ask you to do this the first time you make a service connection. This command must be rerun each time the backend service is deployed.
- Start Exercise Tracker demo
$ npm run dev
- In the Exercise Tracker window (๐), click "Add user" and add a user (any name will do).
- Inspect:
- The username you entered now exists in the Username drop-down box.
- A new User Data object was created and saved to the database using the username you supplied.
- How:
- The React client code passed the username to the backend by calling a server-side, Worker method at
src/pages/create-user.tsx
line 16:
10) const exerciseTracker = estate.getWorker(ExerciseTrackerWorker, "default");
...
16) await exerciseTracker.addUser(username);
- Line 16 makes a transparent backend call to the Estate backend cluster running the service you deployed.
- In the backend code, the username argument is passed to the worker method at
service/index.ts
:
49) addUser(username: string): User {
...
53) const user = new User(username);
54) saveData(user);
...
}
- At line 53, a new instance of the User Data object is instanciated, passing the username to the constructor.
- The following line saves the new user to the database.
- If the user didn't have a unique username,
saveData
would throw an exception. - This is because the User class passes the username from its contructor to
super
, because all Data-derived classes must specify a primary key:
18) export class User extends Data {
19) constructor(public username: string) {
20) super(username);
21) }
22) }
- Data can have any number of properties of any TypeScript type. Each property will be stored in the database when the object is passed to
saveData
.
๐ Estate makes strongly typed data persistence easy
- Click "Open in new Tab" in the top-right (๐).
- Right-click the new browser tab and click "Duplicate"
- Detach the tab and arrange the windows, so you can see all of them at the same time.
- In the Create Exercise form, fill in the fields and click "Create Exercise Log."
- Inspect:
- Do you see how the new exercise showed up in the other window instantly?
- The Message class lets you send strongly typed messages/events from Worker backend code to any number of clients, simultanuosly. This is called a Server-Sent-Event, or SSE.
- How:
- A Message is a POJO that extends
Message
. Any properties you define on the POJO are sent to clients who subscribe to it. TheExercsieAdded
message is:
12) export class ExerciseAdded extends Message {
13) constructor(public exercise: Exercise) {
14) super();
15) }
16) }
- Workers send Messages to clients who have subscribed. The client subscribes to recieve all ExerciseAdded messages sent by the worker at
src/pages/exercises-list.tsx
:
71) estate.subscribeMessageAsync(exerciseTracker, ExerciseAdded, onExerciseAdded)
- The
ExerciseTrackerWorker
sends an ExerciseAdded message atservice/index.ts
:
76) addExercise(exercise: Exercise) {
...
85) sendMessage(this, new ExerciseAdded(exercise));
86) }
โก๏ธ Estate makes it easy to add real-time capabilities
- From the list of exercises, edit an exercise or delete an exercise.
- Inspect:
- See how the change shows up in the other windows?
- How:
- The React client code subscribes to external updates to
Exercise
object changes (including deletes) atsrc/pages/exercises-list.tsx
:
80) estate.subscribeUpdatesAsync(exercises_)
81) .then(() => {
82) ...
83) estate.addUpdateListener(exercises_, onExerciseUpdate);
84) })
- Line 80 tells the Estate back the client wants to recieve updates to the exercise Data instances in the
exercises_
array. - Line 83 tells the Estate browser runtime to call the
onExerciseUpdate
client function when new updates are recieved. This lets the client code update the view/UI when other clients make changes.
๐โ๐จ Estate's Data is remotely observable, adding more real-time goodness
Please check out our GitHub https://github.com/EstateJS/estate (Stars would be very helpful, thanks!)
We're active on Discord! https://discord.gg/ahHffbBkNQ
The estate repo contains the client-side packages: https://github.com/EstateJS/estate
The system repo contains the backend platform: https://github.com/EstateJS/system
If you're interested in how the transport protocol works, check out the Google Flatbuffers schemas: https://github.com/EstateJS/system/tree/main/schema
The framework (Tools SDK, Client runtime) is MIT licensed and the platform is Apache-2 licensed.