Giter Site home page Giter Site logo

savory / danet Goto Github PK

View Code? Open in Web Editor NEW
208.0 4.0 17.0 2.48 MB

The most mature backend framework for Deno. Create awesome HTTP and WebSocket server as well as KVQueue workers !

Home Page: https://danet.land

License: Other

TypeScript 99.97% Shell 0.01% Handlebars 0.01%
deno typescript dependency-injection framework ioc hacktoberfest websocket websocket-server

danet's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

danet's Issues

Controllers are all singleton despite dependencies having SCOPE.REQUEST

Describe the bug
A clear and concise description of what the bug is.

For now, Controllers are singleton;

Expected behavior

Controller whose one or more dependencies have SCOPE.REQUEST should be instantiated on every request.

We need to add metadata on the controller that tags it as "SCOPE.REQUEST" when injecting its dependencies. Then when adding it to resolved map, either uses a singleton or a factory.

Support for simple mongodb paths

My mongo database is at

'mongodb://127.0.0.1:27017/exp_danet'

I can directly alter

mongodb.service.ts

but would nice to be able to specify it in .env

Inject by token

Is your feature request related to a problem? Please describe.
Currently, we cannot do something like the following :

@Controller()
class Controller {
  constructor(@Inject('DATABASE_SERVICE') databaseService)
}

Describe the solution you'd like
We should be able to do it

Additional context
Maybe use TSyringe to handle injection instead of doing it by hand

Danet script for common task

how can I create script in Danet project for common tasks like create tables, create user, database migrations.....? I hope see some doc about this or examples.

Refactor similar behaviors between GuardExecutor and FilterExecutor

Is your feature request related to a problem? Please describe.

GuardExecutor and FilterExecutor look alike. We should probably be able to refactor this!

Describe the solution you'd like

An executor class with an attribute that contains the related metadatakey to get the function to execute. Then maybe create children classes for GuardExecutor and FilterExecutor.

Reconsider if you really want to mimic NestJS

As you are apparently considering a V2 I wanted to make some suggestions with the question: Do you really want to rebuild NestJS?

I am asking because the dependency injection and the modules can make the life of a developer hard and frustrating (thinking of import/export of services, useFactory, circular dependencies). Also NestJS takes 81 ms to start an empty Express app while "plain" Express takes 7 ms and is therefor not usable for serverless functions.

Looking back into Angular (the inspiration of NestJS) it is not needed anymore to register a class that has the @Injectable() decorator. Instead it "just works" and is even tree shaked if it is not used anywhere. Also a thing that Angular did is the remove of modules: Standalone apps dont need modules anymore (but can still import modules for compatibility), components (in our case controllers) import whatever they need in their @Component() decorator and Angular does the magic of putting it all together and tree shaking what is not used.

I just wanted to add this things to consider, you might close this issue :)

P.S. What about another name as well? I am not native english and this name "Danet" is really hard to remember, especially compared to something fresh like "fresh" or even the dead project "mandarine".

Access controller/method in context

Is your feature request related to a problem? Please describe.

Let's say I have a global AuthGuard because almost all my routes need authentication.
The few that don't need it should have a decorator such as @Public that set metadata.

In my global AuthGuard, I should be able to get a reference to the Method (via ctx) to get access to its metadata.

Describe the solution you'd like

In router/router.ts:handleRoute, we should add Controller and ControllerMethod in ctx !

Additional context

Maybe something similar to NestJS

 ctx.getHandler(),
 ctx.getClass(),

Add classes for all HTTP Exception

Is your feature request related to a problem? Please describe.
For now, we only have ForbiddenHttpException defined as follow :

import { HTTP_STATUS } from './enum.ts';

export class ForbiddenHttpException {
	public status = HTTP_STATUS.FORBIDDEN;
	public message = 'Forbidden';
}

We should defined a class for each exception listed below:

        BAD_REQUEST = 400,
	UNAUTHORIZED = 401,
	PAYMENT_REQUIRED = 402,
	FORBIDDEN = 403,
	NOT_FOUND = 404,
	METHOD_NOT_ALLOWED = 405,
	NOT_ACCEPTABLE = 406,
	PROXY_AUTHENTICATION_REQUIRED = 407,
	REQUEST_TIMEOUT = 408,
	CONFLICT = 409,
	GONE = 410,
	LENGTH_REQUIRED = 411,
	PRECONDITION_FAILED = 412,
	PAYLOAD_TOO_LARGE = 413,
	URI_TOO_LONG = 414,
	UNSUPPORTED_MEDIA_TYPE = 415,
	REQUESTED_RANGE_NOT_SATISFIABLE = 416,
	EXPECTATION_FAILED = 417,
	I_AM_A_TEAPOT = 418,
	MISDIRECTED = 421,
	UNPROCESSABLE_ENTITY = 422,
	FAILED_DEPENDENCY = 424,
	PRECONDITION_REQUIRED = 428,
	TOO_MANY_REQUESTS = 429,
	INTERNAL_SERVER_ERROR = 500,
	NOT_IMPLEMENTED = 501,
	BAD_GATEWAY = 502,
	SERVICE_UNAVAILABLE = 503,
	GATEWAY_TIMEOUT = 504,
	HTTP_VERSION_NOT_SUPPORTED = 505

[REQUEST] Fresh Integration: Allow jsonc type to Deno config file

Problem
If you are importing a fresh project into Danet that has Deno config file in jsonc, then fresh integration won't work.

fresh_app
         |_ deno.jsonc
Captura de Tela 2023-09-23 às 13 01 18

Error

$ deno task launch-server

Task launch-server deno run --allow-net --allow-read --allow-run --allow-write --unstable --allow-env run.ts
The manifest has been generated for 7 routes and 0 islands.
error: Uncaught (in promise) TypeError: Module not found "file:///danet_app/src/fresh_app/deno.json".
    at file:///danet_app/src/fresh_app/fresh.gen.ts:5:20
		const manifest = (await import(url + './fresh.gen.ts')).default;
		                  ^
    at async Function.enableFreshOnRoot (https://deno.land/x/[email protected]/mod.ts:43:21)
    at async bootstrap (file:///danet_app/src/bootstrap.ts:14:3)
    at async file:///danet_app/run.ts:3:21

Solution

Allow to pass Deno config file type in FreshModule.enableFreshOnRoot function.

Enum options: json (default), jsonc

enum DenoConfigFileType {
    JSON = 'json',
    JSONC = 'jsonc'
}

auth modules as referance

I think there a need for auth modules as referance, in starter repo or in other place. I suggess so because most project need auth.

@Session decorator

We should be able to get the session in controllers' methods without using @Req decorator, using a new decorator @Session.

It could either take a parameter to return a specific attribute, or no parameter to return the whole session.

It would be used as follow :

class CustomSessionContent {
   userId: string;
   userToken: string;
}

@Controller('')
export class AppController {
  constructor() {
  }

  @Get()
  helloWorld(@Session() mySession: CustomeSessionContent, @Session('userId') userId: string) {
  }
}

Validate body

When using @Body decorator, we should be able to validate the body we are receiving

Using our validatte package, we should validate bodies.

This should be an opt-out feature, we validate by default.

opt-out may look like this :

export const bootstrap = async () => {
  const application = new DanetApplication();
  application.disableBodyValidation();
  await application.init(AppModule);
  return application;
}

Upon injecting the body in router.ts we can validate it, and throws a 400 Bad Request containing validateObject return value.

SetMetadata function to create decorators

Is your feature request related to a problem? Please describe.

If I want to set a controller/method metadata, for example with a Public decorator, I have to create it as follows:

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () =>
(
	// deno-lint-ignore ban-types
	target: ControllerConstructor | Object,
	propertyKey?: string | symbol,
	// deno-lint-ignore no-explicit-any
	descriptor?: TypedPropertyDescriptor<any>,
) => {
	if (propertyKey && descriptor) {
		MetadataHelper.setMetadata(
			IS_PUBLIC_KEY,
			true,
			descriptor.value,
		);
	} else {
		MetadataHelper.setMetadata(IS_PUBLIC_KEY, true, target);
	}
};

This is way too verbose, especially when creating multiple decorators that only set metadata.

Describe the solution you'd like

We should create a SetMetadata function to be used as follows:

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

[REQUEST] Fresh Integration: Add fresh `imports` information in the integration docs

Problem
If you are importing a fresh project into Danet that has imports within deno.json file, then fresh integration won't work.

// danet_app/src/fresh_app/deno.json

{
  "lock": false,
  "tasks": {
    "build": "deno run -A dev.ts build",
    "compile:sass": "sass ./static/scss/styles.scss:./static/css/styles.min.css --style compressed",
    "compile:sass:watch": "sass --watch ./static/scss/styles.scss:./static/css/styles.min.css --style compressed",
    "preview": "deno run -A main.ts",
    "start": "deno run -A --watch=watch=static/,routes/ dev.ts",
    "start:dev": "deno task compile:sass && (deno task compile:sass:watch & deno task start)",
    "update": "deno run -A -r https://fresh.deno.dev/update ."
  },
  "imports": {
    "$fresh/": "https://deno.land/x/[email protected]/",
    "$fresh-seo/": "https://deno.land/x/[email protected]/",
    "$std/": "https://deno.land/[email protected]/",
    "@preact/signals": "https://esm.sh/*@preact/[email protected]",
    "@preact/signals-core": "https://esm.sh/*@preact/[email protected]",
    "preact": "https://esm.sh/[email protected]",
    "preact/": "https://esm.sh/[email protected]/",
    "preact-render-to-string": "https://esm.sh/*[email protected]",
    "preact-router": "https://esm.sh/[email protected]",
    "wouter-preact": "https://esm.sh/*[email protected]",
    "wouter-preact/": "https://esm.sh/*[email protected]/"
  },
  "lint": {
    "rules": {
      "tags": [
        "fresh",
        "recommended"
      ]
    },
    "exclude": [
      "_fresh"
    ]
  },
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  },
  "fmt": {
    "exclude": [
      "_fresh"
    ]
  }
}

Error

$ deno task launch-server

Task launch-server deno run --allow-net --allow-read --allow-run --allow-write --unstable --allow-env run.ts
The manifest has been generated for 7 routes and 0 islands.
error: Uncaught (in promise) Error: deno.json must contain an 'importMap' property.
      throw new Error("deno.json must contain an 'importMap' property.");
            ^
    at Function.fromManifest (https://deno.land/x/[email protected]/src/server/context.ts:111:13)
    at Function.enableFreshOnRoot (https://deno.land/x/[email protected]/mod.ts:44:40)
    at async bootstrap (file:///danet_app/src/bootstrap.ts:14:3)
    at async file:///danet_app/run.ts:3:21

Solution

Add the information to the Integration docs that Fresh project dependencies should be added to the import_map file of the Danet project, and that the imports property in Fresh should be replaced by the importMap property pointing to the import_map file of Danet.

[REQUEST] Fresh Integration: Allow custom directory for the fresh.gen file

Problem

If you are importing a fresh project into Danet with custom folders like src folder, then fresh integration won't work.

fresh_app
         |_ src
                |_ fresh.gen.ts
Captura de Tela 2023-09-23 às 12 45 29

Error

$ deno task launch-server

Task launch-server deno run --allow-net --allow-read --allow-run --allow-write --unstable --allow-env run.ts

error: Uncaught (in promise) TypeError: Module not found "danet_app/src/fresh_app/fresh.gen.ts".
		const manifest = (await import(url + './fresh.gen.ts')).default;
		                  ^
    at async Function.enableFreshOnRoot (https://deno.land/x/[email protected]/mod.ts:43:21)
    at async bootstrap (file:///danet_app/src/bootstrap.ts:12:3)
    at async file:///danet_app/run.ts:3:21

Solution

Allow to pass fresh.gen.ts custom directory in FreshModule.enableFreshOnRoot function.

/ root directory as default

[DOCS] Link License and Contributing.md to ReadMe

Is your feature request related to a problem? Please describe.

  • Improving the docs by adding the License and contributing.md links to the ReadMe file.

Describe the solution you'd like

  • I would like to add the links of LICENSE and Contributing.md at the bottom of the readme file.

Describe alternatives you've considered

Additional context

Throw 403 when any Guard.canActivate returns false

Describe the bug
When a guard's canActivate method returns false, the controllers' code will be executed regardless of the fact that it shouldn't.

Expected behavior
Stop execution by throwing a 403 error if canActivate returns false

Support handlebar template engine

Is your feature request related to a problem? Please describe.
We should be able to render an HTML generated by Handlebars.

Describe the solution you'd like
A @View('viewname') decorator for controller methods
DanetRouter should get a TemplateEngine attribute that will render the given view, with variables returned from the controller's method.

Here is an example:

@Controller('')
class myController {

  @View('index')
  renderIndex() {
return { latestNews: ['an array of latests', 'news to display'] };
  }
}

Additional context
Take inspiration of nestjs

https://docs.nestjs.com/techniques/mvc

Doc: Some code example in REAME

The README could have some code to introduce the framework. We all know it is similar to Nest in Deno. But how much similar??

I thought something like this one below... based in Danet-Starter

If it's possible I'd like to open a simple PR to do that. Being assigned to this Issue =D

import { Module, Controller, Injectable, Get, DanetApplication } from 'https://deno.land/x/[email protected]/mod.ts';

// Services...
@Injectable()
export class helloService {
  sayHello() {
      console.log('Hello World')
  }
}

// Controllers...
@Controller('hello')
export class helloController {
  constructor(private service: helloService){}

  @Get()
  hello() {
    this.service.sayHello()
  }
}

// Modules...
@Module({
  controllers: [helloController],
  injectables: [helloService],
})
export class TodoModule {}

// Deploy...
export const bootstrap = async () => {
  const application = new DanetApplication();
  await application.init(AppModule);
  return application;
}

I can't use services like nest js

I want to use the service like in nest js
but apparently not working

is there something wrong with me

cats.controller.ts

import {
 Controller, 
 Injectable,
 Get } from 'https://deno.land/x/danet/mod.ts';

import {Childservice} from './service/my.service.ts';

@Controller('cats')
export class CatsController {
	constructor(private childservice:Childservice){}

  @Get()
  findAll(): string {
    return this.childservice.Gethello()
  }
}

my.service.ts

import {
 Controller, 
 Injectable,
 SCOPE,
 Get } from 'https://deno.land/x/danet/mod.ts';



@Injectable({ scope: SCOPE.REQUEST })
export class Childservice{
	Gethello(){
		return "ini service"
	}
}

error message in browser :
{"status":500,"message":"Cannot read properties of undefined (reading 'Gethello')"}

Serve static content

Is your feature request related to a problem? Please describe.
We should be able to do something like this app.useStaticAssets(join(__dirname, '..', 'public')); to render static assets from the given directory.

It could attach a middleware on oak app, such as the following from stackoverflow :

// static content
app.use(async (context, next) => {
    const root = `${Deno.cwd()}/${ROOTDIR}`
    try {
        await context.send({ root })
    } catch {
        next()
    }
})

Improve injectable scope in modules

For now, any injectable declared by a module can be injected in any other module.

For example:

@Module({
 injectables: [userService, privateLogicService]
})
export class UserModule {
}

privateLogicService will be accessible in any module that import UserModule.

We should encapsulate injectables to the current module scope and make them exportables with an export property.

Example :

@Module({
 injectables: [userService, privateLogicService],
 export: [userService]
})
export class UserModule {
}

Fresh Layouts are not working with Danet

Describe the bug

When adding a Fresh project to Deno, the Fresh Layouts don't work, both when using Fresh from the root and from a given path.


Fresh Project

Captura de Tela 2023-09-24 às 13 55 50

Fresh into Danet

Captura de Tela 2023-09-24 às 13 56 19

Expected
My layout for root routes including a header, footer, and content inside the main tag.

Console Logs

$ deno task launch-server

The manifest has been generated for 8 routes and 0 islands.
Sun, 24 Sep 2023 16:41:02 GMT [Injector]  Bootstraping DatabaseModule
Sun, 24 Sep 2023 16:41:02 GMT [Injector]  Bootstraping TodoModule
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering TodoController todo
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [GET] /todo
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [GET] /todo/:id
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [POST] /todo
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [PUT] /todo/:id
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [DELETE] /todo/:id
Sun, 24 Sep 2023 16:41:02 GMT [Injector]  Bootstraping AppModule
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering AppController /
Sun, 24 Sep 2023 16:41:02 GMT [Router]  Registering [GET] /
Sun, 24 Sep 2023 16:41:02 GMT [DanetApplication]  Listening on 3000
[uncaught application error]: Http - connection closed before message completed

    at Object.respondWith (ext:deno_http/01_http.js:334:25)
    at eventLoopTick (ext:core/01_core.js:197:13)
[uncaught application error]: Http - connection closed before message completed

request: {
  url: "http://localhost:3000/_frsh/alive",
  method: "GET",
  hasBody: false
}
response: { status: 200, type: undefined, hasBody: true, writable: false }

    at Object.respondWith (ext:deno_http/01_http.js:334:25)
    at eventLoopTick (ext:core/01_core.js:197:13)

Suggestion: Events for Danet

Is your feature request related to a problem? Please describe.

As a Nest.JS developer, I'd like the possibility of making an event-driven system using the framework decorators and module system.

References:

Describe the solution you'd like
I'm not sure about the internals, but I imagine something like this:

this.emitter.connect({
  // Add queue configs when the app is initializing (SQS, Kafka, etc)
})

than, I'd like to bind class methods to events:

// initialization
class MyListeners {

  @OnEvent('new-user')
  handleWelcomeNewUser(user: User) {
    // Do some side effect
  }

}

and also should be able to emit these events, something like:

// initialization
class MyListeners {

  createUser(user: UserDto) {
    // create user
    this.emitter.send('new-user', user)
  }

}

Describe alternatives you've considered

At the moment, the alternative I have considered is to make an EventEmitter module, which manages queues and subscriptions using Deno.Kv queues, to keep it as native as possible.

This is not ideal, but it can work if we set the (un)subscription in onAppBootstrap and onAppClose respectively.

@Injectable()
export class MyListeners implements OnAppBootstrap, OnAppClose {
  listeners: string[] = []

  constructor(@Inject(EventEmitter) private eventEmitter: EventEmitter) {}

  eventHandler() {
    // do something
  }

  async onAppBootstrap() {
    const eventId = await this.eventEmitter.subscribe('event', this.eventHandler);
    this.listeners.push(eventId)
  }

  async onAppClose() {
    await Promise.all(
      this.listeners.map(
        this.eventEmitter.unsubscribe,
      )
    );
  }

}

HTTP engine abstraction

For now, DanetApplication references an oak Application. We should abstract it to prepare for other HTTP engines

@Header decorator

We should be able to get any header using an @Header('myheadername') decorator in controller's methods.

Exceptions Filter

Is your feature request related to a problem? Please describe.
We should be able to attach a custom exception filter to the app, a controller, or a method.

Describe the solution you'd like
Create a @UseFiter decorator that take either a class name or class instance

Additional context
Take inspiration in Nest

does not provide an export named 'Reflect'

info:
deno 1.24.3 (release, x86_64-pc-windows-msvc) v8 10.4.132.20 typescript 4.7.4

deno run -A main.ts
error: Uncaught SyntaxError: The requested module './Reflect.ts' does not provide an export named 'Reflect' export { Reflect } from "./Reflect.ts";

Suggestion: Task Scheduling

Is your feature request related to a problem? Please describe.

As a Nest.JS developer, I'd like the possibility of running cron jobs using the framework decorators and module system.

References:

Describe the solution you'd like

I imagine something like this:

  1. First we register the module at the App root module:
@Module({
  imports: [
    ScheduleModule.forRoot()
  ],
})
export class AppModule {}
  1. Then we can use the @Cron decorator to register listeners to timed events:
@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('45 * * * * *')
  handleCron() {
    this.logger.debug('Called when the current second is 45');
  }
}

The ScheduleModule can be totally made using Deno native features, like the Deno.Cron module

Other than that, I think the implementation should be fairly similar to the Events feature (#76)

Describe alternatives you've considered

Inject Request on Controllers/Injectables that are request scoped

Is your feature request related to a problem? Please describe.

When a controller or an injectable is request scoped, it should be useful to give the actual Request object when instantiating them.

Describe the solution you'd like
Give Request to the injector when getting a class instance, so it can inject it on instantiating request-scoped classes.
Changing signature to :

public get<T>(Type: Constructor<T> | string, context?: httpContext): T {

Nest's way of doing it is clean :

@Injectable({ scope: Scope.REQUEST })
export class CatsService {
  constructor(@Inject(REQUEST) private request: Request) {}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.