Skip to content

Well-defined πŸ’ decorators for Node.

License

Notifications You must be signed in to change notification settings

jeremyben/reflet

Repository files navigation

Reflet πŸ’«

Reflet is a suite of modules made of well-typed decorators.
Reflet is simple and flexible. It will help you organize your app, without the pitfall of verbosity.
But most importantly, Reflet is french for reflection (pronounce ruh flay πŸ”).

Organize your Express application.
Documentation.

@Use(isAuthenticated)
@Router('/things')
class ThingRouter {

  @Get('/:id')
  async get(@Params('id') id: string, @Res res: Response) {
    const thing = await db.collection('things').find({ id })
    res.status(200).send(thing)
  }

  @Send({ status: 201 })
  @UseGuards((req) => canCreateThing(req.user))
  @Post()
  async create(@Body('item') item: string) {
    const newThing = await db.collection('things').insertOne({ item })
    return newThing

Add-on: Reflet/express-middlewares

Declare your Mongoose models in a more concise way.
Documentation.

@Model()
@SchemaOptions({ autoIndex: false })
class User extends Model.I {
  static findByEmail(email) {
    return this.findOne({ email });
  }

  @Field({ type: String, required: true })
  email: string

  @Field(String)
  name: string
}

const user = new User({ email: '[email protected]', name: 'Jeremy' })
await user.save()

Typed HTTP primitives to use within any framework.
Documentation.

@Router('/things')
class ThingRouter {
  @UseStatus(Status.Created)
  @Post()
  async create(@Headers(RequestHeader.Authorization) auth: string) {
    if (!auth) {
      throw HttpError.Unauthorized('Stop right there')
    }
  }
}

Philosophy πŸ“£

Why another decorator framework ? Simply (and biasedly) put, a better developer experience.

Simple and intuitive

Reflet modules aim to stay close to the technology's underlying abstractions they decorate,
so you don't have to learn a whole new terminology to be productive. πŸŒοΈβ€

e.g. Reflet/express has decorators like Use for app.use method, Router for express.Router, Send for res.send.
If you're familiar with Express, you're gonna love its Reflet decorators.

As such, Reflet modules don't try to be agnostic or compatible with multiple technologies at once (e.g. decorators for both Express, Koa, Fastify), because that would mean :

  • more detached, less familiar abstractions. πŸ€”
  • less accurate static typing (conditional types to aknowledge differences). πŸ€₯
  • hence, more error prone code (from my part and yours). 😠

Instead, Reflet hopes to provide a well-defined and well-typed module for each libraries.

With accurate types

Reflet takes full advantage of TypeScript by narrowing its type signatures, to prevent as much mistakes as possible. 🎯

e.g. Headers decorator narrows its input to a union of request headers instead of just string,
UseStatus decorator narrows its input to a union of status codes instead of just number.

With documentation at hand

Every exposed API is fully documented, with examples and even links to the original library documentation, so you're never alone in the dark of your editor theme. β˜€οΈ

Built-in flexibility

Most decorator frameworks are great for the simple stuff, but inevitably get in your way later down the road. 🎠

Reflet does its best to avoid this by staying simple and to the point, composable, and by eating its own dog food: common decorators are built with lower-level tools, that are also provided to you. 🐎

e.g. exposed Express parameter decorators are created by the same createParamDecorator method that is provided to you.

With progressive features

This design allows for easy extension and plugins. 🧩
In this regard, Reflet generally provides a core module with all the basic decorators to do everything, and add-ons for extra convenient features.

e.g. Reflet/express-middlewares provides middleware decorators to handle authorization (UseGuards), response mapping (UseInterceptor), to complete the features of Reflet/express.

Self-control

NPM ecosystem got a bad rep by encouraging intricate third-party dependencies for the sake of DRY principle.
Time has come for a smarter dependency diet. 🍳

no dependencies

Reflet modules fully own their codebase. No direct third-party dependencies, only peer ones.

e.g. Reflet/express only asks for the necessary peer dependencies: express, @types/express, @reflet/http.

Integration-tested

Reflet uses extensive integration testing, to make sure its decorators work with the underlying library.

e.g. Reflet/express is tested with HTTP requests, Reflet/mongoose is tested with mongoose queries and an in-memory mongo.

Readable source code

Careful abstractions && clear-cut modules && commented/documented code && small number of files/folders === reduced indirection and complexity. 🧡

Have a look and compare with similar frameworks. 🧢