https://github.com/dorsale/dorsale
Dorsale is a Bun-based backend framework
https://github.com/dorsale/dorsale
backend bun decorators dependency-injection framework typescript
Last synced: 1 day ago
JSON representation
Dorsale is a Bun-based backend framework
- Host: GitHub
- URL: https://github.com/dorsale/dorsale
- Owner: dorsale
- License: mit
- Created: 2023-05-18T09:46:29.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2026-02-08T16:50:34.000Z (about 2 months ago)
- Last Synced: 2026-02-08T22:34:29.530Z (about 2 months ago)
- Topics: backend, bun, decorators, dependency-injection, framework, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/dorsale
- Size: 404 KB
- Stars: 2
- Watchers: 1
- Forks: 1
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# dorsale
Dorsale is a Bun-based backend TypeScript framework. It is built on top of the Bun server and [wint](https://github.com/aquapi/wint) for routing.
Dorsale uses decorators to define elements of your application.
It is a work in progress, and is not ready for
production use.
If you benchmark it, please let me know the results!
## Installation
Quick and easy, just:
```shell
bun add dorsale
```
You also need to enable TypeScript's decorators feature by adding the following options to your `tsconfig.json`:
```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
```
## Getting started
### Your first dorsale app
The setup is intentionally minimalistic. Bootstrapping a Dorsale application is as simple as:
```ts
import { dorsale } from 'dorsale';
dorsale({ port: 8080 });
```
This will start a server on port 8080. You can then add controllers, services, and repositories to your application.
### Full application example
A full example could look like this:
```
src
├── index.ts
├── user.ts
├── userFinder.ts
├── userManager.ts
├── userController.ts
```
```ts
// index.ts
import { dorsale } from 'dorsale';
dorsale({ port: 8080 });
```
```ts
// user.ts
export interface User {
id: string;
email: string;
password: string;
}
```
```ts
// userFinder.ts
import { User } from "./user";
export interface UserFinder {
findAllUsers(): Promise;
findUserById(id: string): Promise;
findUserByEmail(email: string): Promise;
}
```
```ts
// userManager.ts
import { UserEditor } from "./userEditor";
import { UserFinder } from "./userFinder";
import { User } from "./user";
import { Component } from "dorsale";
@Component
export class UserManager implements UserFinder {
users: User[] // = [... some users];
findAllUsers(): Promise {
return Promise.resolve(this.users);
}
findUserByEmail(email: string): Promise {
const user = this.users.find((u) => u.email === email);
return Promise.resolve(user);
}
findUserById(id: string): Promise {
const user = this.users.find((u) => u.id === id);
return Promise.resolve(user);
}
}
```
```ts
// userController.ts
import { Controller, Get } from "dorsale";
import type { UserFinder } from "./userFinder";
@Controller()
export class UserController {
constructor(private readonly userFinder: UserFinder) {
}
@Get("/")
getAll() {
return this.userFinder.findAllUsers();
}
@Get("/:id")
getById(id: string) {
return this.userFinder.findUserById(id);
}
}
```
You just have to run the file containing the call to the `dorsale` function, and you're good to go!
The other components will be automatically discovered and injected. This reduces the amount of boilerplate code you have
to write.
⚠️ Important: you **must** import interfaces as `type` in the controller file, this is a limitation of Bun. (
see https://github.com/oven-sh/bun/issues/8618)
By default, Dorsale will look for components in the current directory, but you can change this by passing a `rootDir`
option
to the `dorsale` call.
```ts
dorsale({ port: 8080, rootDir: "myFolder/relative/to/the/current/file" });
```
## Controllers
### Controller classes
Controllers are classes that define the routes exposed by your application. They are decorated with `@Controller()`.
```ts
import { Controller } from "dorsale";
@Controller()
export class UserController {
// ... your routes
}
```
You can also specify a prefix for all the routes defined in a controller by passing it as an argument to the decorator.
```ts
@Controller("/users")
export class UserController {
// ... your routes
}
```
### Routes
Routes are defined by decorating methods with `@Get`, `@Post`, `@Put`, `@Patch`, or `@Delete`.
```ts
import { Controller, Get } from "dorsale";
@Controller()
export class UserController {
@Get("/hello")
getHello() {
return "Hello world!";
}
}
```
In the example above, a GET route will be exposed at `/hello`, and will return the string `"Hello world!"`.
### Route parameters
You can define route parameters by adding a colon (`:`) before the parameter name in the route path.
```ts
import { Controller, Get } from "dorsale";
@Controller()
export class UserController {
@Get("/users/:id")
getUserById(id: string) {
// ...
}
}
```
### Query parameters
Query parameters are defined by adding a `@Query` decorator to the parameter.
```ts
import { Controller, Get, Query } from "dorsale";
@Controller()
export class UserController {
@Get("/users") // e.g. GET /users?page=1&limit=10
getUsers(@Query page: number, @Query limit: number) {
// ...
}
}
```
If some query parameters are not provided in the request, they will be `undefined`.
### Body
You can access the body of the request by adding a `@Body` decorator to a parameter.
```ts
import { Controller, Post, Body } from "dorsale";
@Controller()
export class UserController {
@Post("/users")
createUser(@Body user: User) {
// ...
}
}
```
## Testing
Coming soon!
## Contributing
Contributions are welcome! Feel free to open an issue or a PR if you have any suggestions or bug reports.
Please follow the GitHub flow when contributing (see [here](https://guides.github.com/introduction/flow/) for more
information).
Thanks for your interest in Dorsale!