Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/williamragstad/knight
MVC web server framework for Deno π¦ built on Oak
https://github.com/williamragstad/knight
Last synced: 10 days ago
JSON representation
MVC web server framework for Deno π¦ built on Oak
- Host: GitHub
- URL: https://github.com/williamragstad/knight
- Owner: WilliamRagstad
- License: mit
- Created: 2022-02-08T18:29:38.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-03-14T21:58:53.000Z (over 2 years ago)
- Last Synced: 2024-05-01T16:05:48.462Z (6 months ago)
- Language: TypeScript
- Homepage: https://deno.land/x/knight
- Size: 523 KB
- Stars: 8
- Watchers: 2
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Knight
A MVC REST framework for Deno π¦ built on Oak and inspired by Spring.
Build scalable controller-based CRUD APIs with ease.
## About
This framework allows you to create a rich REST API with just a few lines of
code. The web server is powered by [Oak](https://oakserver.github.io/oak/) and
utilizes Denoβs native HTTP server and builds upon the existing middleware and
routing stack, to allow for automatic endpoint generation.Focus on what you want to do, and the framework will take care of the rest.
## Documentation
> ### View the full [technical documentation](https://doc.deno.land/https://deno.land/x/knight/mod.ts).
## Getting Started
You can use the knight framework in three different ways, start by importing the
latest version of the `Knight` module. Now either let Knight create a new server
instance, or use an existing Oak application. In the example below we will use
the former.
> ### Project Structure
>
> We suggest that the project structure is as follows:
>
> ```
> /
> βββ controller/
> β βββ UserController.ts
> βββ model/
> β βββ User.ts
> βββ service/
> β βββ LoggingService.ts
> βββ index.ts
> ```
>
> All files named `*Controller.ts` are automatically found and used by the
> framework.
`/index.ts`
```ts
import { Knight } from "https://deno.land/x/knight/mod.ts";const app = await Knight.build();
console.log("Server ready on http://localhost:8000");
await app.listen({ port: 8000 });
```In this introduction, the `Knight.build()` function will find and use the
`UserController` class from an example below.
Now let's create the `UserController` class. By default the framework provides a
`IController` class, which is a base class for all controllers and provides a
set of overloadable methods that are common to all controllers. Such methods
include `get`, `getById`, `post`, `delete` and `put`. Though you can easily
define your own custom endpoints using the `@Endpoint` decorator.
`/controller/UserController.ts`
```ts
import {
bodyMappingJSON,
Context,
Controller,
created,
Endpoint,
IController,
ok,
Params,
} from "https://deno.land/x/knight/mod.ts";import User from "../model/User.ts";
@Controller("/user")
export default class UserController extends IController {
async post({ request, response }: Context): Promise {
const user = await bodyMappingJSON(request, User);
created(response, `User ${user.firstName} was successfully created`);
}@Endpoint("GET", "/:id/email")
getByEmail({ id }: Params, { response }: Context): void {
const email = id + "@example.com";
ok(response, `User with email ${email} was successfully found`);
}
}
```
The controller class is responsible for handling all requests to the endpoint.
Knight comes with a set of built-in mapping functions that can be used to handle
request of different DTO classes. One of these functions is `bodyMappingJSON`.
This function takes a request and a class and returns the parsed body as an
instance of the class. In the example above, the request body is parsed as a
JSON object and returned as an instance of the `User` class.Creating a model class is as easy as defining a regular class. Mark nullable, or
optional properties with the `?` symbol and the `@Optional` decorator to signify
that the property is optional to body mapping functions.
`/model/User.ts`
```ts
import { Optional } from "https://deno.land/x/knight/mod.ts";export default class User {
firstName: string;
lastName: string;
email: string;
@Optional()
country: string;
@Optional()
city?: string;
@Optional()
phone?: number;constructor(
firstName: string,
lastName: string,
email: string,
country: string,
city?: string,
phone?: number,
) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.country = country;
this.city = city;
this.phone = phone;
}
}
```
> View the source code for this example code on
> [GitHub](https://github.com/WilliamRagstad/Knight/example).## Example
Run the example project by running the following command in the project root:
```bash
> deno run -A --unstable example/index.ts
```## Endpoints
As described in the previous section, the framework provides a set of
overloadable methods that are commonly used through the `IController` class.
These methods are:- `get(ctx: Context): void | Promise`
- `getById(id: string, ctx: Context): void | Promise`
- `post(ctx: Context): void | Promise`
- `delete(id: string, ctx: Context): void | Promise`
- `put(id: string, ctx: Context): void | Promise`All of these methods are overloaded to accept a `Context` object, which is a
type provided by Knight to allow for easy access to the request and response
objects. As well as the `Params` object for custom `@Endpoint` methods, which
contains the parameters passed to the endpoint.All of these have full support for asynchronous alternatives, which means that
you can use `async`/`await` and return a `Promise` from the controller method
and the framework will adapt the response accordingly.## Services
If we in the `UserController` class above, would like to use a service. We can
create a service class and inject it into the controller as seen below.Let's create a logging service.
`service/LoggingService.ts`
```ts
import {
ConsoleSink,
FileSink,
Logger,
LoggingLevel,
Service,
} from "https://deno.land/x/knight/mod.ts";export default Service(
class LoggingService {
public logger: Logger;
constructor() {
this.logger = new Logger()
.attach(new ConsoleSink())
.attach(
new FileSink("./example/logs/log.txt").fromRange(
LoggingLevel.Success,
),
);
}
},
);
```And now we can use the service in our controller.
`controller/UserController.ts`
```ts
import LoggingService from "../service/LoggingService.ts";export default class UserController extends IController {
private log: Logger = LoggingService.instance().logger;
// ...
}
```
## Consider contributing! π
All contributions are welcome! Create an issue or pull request on
[GitHub](https://github.com/WilliamRagstad/Knight) to help us improve the
framework.
## Contributors β¨
Thanks goes to these wonderful people
([emoji key](https://allcontributors.org/docs/en/emoji-key)):
William RΓ₯gstad
π» π β οΈ π β π¨ π‘ π€ π¦ π π
This project follows the
[all-contributors](https://github.com/all-contributors/all-contributors)
specification. Contributions of any kind welcome!