برنامه نویسی

[NestJS] من شبیه ساز باطن (مسخره) را برای برنامه های ظاهری ساختم

خلاصه

من شبیه ساز سرور باطن NestJS را در نستیای کتابخانه خود ساختم.

شما می توانید شبیه ساز سرور باطن را تنها با یک دستور خط بسازید.

nestia کدهای سرور شما را تجزیه و تحلیل می‌کند و کدهای تقلیدی را تولید می‌کند که داده‌های ماکت‌آپ را تأیید و ترکیب می‌کنند. می توان آن را در برنامه های جلویی، بدون تعامل با سرور واقعی استفاده کرد.

  • کلاینت (frontend) نیازی به اتصال با سرور باطن ندارد
  • Frontend می تواند توسعه را شروع کند حتی اگر سرور باطن آماده نباشد
  • Backend نیازی به هدر دادن زمان برای ترکیب داده های ماکت ندارد
const article: IBbsArticle = await api.functional.bbs.articles.store(
    {
        random: true, // activate simulator
        host: "http://127.0.0.1", // not important when simulating
    },
    "notice",
    {
        title: "Hello, world!",
        content: "This is a test article.",
    },
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

علاوه بر این، nestia از ویژگی های بسیار راحت تر و قدرتمندتر مانند زیر پشتیبانی می کند. با این ویژگی ها، می توانید بهره وری بالایی داشته باشید، حتی با به دست آوردن عملکرد بالا و توسعه آسان.

  • بهره وری

    • ژنراتور خودکار توابع e2e
    • شبیه ساز NestJS برای توسعه دهندگان فرانت اند
    • کتابخانه SDK برای توسعه دهندگان frontend
  • کارایی

    • اعتبارسنجی 25000 برابر سریعتر
    • سریال سازی JSON 200 برابر سریعتر
    • عملکرد کاملاً 30 برابر
  • توسعه آسان

    • فقط نوع TypeScript خالص مورد نیاز است
    • علاوه بر این، NestJS به تعاریف DTO 3 بار تکراری نیاز دارد

ps) در حال حاضر، شبیه ساز سرور باطن سطح frontend تنها زمانی امکان پذیر است که سرور باطن توسط NestJS (+nestia) پیاده سازی شود. با این حال، به زودی در هر چارچوب سرور باطنی امکان پذیر خواهد بود.

پیشگفتار

در حال حاضر، من یک شبیه ساز سرور باطن NestJS در نستیای کتابخانه خود توسعه داده ام.

برای مرجع، شبیه ساز به سرور باطن راه دور متصل نمی شود. این فقط اعتبار داده درخواست و ساخت داده پاسخ را به خودی خود تقلید می کند. بنابراین، توسعه دهندگان فرانت اند می توانند توسعه برنامه را تنها با رابط های API شروع کنند، حتی اگر سرور باطن آماده نباشد.

در زیر قطعه ای از کد SDK (کیت توسعه نرم افزار) است که توسط nestia تولید شده و از حالت شبیه سازی پشتیبانی می کند. همانطور که می بینید، شبیه ساز فقط با پیکربندی فعال می شود IConnection.random ارزش بودن true. در غیر این صورت، SDK به سرور پشتیبان راه دور متصل می شود.

/**
 * @packageDocumentation
 * @module api.functional.bbs.articles
 * @nestia Generated by Nestia - https://github.com/samchon/nestia 
 */
//================================================================
import { Fetcher } from "@nestia/fetcher";
import type { IConnection } from "@nestia/fetcher";
import typia from "typia";

import { NestiaSimulator } from "./../../../utils/NestiaSimulator";
import type { IBbsArticle } from "./../../../structures/IBbsArticle";

/**
 * Update an article.
 * 
 * @param section Section code
 * @param id Target article ID
 * @param input Content to update
 * @returns Updated content
 * 
 * @controller BbsArticlesController.update()
 * @path PUT /bbs/:section/articles/:id
 * @nestia Generated by Nestia - https://github.com/samchon/nestia
 */
export async function update(
    connection: IConnection,
    section: string,
    id: string,
    input: update.Input,
): Promise<update.Output> {
    return !!connection.random
        ? update.simulate(
              connection,
              section,
              id,
              input,
          )
        : Fetcher.fetch(
              connection,
              update.ENCRYPTED,
              update.METHOD,
              update.path(section, id),
              input,
          );
}
export namespace update {
    export type Input = IBbsArticle.IStore;
    export type Output = IBbsArticle;

    export const METHOD = "PUT" as const;
    export const PATH: string = "/bbs/:section/articles/:id";
    export const ENCRYPTED: Fetcher.IEncrypted = {
        request: false,
        response: false,
    };

    export const path = (section: string, id: string): string => {
        return `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`;
    }
    export const random = (g?: Partial<typia.IRandomGenerator>): Output =>
        typia.random<Output>(g);
    export const simulate = async (
        connection: IConnection,
        section: string,
        id: string,
        input: update.Input,
    ): Promise<Output> => {
        const assert = NestiaSimulator.assert({
            method: METHOD,
            host: connection.host,
            path: path(section, id)
        });
        assert.param("section")("string")(() => typia.assert(section));
        assert.param("id")("uuid")(() => typia.assert(id));
        assert.body(() => typia.assert(input));
        return typia.random<Output>(
            typeof connection.random === 'object'
            && connection.random !== null
                ? connection.random
                : undefined
        );
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در چارچوب توسعه دهندگان باطن، آنها همچنین نیازی به نوشتن داده های ماکت ندارند.

ترکیب داده‌های ماکت‌آپ توسط شبیه‌ساز خودکار می‌شود و توسعه‌دهندگان باطن ممکن است روی منطق تجاری مانند طراحی‌های رابط API و توسعه‌های برنامه اصلی تمرکز کنند.

مانند کد مثال زیر، فقط رابط های API را تعریف کنید. سپس، شبیه ساز می سازد و به توسعه دهندگان فرانت اند ارائه می دهد. سپس شما توسعه دهنده باطن می توانید برنامه را همزمان با توسعه دهندگان فرانت اند به صورت موازی پیاده سازی کنید.

برای referecen، nestia همچنین می تواند توابع تست e2e را به طور خودکار تولید کند. بنابراین، اگر موفق به تعیین مشخصات API شده اید، می توانید فقط بر روی توسعه برنامه اصلی تمرکز کنید.

@Controller("bbs/articles")
export class BbsArticlesController {
    @TypedRoute.Post()
    public async store(
        @TypedBody() input: IBbsArticle.IStore,
    ): Promise<IBbsArticle> {
        return typia.random<IBbsArticle>();
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بسته توسعه نرم افزار

SDK

در توسعه نرم افزارهای سنتی، توسعه دهندگان فرانت اند با خواندن مشخصات API سرور بک اند را تشخیص می دهند Swagger Documents یا مشابه مشابه RestDocs. به هر حال، از آنجایی که توسعه دهندگان frontend ربات نیستند، بلکه انسان هستند، خواندن و نوشتن مجدد مشخصات API در برنامه frontend کاری بسیار آزاردهنده و مستعد خطا است.

nestia همچنین می تواند اسناد Swagger را تولید کند و نسبت به NestJS سنتی بسیار تکامل یافته است. با این حال، من به توسعه دهندگان باطن NestJS توصیه می کنم از کتابخانه SDK بسیار بیشتر برای توسعه دهندگان frontend استفاده کنند.

در مورد nestia، می تواند کتابخانه SDK (کیت توسعه نرم افزار) را برای توسعه دهندگان frontend ایجاد کند. همانطور که از کد مثال بالا می بینید، کتابخانه SDK بسیار ساده و آسان برای استفاده است. فقط import و function بیانیه تماس مورد نیاز است. کتابخانه SDK توسعه frontend را از طریق راهنمایی های نوع بسیار ایمن تر می کند. شبیه ساز سرور باطن NestJS نیز توسط کتابخانه SDK ارائه شده است.

به هر حال، نستیا چگونه کتابخانه SDK را تولید می کند؟ راز در کد منبع شماست. nestia کد سرور باطن NestJS شما را مستقیما می خواند (مخصوصا controllers، و تجزیه و تحلیل می کند که کدام مسیرهای API ارائه شده است، و کدام نوع DTO استفاده می شود.

پس از این تحلیل ها نستیا می نویسد fetch توابع برای هر مسیر API، و دستورات وارد کردن که در هر کلاس کنترلر استفاده می شود. کتابخانه SDK توسط چنین تجزیه و تحلیل کد منبع و ترکیب توابع واکشی و عبارات واردات ایجاد می شود.

شبیه‌ساز سرور باطن NestJS، فقط یک توسعه کوچک از چنین کتابخانه SDK است.

تدوین AOT

به هر حال، در بخش قبلی #Summary، گفته بودم که nestia بسیار ساده تر از NestJS سنتی است. من گفته بودم که هنگام تعریف طرحواره DTO، nestia فقط به نوع TypeScript خالص نیاز دارد، اما NestJS سنتی نیاز به تعاریف تکراری سه برابری دارد.

با نگاهی به مثال زیر تعاریف طرحواره DTO، ممکن است متوجه شوید که من چه می گویم.

//----
// Traditional NestJS needs 3x duplicated definitions
//----
export class BbsArticle {
    @ApiProperty({
        type: () => AttachmentFile,
        nullable: true,
        isArray: true,
        description: "List of attached files.",
    })
    @Type(() => AttachmentFile)
    @IsArray()
    @IsOptional()
    @IsObject({ each: true })
    @ValidateNested({ each: true })
    files!: AttachmentFile[] | null;
}

//----
// Besides, nestia can understand pure TypeScript type
//----
export interface IBbsArticle {
    /**
     * List of attached files.
     */
    files: IAttachmentFile[] | null;
}

//----
// Therefore, advanced NestJS controller code can be
//----
@Controller("bbs/articles")
export class BbsArticlesController {
    @TypedRoute.Post()
    public async store(
        @TypedBody() input: IBbsArticle.IStore,
    ): Promise<IBbsArticle> {
        // just fine with pure interface
        //
        // validation is 25,000x times faster
        // JSON serialization is 200x faster
        ...
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با نگاهی به کد مثال بالا، ممکن است کسی از من بپرسد:

“هی سامچون، چگونه اعتبار سنجی را انجام دهیم IBbsArticle.IStore نوع

اگر مشتری با داده های نامعتبر درخواست کند، آیا امکان رد وجود دارد؟ همانطور که می دانید، رابط TypeScript هیچ گونه اطلاعات طرح واره ای در زمان اجرا ندارد و به همین دلیل است که NestJS سنتی توسعه دهندگان را مجبور به تعریف طرحواره های DTO سه بار تکراری کرده است.

با این حال، پاسخ من این است که “به اندازه کافی ممکن است”.

همچنین، حتی در این مورد، راز در کد منبع شما است.

وقتی سرور باطن NestJS خود را کامپایل می‌کنید، نستیا کدهای منبع شما را تجزیه و تحلیل می‌کند و نحوه ایجاد طرحواره‌های DTO را تجزیه و تحلیل می‌کند. با خواندن طرحواره‌های DTO و ویژگی‌های سفر آنها، nestia کدهای اعتبارسنجی و سریال‌سازی JSON را برای هر مسیر API ایجاد می‌کند.

با نگاهی به کد کامپایل شده زیر، به طور اختصاصی برای آن بهینه شده است IBbsArticle.IStore نوع این راز نستیا است که به تعاریف طرحواره DTO سه بار تکراری نیاز ندارد، بلکه فقط به نوع TypeScript خالص نیاز دارد. همچنین، چنین بهینه سازی زمان کامپایل از طریق تجزیه و تحلیل کد منبع، “AOT (پیش از زمان)” نامیده می شود.

__param(2, core_1.default.TypedBody({ type: "assert", assert: input => {
        const __is = input => {
            const $is_custom = core_1.default.TypedBody.is_custom;
            const $is_url = core_1.default.TypedBody.is_url;
            const $io0 = input => "string" === typeof input.title && 3 <= input.title.length && 50 >= input.title.length && "string" === typeof input.body && (Array.isArray(input.files) && input.files.every(elem => "object" === typeof elem && null !== elem && $io1(elem)));
            const $io1 = input => (null === input.name || "string" === typeof input.name && 255 >= input.name.length && $is_custom("minLengt", "string", "1", input.name)) && (null === input.extension || "string" === typeof input.extension && 1 <= input.extension.length && 8 >= input.extension.length) && ("string" === typeof input.url && $is_url(input.url));
            return "object" === typeof input && null !== input && $io0(input);
        };
        if (false === __is(input))
            ((input, _path, _exceptionable = true) => {
                const $guard = core_1.default.TypedBody.guard;
                const $is_custom = core_1.default.TypedBody.is_custom;
                const $is_url = core_1.default.TypedBody.is_url;
                const $ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.title && (3 <= input.title.length || $guard(_exceptionable, {
                    path: _path + ".title",
                    expected: "string (@minLength 3)",
                    value: input.title
                })) && (50 >= input.title.length || $guard(_exceptionable, {
                    path: _path + ".title",
                    expected: "string (@maxLength 50)",
                    value: input.title
                })) || $guard(_exceptionable, {
                    path: _path + ".title",
                    expected: "string",
                    value: input.title
                })) && ("string" === typeof input.body || $guard(_exceptionable, {
                    path: _path + ".body",
                    expected: "string",
                    value: input.body
                })) && ((Array.isArray(input.files) || $guard(_exceptionable, {
                    path: _path + ".files",
                    expected: "Array<IAttachmentFile>",
                    value: input.files
                })) && input.files.every((elem, _index1) => ("object" === typeof elem && null !== elem || $guard(_exceptionable, {
                    path: _path + ".files[" + _index1 + "]",
                    expected: "IAttachmentFile",
                    value: elem
                })) && $ao1(elem, _path + ".files[" + _index1 + "]", true && _exceptionable) || $guard(_exceptionable, {
                    path: _path + ".files[" + _index1 + "]",
                    expected: "IAttachmentFile",
                    value: elem
                })) || $guard(_exceptionable, {
                    path: _path + ".files",
                    expected: "Array<IAttachmentFile>",
                    value: input.files
                }));
                const $ao1 = (input, _path, _exceptionable = true) => (null === input.name || "string" === typeof input.name && (255 >= input.name.length || $guard(_exceptionable, {
                    path: _path + ".name",
                    expected: "string (@maxLength 255)",
                    value: input.name
                })) && ($is_custom("minLengt", "string", "1", input.name) || $guard(_exceptionable, {
                    path: _path + ".name",
                    expected: "string (@minLengt 1)",
                    value: input.name
                })) || $guard(_exceptionable, {
                    path: _path + ".name",
                    expected: "(null | string)",
                    value: input.name
                })) && (null === input.extension || "string" === typeof input.extension && (1 <= input.extension.length || $guard(_exceptionable, {
                    path: _path + ".extension",
                    expected: "string (@minLength 1)",
                    value: input.extension
                })) && (8 >= input.extension.length || $guard(_exceptionable, {
                    path: _path + ".extension",
                    expected: "string (@maxLength 8)",
                    value: input.extension
                })) || $guard(_exceptionable, {
                    path: _path + ".extension",
                    expected: "(null | string)",
                    value: input.extension
                })) && ("string" === typeof input.url && ($is_url(input.url) || $guard(_exceptionable, {
                    path: _path + ".url",
                    expected: "string (@format url)",
                    value: input.url
                })) || $guard(_exceptionable, {
                    path: _path + ".url",
                    expected: "string",
                    value: input.url
                }));
                return ("object" === typeof input && null !== input || $guard(true, {
                    path: _path + "",
                    expected: "IBbsArticle.IStore",
                    value: input
                })) && $ao0(input, _path + "", true) || $guard(true, {
                    path: _path + "",
                    expected: "IBbsArticle.IStore",
                    value: input
                });
            })(input, "$input", true);
        return input;
    } })),
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای مرجع، چنین بهینه سازی کامپایل AOT بسیار سبک و سریعتر از منطق های عمومی است که به هر ویژگی از طریق دسترسی دارند. for (const key in obj) عباراتی مانند NestJS سنتی. با معیار اندازه گیری، 25000 برابر سریعتر است.

درباره این راز (static دسترسی در مقابل dynamic دسترسی)، بعداً مقاله دیگری خواهم نوشت. این به بهینه سازی موتور v8 مربوط می شود و مطمئن هستم که از هر داستان دیگری بسیار جالب خواهد بود.

تایید معیار

ژنراتور تصادفی

اگر مقاله من را به دقت خوانده باشید، ممکن است متوجه شوید که چگونه داده های پاسخ را به تنهایی شبیه سازی کنید.

بله، داده های پاسخ نیز توسط مهارت کامپایل AOT تشکیل شده است. بیایید دوباره کد شبیه ساز (SDK) را بخوانیم، سپس می توانید آن را پیدا کنید typia.random<T>() تابع استفاده شود را typia.random<T>() آخرین راز شبیه ساز تولید شده nestia است. داده های پاسخ تصادفی را با تجزیه و تحلیل نوع پاسخ DTO تولید می کند.

/**
 * @packageDocumentation
 * @module api.functional.bbs.articles
 * @nestia Generated by Nestia - https://github.com/samchon/nestia 
 */
//================================================================
import { Fetcher } from "@nestia/fetcher";
import type { IConnection } from "@nestia/fetcher";
import typia from "typia";

import { NestiaSimulator } from "./../../../utils/NestiaSimulator";
import type { IBbsArticle } from "./../../../structures/IBbsArticle";

/**
 * Update an article.
 * 
 * @param section Section code
 * @param id Target article ID
 * @param input Content to update
 * @returns Updated content
 * 
 * @controller BbsArticlesController.update()
 * @path PUT /bbs/:section/articles/:id
 * @nestia Generated by Nestia - https://github.com/samchon/nestia
 */
export async function update(
    connection: IConnection,
    section: string,
    id: string,
    input: update.Input,
): Promise<update.Output> {
    return !!connection.random
        ? update.simulate(
              connection,
              section,
              id,
              input,
          )
        : Fetcher.fetch(
              connection,
              update.ENCRYPTED,
              update.METHOD,
              update.path(section, id),
              input,
          );
}
export namespace update {
    export type Input = IBbsArticle.IStore;
    export type Output = IBbsArticle;

    export const METHOD = "PUT" as const;
    export const PATH: string = "/bbs/:section/articles/:id";
    export const ENCRYPTED: Fetcher.IEncrypted = {
        request: false,
        response: false,
    };

    export const path = (section: string, id: string): string => {
        return `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`;
    }
    export const random = (g?: Partial<typia.IRandomGenerator>): Output =>
        typia.random<Output>(g);
    export const simulate = async (
        connection: IConnection,
        section: string,
        id: string,
        input: update.Input,
    ): Promise<Output> => {
        const assert = NestiaSimulator.assert({
            method: METHOD,
            host: connection.host,
            path: path(section, id)
        });
        assert.param("section")("string")(() => typia.assert(section));
        assert.param("id")("uuid")(() => typia.assert(id));
        assert.body(() => typia.assert(input));
        return typia.random<Output>(
            typeof connection.random === 'object'
            && connection.random !== null
                ? connection.random
                : undefined
        );
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگامی که شما کتابخانه SDK را کامپایل می کنید، چنین اسکریپتی در آن نوشته می شود update.random() تابع، از طریق کامپایل AOT.

پارامتر g?: Partial<typia.IRandomGenerator> یک بذر تصادفی است، اما ضروری نیست.

export.update = update;
(function update() {
    update.random = (g) => (generator => {
        const $generator = typia_1.default.random.generator;
        const $pick = typia_1.default.random.pick;
        const $ro0 = (_recursive = false, _depth = 0) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2; return ({
            id: (_d = (_c = (_b = ((_a = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _a !== void 0 ? _a : $generator.customs)) === null || _b === void 0 ? void 0 : _b.string) === null || _c === void 0 ? void 0 : _c.call(_b, [
                {
                    name: "format",
                    value: "uuid"
                }
            ])) !== null && _d !== void 0 ? _d : ((_e = generator === null || generator === void 0 ? void 0 : generator.uuid) !== null && _e !== void 0 ? _e : $generator.uuid)(),
            section: (_j = (_h = (_g = ((_f = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _f !== void 0 ? _f : $generator.customs)) === null || _g === void 0 ? void 0 : _g.string) === null || _h === void 0 ? void 0 : _h.call(_g, [])) !== null && _j !== void 0 ? _j : ((_k = generator === null || generator === void 0 ? void 0 : generator.string) !== null && _k !== void 0 ? _k : $generator.string)(),
            created_at: (_p = (_o = (_m = ((_l = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _l !== void 0 ? _l : $generator.customs)) === null || _m === void 0 ? void 0 : _m.string) === null || _o === void 0 ? void 0 : _o.call(_m, [
                {
                    name: "format",
                    value: "date-time"
                }
            ])) !== null && _p !== void 0 ? _p : ((_q = generator === null || generator === void 0 ? void 0 : generator.datetime) !== null && _q !== void 0 ? _q : $generator.datetime)(),
            title: (_u = (_t = (_s = ((_r = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _r !== void 0 ? _r : $generator.customs)) === null || _s === void 0 ? void 0 : _s.string) === null || _t === void 0 ? void 0 : _t.call(_s, [
                {
                    name: "minLength",
                    value: "3"
                },
                {
                    name: "maxLength",
                    value: "50"
                }
            ])) !== null && _u !== void 0 ? _u : ((_v = generator === null || generator === void 0 ? void 0 : generator.string) !== null && _v !== void 0 ? _v : $generator.string)(((_w = generator === null || generator === void 0 ? void 0 : generator.integer) !== null && _w !== void 0 ? _w : $generator.integer)(3, 50)),
            body: (_0 = (_z = (_y = ((_x = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _x !== void 0 ? _x : $generator.customs)) === null || _y === void 0 ? void 0 : _y.string) === null || _z === void 0 ? void 0 : _z.call(_y, [])) !== null && _0 !== void 0 ? _0 : ((_1 = generator === null || generator === void 0 ? void 0 : generator.string) !== null && _1 !== void 0 ? _1 : $generator.string)(),
            files: ((_2 = generator === null || generator === void 0 ? void 0 : generator.array) !== null && _2 !== void 0 ? _2 : $generator.array)(() => $ro1(_recursive, _recursive ? 1 + _depth : _depth))
        }); };
        const $ro1 = (_recursive = false, _depth = 0) => { var _a, _b, _c, _d, _e; return ({
            name: $pick([
                () => null,
                () => { var _a, _b, _c, _d, _e, _f; return (_d = (_c = (_b = ((_a = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _a !== void 0 ? _a : $generator.customs)) === null || _b === void 0 ? void 0 : _b.string) === null || _c === void 0 ? void 0 : _c.call(_b, [
                    {
                        name: "minLengt",
                        value: "1"
                    },
                    {
                        name: "maxLength",
                        value: "255"
                    }
                ])) !== null && _d !== void 0 ? _d : ((_e = generator === null || generator === void 0 ? void 0 : generator.string) !== null && _e !== void 0 ? _e : $generator.string)(((_f = generator === null || generator === void 0 ? void 0 : generator.integer) !== null && _f !== void 0 ? _f : $generator.integer)(5, 255)); }
            ])(),
            extension: $pick([
                () => null,
                () => { var _a, _b, _c, _d, _e, _f; return (_d = (_c = (_b = ((_a = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _a !== void 0 ? _a : $generator.customs)) === null || _b === void 0 ? void 0 : _b.string) === null || _c === void 0 ? void 0 : _c.call(_b, [
                    {
                        name: "minLength",
                        value: "1"
                    },
                    {
                        name: "maxLength",
                        value: "8"
                    }
                ])) !== null && _d !== void 0 ? _d : ((_e = generator === null || generator === void 0 ? void 0 : generator.string) !== null && _e !== void 0 ? _e : $generator.string)(((_f = generator === null || generator === void 0 ? void 0 : generator.integer) !== null && _f !== void 0 ? _f : $generator.integer)(1, 8)); }
            ])(),
            url: (_d = (_c = (_b = ((_a = generator === null || generator === void 0 ? void 0 : generator.customs) !== null && _a !== void 0 ? _a : $generator.customs)) === null || _b === void 0 ? void 0 : _b.string) === null || _c === void 0 ? void 0 : _c.call(_b, [
                {
                    name: "format",
                    value: "url"
                }
            ])) !== null && _d !== void 0 ? _d : ((_e = generator === null || generator === void 0 ? void 0 : generator.url) !== null && _e !== void 0 ? _e : $generator.url)()
        }); };
        return $ro0();
    })(g);
})((update = exports.update) || (exports.update = {}));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بسته شدن

در مقاله فعلی، شبیه ساز سرور باطن NestJS را از طریق کتابخانه SDK معرفی کرده ام. با این حال، همانطور که می‌دانید، شبیه‌سازی سرور بک‌اند سطح frontend تنها زمانی امکان‌پذیر است که سرور بک‌اند با NestJS (+nestia) توسعه داده شود.

به هر حال، من در حال توسعه یک کتابخانه جدید هستم @nestia/migrate، که می تواند تبدیل شود swagger.json فایل به پروژه NestJS. هنوز در حال توسعه است، اما بیشتر آنها به هدف رسیده اند. بعد از @nestia/migrate کتابخانه منتشر می شود، می توانید هر سرور باطنی را شبیه سازی کنید، حتی آن که با NestJS توسعه نیافته است.

منتظر آن باشید، توسعه اپلیکیشن بسیار ساده تر خواهد بود.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا