Atscript Mongo for Moost.
npm install @atscript/moost-mongoSimple generator‑free CRUD for MongoDB collections defined with
atscript and served by
Moost.
- ✅ Zero boilerplate – one decorator turns your .as model into a fully‑featured controller.
- 🔌 Pluggable – override protected hooks to adjust validation, projections, or write logic.
- ⚙️ URLQL powered filtering / paging / projections on GET /query & GET /pages.
- 🧱 Type‑safe – everything is inferred from your annotated interface.
---
``bash`
pnpm add @atscript/moost-mongo # this package
pnpm add @atscript/mongo mongodb # runtime peer deps
pnpm add moost @moostjs/event-http # your HTTP adapter
---
`ts`
// src/collections/user.collection.as
@mongo.collection 'users'
export interface User {
@mongo.index.unique
email: string
name: string
age: number
}
`ts
import { AsMongoController, CollectionController } from '@atscript/moost-mongo'
import type { User } from '../collections/user.collection.as'
/ Provide AsMongo connection for the controller /
@Provide(AsMongo, () => new AsMongo(process.env.MONGO_URI!))
@CollectionController(User) // optional prefix param available
export class UsersController extends AsMongoController
`
`ts
import { Moost } from 'moost'
import { MoostHttp } from '@moostjs/event-http'
import { UsersController } from './controllers/users.controller'
const app = new Moost()
void app.adapter(new MoostHttp()).listen(3000)
void app.registerControllers(UsersController).init()
`
Hit the endpoints:
``
GET /users/query ?$filter=age>18&$select=name,email
GET /users/pages ?$page=2&$size=20&$sort=age:-1
GET /users/one/{id}
POST /users (JSON body) – insert 1‒n documents
PUT /users (JSON body with _id) – replace
PATCH/DELETE analogously
---
Base class you extend. T is the atscript constructor exported from the
.as file.
| Hook / Method | When to override | Typical use‑case |
| --------------------------- | ------------------------------------ | --------------------------------------- |
| protected init() | Once at controller creation | Create indexes, seed data |transformProjection()
| | Before running find() | Force whitelist / blacklist projections |validate*Controls()
| | Per endpoint | Custom URLQL control validation |onRemove(id, opts)
| | Before deleteOne | Soft‑delete or veto |onWrite(action,data,opts)
| | Before any insert / update / replace | Auto‑populate fields, audit, veto |
All CRUD endpoints are already wired – just subclass and go.
Decorator that glues your subclass to Moost:
1. Registers the collection constructor under DI token __atscript_mongo_collection_def.@Controller(prefix)
2. Marks the class as a (defaults to the collection name).@Inherit
3. Ensures route metadata from the parent is inherited ().
`ts`
@CollectionController(User, 'users')
export class UsersController extends AsMongoController
When you need raw collection access outside the generated controller,
use @InjectCollection:
`ts
@Injectable()
export class AuditService {
constructor(
@InjectCollection(User)
private users: AsCollection
) {}
async purgeSoftDeleted() {
await this.users.collection.deleteMany({ deleted: true })
}
}
`
AsMongo is resolved automatically from DI, so make sure it is provided
globally (see Quick start).
---
| Route | Description | Query controls |
| ------------------------ | ----------------------------------------------------------- | ---------------------------------------------------------- |
| GET / | List documents / count mode | $filter, $select, $sort, $limit, $skip, $count |GET /
| | Paged list with meta | same + $page, $size |GET /
| | Single document by _id or any @mongo.index.unique field | $select only |POST /
| | Insert one or many | – |PUT /
| | Replace by _id | – |PATCH /
| | Update by _id | – |DELETE /
| | Remove by _id | – |
---
`tscreatedAt
@CollectionController(User)
export class UsersController extends AsMongoController
/* Add on every insert. /
protected async onWrite(action, data) {
if (action === 'insert' && data) {
;(data as any).createdAt = new Date()
}
return data
}
/* Soft delete instead of hard delete. /
protected async onRemove(id, opts) {
await this.asCollection.collection.updateOne(
{ _id: this.asCollection.prepareId(id) },
{ $set: { deleted: true, deletedAt: new Date() } }
)
// prevent actual deleteOne
return undefined
}
}
``
---
MIT © 2025 Artem Maltsev