question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No routes generated when following the Getting started guide

See original GitHub issue

Sorting

  • I’m submitting a …

    • bug report
    • feature request
    • support request
  • I confirm that I

    • used the search to make sure that a similar issue hasn’t already been submit

Expected Behavior

The routes.ts file is produced and contains models and routes based on the provided controllers.

\\ package.json
{
  "name": "tsoa-hello-world",
  "main": "build/src/server.js",
  "scripts": {
    "build": "tsoa spec-and-routes && tsc",
    "start": "node build/src/server.js"
  },
  "dependencies": {
    "body-parser": "^1.20.0",
    "express": "^4.18.1",
    "tsoa": "^4.1.1"
  },
  "devDependencies": {
    "@types/body-parser": "^1.19.2",
    "@types/express": "^4.17.13",
    "@types/node": "^18.7.13",
    "typescript": "^4.7.4"
  }
}
// tsoa.json
{
  "entryFile": "src/server.ts",
  "noImplicitAdditionalProperties": "throw-on-extras",
  "controllerPathGlobs": ["src/**/*Controller.ts"],
  "spec": {
    "outputDirectory": "build",
    "specVersion": 3
  },
  "routes": {
    "basePath": "/api/v1",
    "routesDir": "src",
    "middleware": "express"
  }
}
\\ healthController.ts
import { Get, Route, Controller } from "tsoa";
import { Health } from "./health";
import { HealthService } from "./healthService";

@Route("health")
export class HealthController extends Controller {
  @Get()
  public async getHealth(): Promise<Health> {
    return new HealthService().get();
  }
}

A complete sample of a simplified implementation of the tsoa’s Getting Started Guide can be found here.

Current Behavior

Running yarn tsoa routes produces a routes.ts file, but it doesn’t contain any of the information from the controllers or any route.

// routes.ts
import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, HttpStatusCodeLiteral, TsoaResponse, fetchMiddlewares } from '@tsoa/runtime';
import type { RequestHandler } from 'express';
import * as express from 'express';

const models: TsoaRoute.Models = {
};
const validationService = new ValidationService(models);

export function RegisterRoutes(app: express.Router) {
    function isController(object: any): object is Controller {
        return 'getHeaders' in object && 'getStatus' in object && 'setStatus' in object;
    }

    function promiseHandler(controllerObj: any, promise: any, response: any, successStatus: any, next: any) {
        return Promise.resolve(promise)
            .then((data: any) => {
                let statusCode = successStatus;
                let headers;
                if (isController(controllerObj)) {
                    headers = controllerObj.getHeaders();
                    statusCode = controllerObj.getStatus() || statusCode;
                }

                

                returnHandler(response, statusCode, data, headers)
            })
            .catch((error: any) => next(error));
    }

    

    function returnHandler(response: any, statusCode?: number, data?: any, headers: any = {}) {
        if (response.headersSent) {
            return;
        }
        Object.keys(headers).forEach((name: string) => {
            response.set(name, headers[name]);
        });
        if (data && typeof data.pipe === 'function' && data.readable && typeof data._read === 'function') {
            data.pipe(response);
        } else if (data !== null && data !== undefined) {
            response.status(statusCode || 200).json(data);
        } else {
            response.status(statusCode || 204).end();
        }
    }

    function responder(response: any): TsoaResponse<HttpStatusCodeLiteral, unknown>  {
        return function(status, data, headers) {
            returnHandler(response, status, data, headers);
        };
    };

    function getValidatedArgs(args: any, request: any, response: any): any[] {
        const fieldErrors: FieldErrors  = {};
        const values = Object.keys(args).map((key) => {
            const name = args[key].name;
            switch (args[key].in) {
                case 'request':
                    return request;
                case 'query':
                    return validationService.ValidateParam(args[key], request.query[name], name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                case 'path':
                    return validationService.ValidateParam(args[key], request.params[name], name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                case 'header':
                    return validationService.ValidateParam(args[key], request.header(name), name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                case 'body':
                    return validationService.ValidateParam(args[key], request.body, name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                case 'body-prop':
                    return validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, 'body.', {"noImplicitAdditionalProperties":"throw-on-extras"});
                case 'formData':
                    if (args[key].dataType === 'file') {
                        return validationService.ValidateParam(args[key], request.file, name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                    } else if (args[key].dataType === 'array' && args[key].array.dataType === 'file') {
                        return validationService.ValidateParam(args[key], request.files, name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                    } else {
                        return validationService.ValidateParam(args[key], request.body[name], name, fieldErrors, undefined, {"noImplicitAdditionalProperties":"throw-on-extras"});
                    }
                case 'res':
                    return responder(response);
            }
        });

        if (Object.keys(fieldErrors).length > 0) {
            throw new ValidateError(fieldErrors, '');
        }
        return values;
    }
}

Possible Solution

Steps to Reproduce

  1. git clone https://github.com/amda-phd/tsoa-hello-world
  2. yarn install
  3. yarn tsoa routes

Live version here.

Context (Environment)

Version of the library: 4.1.1 Version of NodeJS: 16.15.0 (but also tested with 12.22.12 with the same result) Version of Typescript: 4.7.4 due to this issue

  • Confirm you were using yarn not npm: [x]

Detailed Description

The behavior is exactly as it was reported in this previously opened issue, but no ignore patterns are being provided in this case. As mentioned in the previous issue, multiple glob patterns have been attempted, including the specific path to the controller and importing the controller on the app.ts file. All with the same result. When deliberately specifying an incorrect glob pattern, an error is returned, exactly as expected.

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:17

github_iconTop GitHub Comments

1reaction
WoHcommented, Aug 30, 2022

@vekexasia should be g2g now, I tested vs. the sample repo provided by @amda-phd.

yarn install && yarn add typescript tsoa@next && yarn routes

now gives me a model, a controller and fetchMiddleware without type roblems. Still expect some bumps with TS 4.8 though.

tsoa-hello-world on  main [!] is 📦 v1.0.0 via  v16.13.0 on ️🇬️  
❯ curl localhost:3000/api/v1/health
{"healthy":true,"hello":"world"}%                                                    
1reaction
amda-phdcommented, Aug 30, 2022

Changing the typescript version for all the project via resolutions has finally produced a valid routes.ts for me:

"resolutions": {
    "typescript": "4.7.4"
  }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Defining Your Routes - Routing - Ember Guides
When your application starts, the router matches the current URL to the routes that you've defined. The routes, in turn, are responsible for...
Read more >
Rails Routing from the Outside In
Rails Routing from the Outside InThis guide covers the user-facing features ... The articles resource here will have the following routes generated for...
Read more >
Configure route tables - Amazon Virtual Private Cloud
Main route table—The route table that automatically comes with your VPC. It controls the routing for all subnets that are not explicitly associated...
Read more >
Express Tutorial Part 4: Routes and controllers - MDN Web Docs
First we create routes for a wiki in a module named wiki.​​ The code first imports the Express application object, uses it to...
Read more >
Add navigation with routing
This section explains the following: Get the route that created it; Extract the id from the route; Get the hero with that id...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found