- v3.0.1Latest
- v3.0.0
- v2.0.1
- v2.0.0
- v1.15.2
- v1.15.1
- v1.15.0
- v1.14.2
- v1.14.1
- v1.14.0
- v1.13.19
- v1.13.18
- v1.13.17
- v1.13.16
- v1.13.15
- v1.13.14
- v1.13.13
- v1.13.12
- v1.13.11
- v1.13.10
- v1.13.9
- v1.13.8
- v1.13.7
- v1.13.6
- v1.13.5
- v1.13.4
- v1.13.3
- v1.13.2
- v1.13.1
- v1.13.0
- v1.12.3
- v1.12.2
- v1.12.1
- v1.12.0
- v1.11.4
- v1.11.3
- v1.11.2
- v1.11.1
- v1.11.0
- v1.10.9
- v1.10.8
- v1.10.7
- v1.10.6
- v1.10.5
- v1.10.4
- v1.10.3
- v1.10.2
- v1.10.1
- v1.10.0
- v1.9.3
- v1.9.2
- v1.9.1
- v1.9.0
- v1.8.10
- v1.8.9
- v1.8.8
- v1.8.7
- v1.8.6
- v1.8.5
- v1.8.4
- v1.8.3
- v1.8.2
- v1.8.1
- v1.8.0
- v1.7.4
- v1.7.3
- v1.7.2
- v1.7.1
- v1.7.0
- v1.6.3
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.11.0
- v0.10.4
- v0.10.3
- v0.10.2
- v0.10.1
- v0.10.0
- v0.9.1
- v0.9.0
- v0.8.3
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.8
- v0.7.7
- v0.7.6
- v0.7.5
- v0.7.4
- v0.7.3
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.5
- v0.6.4
- v0.6.3
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.5
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.2
- v0.3.1
- v0.3.0
- v0.2.0
- v0.1.5
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- v0.0.23
- v0.0.22
- v0.0.21
- v0.0.20
- v0.0.18
- v0.0.17
- v0.0.16
- v0.0.15
- v0.0.14
- v0.0.13
- v0.0.12
- v0.0.11
- v0.0.10
- v0.0.9
- v0.0.8
- v0.0.7
- v0.0.6
- v0.0.5
- v0.0.4
- v0.0.3
- v0.0.2
- v0.0.1
oak_nest
Rely on oak@v10.0.0 to simulate some annotation functions of nestjs which is a frame for nodejs.
I will update the oak version if need.
run
deno run --allow-net --allow-env --allow-write --unstable --config tsconfig.json example/main.ts
or you can use denon:
denon dev
Demo
Controller
Decorators
Module
、Controller
、Injectable
、UseGuards
、Get
、Post
、Body
、Headers
、Query
、Res
、Req
now are available:
import {
Body,
Context,
Controller,
createParamDecorator,
createParamDecoratorWithLowLevel,
ForbiddenException,
Get,
Headers,
Post,
Query,
Res,
UseGuards,
} from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import type { CanActivate } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import mockjs from "https://deno.land/x/deno_mock@v2.0.0/mod.ts";
import { delay } from "https://deno.land/std/async/mod.ts";
class AuthGuard implements CanActivate {
async canActivate(context: Context): Promise<boolean> {
console.log("--AuthGuard---");
await delay(100);
// throw new ForbiddenException('this is AuthGuard error');
return true;
}
}
class AuthGuard2 implements CanActivate {
async canActivate(context: Context): Promise<boolean> {
console.log("--AuthGuard2---");
return true;
}
}
class AuthGuard3 implements CanActivate {
async canActivate(context: Context): Promise<boolean> {
throw new ForbiddenException("this is AuthGuard3 error");
return false;
}
}
@UseGuards(AuthGuard)
@Controller("/user")
export class UserController {
@UseGuards(AuthGuard2, AuthGuard3)
@Get("/info/:id")
test(
context: Context,
@add() name: string,
@Query() params: any,
@Query("age") age: string,
) {
console.log(params, age);
context.response.body = "role info " + name + " - " +
JSON.stringify(params);
}
@Get("/info")
getInfo(@Res() res: Response, @Query() params: any) {
console.log(params);
res.body = "role get info " + JSON.stringify(params);
}
@Get("list")
list(context: Context) {
console.log("---list----");
this.testInnerCall();
context.response.body = "list";
}
testInnerCall() {
console.log("---test---");
}
}
You can customize the decorator by createParamDecorator
or
createParamDecoratorWithLowLevel
:
const Add = createParamDecorator(async (ctx: any) => {
const result = ctx.request.body(); // content type automatically detected
if (result.type === "json") {
const value = await result.value; // an object of parsed JSON
// console.log('value', value);
return value.userId;
}
});
function Add2(params: any) {
return createParamDecoratorWithLowLevel(async (ctx: any) => {
return params;
});
}
Then use like this:
@Post("/info")
info(
@Add() name: string,
@Add2("name") name2: string,
@Body() params: any,
@Headers() headers: any,
@Headers("host") host: any,
@Res() res: Response,
) {
console.log("ctx", name, name2, params, headers, host);
res.body = "role info " + name + name2;
}
You can also use class validator
like this:
class Dto {
@Max(2)
@Min(1)
pageNum!: number;
@Max(5)
@Min(1)
pageCount!: number;
}
@Post("/info")
info(
@Body() params: Dto
) {
console.log("ctx", params);
return "role info " + name;
}
The Body
decorator is using
deno_class_validator for
validator, which is forked from class-validator
which is using in nodejs, if
it fails, then will throw an 400
Error.
Controller use Service
You can use Injectable
to flag the service can be injectable, and it can be
used by your Controller or other Service.
import { Injectable } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
@Injectable()
export class RoleService {
info() {
return "info from RoleService";
}
}
@Injectable()
export class UserService {
constructor(
private readonly roleService: RoleService,
) {}
info() {
return mockjs.mock({
name: "@name",
"age|1-100": 50,
"val|0-2": 1,
role: this.roleService.info(),
user2: this.userService2.info(),
});
}
}
@Controller("user")
export class User2Controller {
constructor(
private readonly userService: UserService,
private readonly roleService: RoleService,
) {
}
@Get("/user2")
info(context: Context) {
context.response.body = this.userService.info() + this.roleService.info();
}
}
If you like to use the Service
alone in anywhere, you can with Factory
like
this:
import { Factory } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
Factory(UserService).info();
router add Controller
import { UserController } from "./user.controller.ts";
import { User2Controller } from "./user2.controller.ts";
import { RoleController } from "./role.controller.ts";
import { Router } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
const router = new Router();
await router.add(UserController);
router.setGlobalPrefix("api");
await router.add(RoleController, User2Controller);
It should be noted that
router.add
has been modified to asynchronous by me. Of course, I now recommend the following wayuse Module
.
use router in app
import {
Application,
isHttpError,
send,
Status,
} from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import router from "./router/index.ts";
const app = new Application();
// Timing
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
app.use(router.routes());
const port = Number(Deno.env.get("PORT") || 1000);
console.log(`app will start with: http://localhost:${port}`);
await app.listen({ port });
now you can visit
http://localhost:1000/api/user/info
,http://localhost:1000/api/user/list
.
use Module
First is the AppModule:
import { Module } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import { AppController } from "./app.controller.ts";
import { UserModule } from "./user/user.module.ts";
@Module({
imports: [
UserModule,
],
controllers: [AppController],
})
export class AppModule {}
Then this is UserModule
:
import { Module } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import { RoleController } from "./controllers/role.controller.ts";
import { UserController } from "./controllers/user.controller.ts";
import { User2Controller } from "./controllers/user2.controller.ts";
@Module({
imports: [],
controllers: [
UserController,
RoleController,
User2Controller,
],
})
export class UserModule {
}
Then this is your main.ts:
import { Context, isHttpError, NestFactory, Status } from "../mod.ts";
import { AppModule } from "./app.module.ts";
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix("api");
// Timing
app.use(async (ctx: Context, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
app.get("/hello", (ctx: Context) => {
ctx.response.body = "hello";
});
app.use(app.routes());
const port = Number(Deno.env.get("PORT") || 1000);
console.log(`app will start with: http://localhost:${port}`);
await app.listen({ port });
If you want to register a Model such as Mongodb, you can do like this:
import { Module } from "https://deno.land/x/oak_nest@v0.5.4/mod.ts";
import { AppController } from "./app.controller.ts";
import { UserModule } from "./user/user.module.ts";
@Module({
imports: [
MongoFactory.forRoot(globals.db),
UserModule,
],
controllers: [AppController],
})
export class AppModule {}
And you maybe register your Model:
export const InjectModel = (Cls: Constructor) =>
(target: Constructor, _property: any, index: number) => {
Reflect.defineMetadata("design:inject" + index, {
params: [Cls],
fn: getModel,
}, target);
};
The function getModel
can return a new Model which will be used in Service
.
It maybe like this:
async function getModel<T>(
cls: SchemaCls,
): Promise<Model<T>> {
// do some else
return new cls();
}
To support it, I changed the
router.add
method to asynchronous. It was not a pleasant decision.
Here is a Service example:
@Injectable()
export class UserService {
constructor(@InjectModel(User) private readonly model: Model<User>) {
}
async save(createUserDto: AddUserDto): Promise<string> {
console.log(this.model);
const id = await this.model.insertOne(createUserDto);
console.log(id);
return id.toString();
}
}
In the above code, this.model
is the getModel
result.
You can see more in the example dirs.