DEV Community

ringeringeraja
ringeringeraja

Posted on

We finally have a fullstack framework for MongoDB

The two most popular alternatives for working with MongoDB in JavaScript (Prisma and Mongoose) are both flawed.

Mongoose adds a lot of polyfills and hooks to the MongoDB driver, stealing all of MongoDB's intended simplicity, requires external plugins to autopopulate references and does it badly (using hooks and multiple transactions instead of doing it in a single transaction leveraging the Aggregation Framework). It also has limited TypeScript support.

Prisma, on the other hand, emulates relational behavior when used with Mongo and misses denormalization, which is a very powerful feature intrinsic to Document-oriented databases.

I thought the JavaScript community deserved better, so I put together some good people and started working on a web framework written on top of MongoDB.

First, we have a compiler written in PureScript that parses .aeria files and emits JavaScript and TypeScript code.
The compiler runs in watch mode when the development server is active, so there's no need of running something like "aeria migrate" every time. Types are automatically updated on each compilation without need for IDE hacks also.

// the framework packs some useful builtins
collection File extends aeria.file {}
collection Pet {
properties {
name str
pictures []{
properties {
file File @accept(["image/*"])
description str
}
}
}
functions {
get @expose
getAll @expose
insert @expose
remove @expose
upload @expose
}
}
view raw main.aeria hosted with ❤ by GitHub

Role-Based Access Control

The framework let's you bring up RESTful endpoints automatically. Once exposed with the @expose attribute alone, any authenticated user can reach the endpoint. To control which set of users can have access to a specific endpoint, we can extend the builtin aeria.user collection with the roles property and then pass the desired roles as parameter to the @expose attribute.

collection User extends aeria.user {
properties {
// the actual roles used throughout the application
roles enum @values([
"root",
"manager",
"supervisor"
])
}
}
collection Shipment {
properties {
// ...
}
functions {
get @expose(["root", "manager", "supervisor"])
getAll @expose(["root", "manager", "supervisor"])
insert @expose(["root", "manager"])
remove @expose(["root"])
// dispatch is a custom function
dispatch? @expose(["root", "manager"])
}
}
view raw main.aeria hosted with ❤ by GitHub

Access Control remains strongely typed across the application. This could be used to be used to verify the access to critical functions in development/compile time.

declare const fn: (payload: unknown, context: RouteContext<'root' | 'manager'>) => void
router.POST('/privileged', (context) => {
if( context.token.authenticated ) {
// const roles: ('root' | 'manager' | 'supervisor')[]
const roles = context.token.roles
// TS error! `typeof context` is too broad for RouteContext<'root' | 'manager'>
// that means the 'supervisor' role could also execute `fn()` (dangerous)
fn(null, context)
}
}, {
roles: true,
})
view raw routes.ts hosted with ❤ by GitHub

Error handling

Since the begining, we know we didn't want try/catch error handling in our framework, so our first option was FP-style Eithers. That worked fine, but the user would have to adapt to idiomatic code like isLeft() and unwrapEither(), what didn't sound right. Later we finally came across our actual error handling approach, which is returning a Result.Either<E, R> object that the user can destructure and narrow as needed.

// const fn: () => Result.Either<CustomError.OhNo, 'hello, world!'>
const fn = () => {
if( businessLogic() ) {
return Result.error(CustomError.OhNo)
}
return Result.result('hello, world!')
}
const { error, result: _result } = fn()
if( error ) {
console.log("it's over")
return
}
view raw index.ts hosted with ❤ by GitHub

Extensible dashboard

All the backend metadata (information about collections and routes) is made available in the runtime as parseable JSON-schemas, so making a adaptative dashboard for Aeria was just around the corner. Aeria UI is a frontend counterpart for Aeria that allows bringing a CRUD-based application up in minutes. It is currently extensible with Vue only, but we plan on making it available to other UI frameworks as well.


That's it for now!
Also I am an ESL person, so forgive me if the text was sloppy at times.

Star us on GitHub!

We dream of getting traction someday and turning Aeria into a self-sustainable Open Source project.
If you liked the project and want it to thrive, please consider starring us on GitHub:

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up