nestia icon indicating copy to clipboard operation
nestia copied to clipboard

Customize/pluggable SDK output

Open SkyaTura opened this issue 1 year ago • 12 comments

The considerations written below are wonderings of mine about the possibilities this tool could offer. Please let me know if they are out of scope or overthinking.


The current methodology used for code generation is kinda restrained to a fixed output format. Although the current output is actually fine, on bigger problems this could, quickly, get harder to use or augment.

My proposition is parametrization of each (possible) step of the flow from analysis to code generation. This would allow creation of custom plugins to add more powerful features to the final output without too much effort.

Here are some examples of pluggable features:

Runtime validators

Using the definitions extracted from the controllers, create runtime validation helpers to facilitate integration with reactive frontends, providing input errors in realtime without need for calling the API.

Many validation libraries could also be used for this integration.

Empty model generator

Also intending to ease integration with frontends, this would expose a method that could initialize forms with valid empty fields, expanding the objective of remove code repetition, since would've already defined how this object should be.

Random data generator

A plugin could provide decorators to specify methods of generating random (or not) examples of usage of the exposed methods/schemas.

This one could be used on prototypes, automated tests, to extend documentation descriptions, and more.

One example of lib to use in this is faker-js.

Choose data fetcher

Currently nestia uses it's own fetcher, but should be possible to customize or even switch it with another to specify any internal requirement.

Intercept requests/responses

This would be like middlewares for client side, allowing the developer to have more control on error handling strategies, offline behavior, state management, etc.

Prototyping mode

Using the some of the already described above, this one would allow to generate automatic responses on the frontend without even calling the API while working on features that doesn't actually need to persist anything in the backend.

Important

Interceptions

While these features are already possible to implement using service workers, actually doing it isn't trivial, and I think this could be improve the development experience substantially

Separation of concern

All these implementations could be possible, the responsibility of any of them should be on nestia itself, but on independent packages (official or not) that would benefit from exposed options.

Complexity

I agree that this is not an easy thing to implement, and a lot of mental work should yet be made on structuring how this would be possible, but I want to know what the community, and the author, thinks about the development of this idea.

~This issue ended up being bigger than I initially expected, which makes me even more excited about~

SkyaTura avatar Aug 10 '22 16:08 SkyaTura

Thinking even further, some of these plugins could also benefit the swagger output, or any other format desired by the end developer

SkyaTura avatar Aug 10 '22 16:08 SkyaTura

Runtime validators

@SkyaTura You can turn on runtime validator by configuring nestia.config.ts file like below:

import nestia from "nestia";

export const NEESTIA_CONFIG: nestia.IConfiguration = {
    assert: true
};

When turning on the assert option, SDK functions generated nestia utilizes my another library typescript-json's assertType function to validate parameter types like below code:

import TSON from "typescript-json";

export function store
    (
        connection: IConnection,
        orderId: string,
        goodId: string,
        input: Primitive<store.Input>
    ): Promise<store.Output>
{
    TSON.assertType<typeof orderId>(orderId);
    TSON.assertType<typeof goodId>(goodId);
    TSON.assertType<typeof input>(input);

    return Fetcher.fetch
    (
        connection,
        store.ENCRYPTED,
        store.METHOD,
        store.path(orderId, goodId),
        input
    );
}
export namespace store
{
    export type Input = Primitive<IShoppingOrderGoodRevert.IStore>;
    export type Output = Primitive<IShoppingOrderGoodRevert>;

    export const METHOD = "POST" as const;
    export const PATH: string = "/shoppings/consumers/orders/:orderId/goods/:goodId/revert";
    export const ENCRYPTED: Fetcher.IEncrypted = {
        request: true,
        response: true,
    };

    export function path(orderId: string, goodId: string): string
    {
        return `/shoppings/consumers/orders/${encodeURIComponent(orderId)}/goods/${encodeURIComponent(goodId)}/revert`;
    }
}
```typescript

samchon avatar Aug 12 '22 06:08 samchon

Random data generator

Also, I'm tending to utilize SDK generated by nestia when implementing test automation program.

During those implementations, I also feel that random generator utilizing the DTO type would be useful. However, nestia only understands primitive type. Therefore, it can generate random literal, string or numeric values, but it can dissapoint developers when special logic random data required.

If you have any good strategy about the random data generator, please tell me.

Also, how do you think about that publishing a new library for the random data generator using TypeScript DT type?

samchon avatar Aug 12 '22 06:08 samchon

Fetcher & Interceptor

To accomplish this mission, there would be two ways:

The 1st is supporting more options on the IConnection structure, as functional instance. The 2nd is configurating such options in the nestia.config.ts level. Interceptor, 1st option would be suitable for it. About using whether fetcher or axios, 2nd option would be better.

I'll try 1st option with interceptors after #128.

samchon avatar Aug 12 '22 06:08 samchon

Random data generator

Also, I'm tending to utilize SDK generated by nestia when implementing test automation program.

During those implementations, I also feel that random generator utilizing the DTO type would be useful. However, nestia only understands primitive type. Therefore, it can generate random literal, string or numeric values, but it can dissapoint developers when special logic random data required.

If you have any good strategy about the random data generator, please tell me.

Also, how do you think about that publishing a new library for the random data generator using TypeScript DT type?

I think Decorators would be a useful way to customize how random data could be generated.

I know one of the key points of nestia is the minimum usage of additional artifacts, but I think this is a good trade-off for those who want to take advantage of this feature.

Also, I do in fact think a dedicated lib on that matter would be great.

SkyaTura avatar Aug 12 '22 06:08 SkyaTura

Runtime validators

@SkyaTura You can turn on runtime validator by configuring nestia.config.ts file like below:

// ...

When turning on the assert option, SDK functions generated nestia utilizes my another library typescript-json's assertType function to validate parameter types like below code:

// ...

What I meant was about more specific validations, like zod and joi.

SkyaTura avatar Aug 12 '22 07:08 SkyaTura

Runtime validators

@SkyaTura You can turn on runtime validator by configuring nestia.config.ts file like below:

// ...

When turning on the assert option, SDK functions generated nestia utilizes my another library typescript-json's assertType function to validate parameter types like below code:

// ...

What I meant was about more specific validations, like zod and joi.

TSON.assertType() can generate validator function automatically by analyzing DTO in the compilation level. However, zod and joi are not.

In that case, should nestia generate schema of them automatically, through TypeScript Compiler API?

samchon avatar Aug 12 '22 08:08 samchon

Yes, by using transformers, and pluggable hooks, one could create it's own validation strategy, or random data generators or whatever, not necessarily predefined on nestia core itself.

The value in that would be abstract the complexity of analysing the ASTs and extracting useful information from nestjs patterns and ease those compile-time plugins.

That would be similar to what frameworks like Nuxt do to improve developer experience with Vue.

Since the idea seems to be considered promising, I'll try to draft some examples of inputs and desirable outputs over the weekend, to better illustrate and help us to find the best approach to achieve it.

SkyaTura avatar Aug 12 '22 09:08 SkyaTura

In addition, I found a great example to inspire for annotating things TS can't provide.

https://github.com/fabien0102/ts-to-zod

SkyaTura avatar Aug 12 '22 09:08 SkyaTura

@SkyaTura Have studied your suggestion libraries zod and joi.

As a result of the study, I won't develop automatic zodand joi schema generators, beacuse they can't support complicated structured union object type. As I'd tried to implement automatic JSON schema (ajv), I know that how such libraries are vulnerable to union types.

https://github.com/samchon/typescript-json#runtime-type-checkers

ajv, zod and joi, all of them can't retrieve optimal search path and analyze inclusion relationships.

Therefore, if user wants to utilize zod or joi, it would better user to define zod or joi schema by user himself. Instead, I will support format comments like ts-to-zod from typescript-json. I'm sorry for cannot accept your suggestion.

https://github.com/samchon/typescript-json/issues/175

samchon avatar Aug 15 '22 12:08 samchon

@SkyaTura Random generator would be support by two ways: through pure type and decorator.

The 1st pure type based random generator would be supported by https://github.com/samchon/typescript-json/issues/174.

The 2nd would be supported by nestia-helper and its decorator maybe @Random. However, using such decorator function can't assure type safety. Therefore, I'm considering how to make it safey.

Also, not only for the random generator case, supporting client library (nestia generated SDK) through decorator function in server side, it seems really an wonderful idea. I think many helper decorator functions for client developers would be developed continuosuly.

TSON.random<IShoppingSale.IStore>(); // 1st, automated generator
api.functional.shoppings.sellers.sales.store.random(); // 2nd, manual generator

@nest.Controller("shoppings/sellers/sales")
export class ShoppingSellerSalesController {
    public async store(
        @nest.Request() request: express.Request,
        @helper.Random(() => generate_random_sale())
        @helper.TypedBody() 
            input: IShoppingSale.IStore
    ): Promise<IShoppingSale>;
}

Anway, the random generator is only for request body data? or for both request and response body data?

samchon avatar Aug 15 '22 12:08 samchon

@samchon Unfortunately, I had no time this weekend to think further about these as I intended to.

I hope that I can study on my on as well so that I can help you as much as I can in this process. (I'm literally in love with this project)

Anway, the random generator is only for request body data? or for both request and response body data?

I think both would be ideal, since the most useful use case for this feature would be prototyping and automated tests (both on the server and the project that implements this client)

I can think at least in this two scenarios:

  • pre fill forms with random data to perform demonstrations
  • generate random responses without an actual server running to improve both developer experience and frontend automated tests

In sake of organization, what do you think about breaking this subject in a dedicated issue to discuss about the specs and requirements on each feature we consider useful, and another to discuss about the tecnicallyties envolved with making them possible in general?

SkyaTura avatar Aug 15 '22 13:08 SkyaTura

validation and random data generation are fully completed:

  • validation through @nestia/core
  • random data generation through typia.random<T>()

samchon avatar Mar 16 '23 17:03 samchon