lukeautry / tsoa

Build OpenAPI-compliant REST APIs using TypeScript and Node
MIT License
3.32k stars 481 forks source link

programmatic way to generate the spec and routes #1604

Closed kamit-transient closed 1 month ago

kamit-transient commented 3 months ago

Hi,

I am trying to use the programmatic way to generate the spec and routes as I need to use env variables.

the approach mentioned in docs here is not very clear.

Would you please suggest how can generate the spec and route programmatically?

NOTE: I tried programmatically(using ts-node tsoa.ts) I always get the no controllers found issues however the same configurations work tsoa.json approach.

github-actions[bot] commented 3 months ago

Hello there kamit-transient 👋

Thank you for opening your very first issue in this project.

We will try to get back to you as soon as we can.👀

poph2 commented 3 months ago

The routeOptions in the docs did not specify controllerPathGlobs. Try this:

const routeOptions: ExtendedRoutesConfig = {
        bodyCoercion: false, 
        noImplicitAdditionalProperties: "throw-on-extras",
        basePath: "/api",
        entryFile: "./api/server.ts",
        routesDir: "./api",
        controllerPathGlobs: ["./routeControllers/**/*Controller.ts"],
    };
kamit-transient commented 3 months ago

@poph2 whatever controller glob pattern I provide, it always says zero controller found.

However, exactly the same glob pattern works with the tsoa.json config file but I can not use this JSON file approach as i need to use env variables.

luke-lacroix-healthy commented 3 months ago

In my code, I use this:

await generateRoutes({
      authenticationModule: path.join(args.routesDir, 'authentication'),
      basePath: args.basePath,
      bodyCoercion: true,
      controllerPathGlobs: ['src/interfaces/http/**/*.controller.{ts,js}'],
      entryFile: args.entryFile,
      iocModule: path.join(args.routesDir, 'ioc'),
      noImplicitAdditionalProperties: args.noImplicitAdditionalProperties,
      routesDir: args.routesDir,
      rootSecurity: args.rootSecurity,
    });

The glob is relative to the basePath directory.

kamit-transient commented 3 months ago

@luke-lacroix-healthy would love to see the complete example of the above tsoa config .ts file.

Would you please share your tsoa config?

luke-lacroix-healthy commented 3 months ago

@kamit-transient I don't know what you mean. There is no config file. The configuration is passed to the generateRoutes function and aside from the data, which is provided on the command line for me, that's the complete example.

kamit-transient commented 3 months ago

then how do you run this?

i use ts-node to run my tsoa.ts file. am I missing something ?

would you please share the command for example?

kamit-transient commented 3 months ago

here is how i am doing:

i have file named tsoa.ts with below content and then i use ts-node tsoa.ts command to run it. but it fails for me. is it the right way?

const {
    generateRoutes,
    generateSpec,
    ExtendedRoutesConfig,
    ExtendedSpecConfig,
} = require("tsoa");

// import {
//     generateRoutes,
//     generateSpec,
//     ExtendedRoutesConfig,
//     ExtendedSpecConfig,
// } from "tsoa";

(async () => {
    const specOptions = {
        "entryFile": "apps/project/src/app/app.ts",
        specVersion: 3,
        "outputDirectory": "./apps/project/generated/spec",
        "controllerPathGlobs": [
            "./apps/project/src/controllers/**/*controller.ts"
        ],
        "host": "http://localhost:80/api/docs",
        noImplicitAdditionalProperties: "throw-on-extras",

    };

    const routeOptions = {
        "entryFile": "apps/project/src/app/app.ts",
        "routesDir": "apps/project/generated/routes",
        noImplicitAdditionalProperties: "throw-on-extras",

    };

    await generateSpec(specOptions, {
        "paths": {
            "@sharedtypes": [
                "apps/sharedtypes/index.ts"
            ]
        }
    });

    await generateRoutes(routeOptions, {
        "paths": {
            "@sharedtypes": [
                "apps/sharedtypes/index.ts"
            ]
        }
    });
})();
luke-lacroix-healthy commented 3 months ago

@kamit-transient My example shows passing the controllerPathGlobs to the generateRoutes function. You're not telling generateRoutes where to find your controllers so that it can output the code necessary to load them at run-time.

kamit-transient commented 3 months ago

do you use tsoa command to run your generate route function ? @luke-lacroix-healthy

luke-lacroix-healthy commented 3 months ago

No. If you're using the TSOA command line, then you need to export a configuration object from your tsoa.js or tsoa.ts file, like this:

/** @type {import('tsoa').Config} */
module.exports = {
  entryFile: 'src/index.ts',
  noImplicitAdditionalProperties: 'throw-on-extras',
  controllerPathGlobs: ['src/routes/*.controller.ts'],
  spec: {
    outputDirectory: 'src/generated',
    specVersion: 3,
    specFileBaseName: 'tsoa-openapi',
    contact: {
      name: 'Healthy.io',
    },
    securityDefinitions: {
      umAuth: {
        description: 'Authenticate via User Management',
        scheme: 'Bearer',
        type: 'http',
      },
    },
    spec: {
      servers: [
        { url: 'http://localhost:7323', description: 'Local Environment' },
      ],
    },
  },

  routes: {
    routesDir: 'src/generated',
    authenticationModule: 'src/expressAuthentication.ts',
  },
};

You only call generateRoutes and generateSpecs if you're trying to write your own tooling and command lines.

kamit-transient commented 3 months ago

is there any minimal example express.js repo, which uses this programmatic approach?

That would be helpful if any.

github-actions[bot] commented 1 month ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days