elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
10.59k stars 226 forks source link

feat: add method instance.ready() #822

Open PandaWorker opened 2 months ago

PandaWorker commented 2 months ago

What is the problem this feature would solve?

If you do not wait in the router.modules.then controller, even if you put the wait on the main router, these routers will still not be loaded

If you use a controller that registers using use(import()), then if you do not wait for instance.modules, then these routers are loaded and will not be installed. I would like to add a .ready() method that would return a promise returning the current instance after instance.modules.then()

What is the feature you are proposing to solve the problem?

// get-by-id.command.ts

import { Elysia, t } from 'elysia';

export default new Elysia().get(
    '/:id',
    () => {
        return { id: 1 };
    },
    {
        params: t.Object({
            id: t.Integer({ example: 1 }),
        }),
    }
);

// controller.ts
import { Elysia } from 'elysia';

export const controller = new Elysia({ prefix: '/bug' }).use(
    import('./get-by-id.command.ts')
);

// may be .ready(): Promise<Elysia> ?

// work 
// await controller.modules;

// work But it cannot be used in .use without additional wrapping 
export const asyncController = controller.modules.then(() => controller)

// not work
// export default controller;

// work
export default asyncController

// main.ts

import { swagger } from '@elysiajs/swagger';
import { Elysia } from 'elysia';

const app = new Elysia()
    .use(swagger({provider: 'swagger-ui', path: '/docs'}))
    .use(import('./controller.ts'))
    .listen(3000)

What alternatives have you considered?

import { Elysia } from 'elysia';

export const controller = new Elysia({ prefix: '/bug' }).use(
    import('./get-by-id.command.ts')
).ready();

export default controller;
kravetsone commented 2 months ago

await app.modules?

PandaWorker commented 2 months ago

app.modules returns a non-elysia instance

To do this, I suggest making a wrapper in the form of:

export const asyncController = controller.modules.then(() => controller)

I don't want to write wrappers every time.

const controller = new Elysia().use(import('./a.ts'));

export default controller.modules.then(() => controller)

I want to return the promise instance immediately if there are dynamic modules

export default new Elysia()
  .use(import('./a.ts'))
  .ready()
kravetsone commented 2 months ago

app.modules returns a non-elysia instance

To do this, I suggest making a wrapper in the form of:

export const asyncController = controller.modules.then(() => controller)

I don't want to write wrappers every time.

const controller = new Elysia().use(import('./a.ts'));

export default controller.modules.then(() => controller)

I want to return the promise instance immediately if there are dynamic modules

export default new Elysia()
  .use(import('./a.ts'))
  .ready()

Why not just put await everywhere before import?

PandaWorker commented 2 months ago

This does not solve the problem

PandaWorker commented 2 months ago

I just want to call ready() if there are dynamic imports from the instance, which will return a promo with the instance when all dynamic modules are loaded. And the instance that will use this module will already use a ready-made instance where all dynamic modules are loaded

Now, in order to use dynamic modules, you need to write your own wrappers, which will wait until the dynamic modules are loaded

const controller = new Elysia().use(import('./a.ts'));

export default controller.modules.then(() => controller)

a.ts

import { Elysia } from 'elysia';

const app = new Elysia()
    .use(import('./b.ts'))
await app.modules;

// app without routes c & d
console.log(app);

import b from './b.ts'; // ????
await b.modules; // ????

const app2 = new Elysia()
    .use(b)

// app with routes c & d
console.log(app2);

b.ts

import { Elysia } from 'elysia';

export default new Elysia({ prefix: '/api' })
    .use(import('./c.ts'))
    .use(import('./d.ts'));

c.ts

import { Elysia } from 'elysia';

export default new Elysia().get('/hello', () => 'hello');

d.ts

import { Elysia } from 'elysia';

export default new Elysia().get('/world', () => 'world');
PandaWorker commented 2 months ago

@SaltyAom , I hope you understand me, please add this feature

PandaWorker commented 2 months ago
import { Elysia } from 'elysia';

// use .ready() method
// export default new Elysia({ prefix: '/api' })
//  .use(import('./c.ts'))
//  .use(import('./d.ts'));
//  .ready() // Promise<Elysia>

const component = new Elysia().use(import('./c.ts')).use(import('./d.ts'));

// write async plugin for waiting ?
export const plugin = (): Promise<Elysia> => {
    // wait for all loading dynamic modules and return component
    return component.modules.then(() => component);
};

// Promise<Elysia>
export default component.modules.then(() => component);
kravetsone commented 2 months ago

I just want to call ready() if there are dynamic imports from the instance, which will return a promo with the instance when all dynamic modules are loaded. And the instance that will use this module will already use a ready-made instance where all dynamic modules are loaded

Now, in order to use dynamic modules, you need to write your own wrappers, which will wait until the dynamic modules are loaded

const controller = new Elysia().use(import('./a.ts'));

export default controller.modules.then(() => controller)

a.ts

import { Elysia } from 'elysia';

const app = new Elysia()
  .use(import('./b.ts'))
await app.modules;

// app without routes c & d
console.log(app);

import b from './b.ts'; // ????
await b.modules; // ????

const app2 = new Elysia()
  .use(b)

// app with routes c & d
console.log(app2);

b.ts

import { Elysia } from 'elysia';

export default new Elysia({ prefix: '/api' })
  .use(import('./c.ts'))
  .use(import('./d.ts'));

c.ts

import { Elysia } from 'elysia';

export default new Elysia().get('/hello', () => 'hello');

d.ts

import { Elysia } from 'elysia';

export default new Elysia().get('/world', () => 'world');

Just add waitElysia() helper? two lines of code

PandaWorker commented 2 months ago

add method .ready in class Elysia

ready(){ return this.modules.then(() => this); }