Home > database >  TypeError - Cannot read properties of undefined (reading 'name')
TypeError - Cannot read properties of undefined (reading 'name')

Time:07-28

Basically I'm running into this error when trying to use UseCase class on Deno.

[uncaught application error]: TypeError - Cannot read properties of undefined (reading 'getAppInfosUseCase')

request: { url: "http://127.0.0.1:3030/", method: "GET", hasBody: false }
response: { status: 200, type: undefined, hasBody: false, writable: true }

    at handle (file:///C:/Users/joaop/Documents/dev/web/back-end/deno/shynotes-api (MVC)/src/useCases/GetAppInfos/GetAppInfosController.ts:11:39)
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at https://deno.land/x/[email protected]/router.ts:1148:20
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at composedMiddleware (https://deno.land/x/[email protected]/middleware.ts:44:12)
    at dispatch (https://deno.land/x/[email protected]/router.ts:1154:28)
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at composedMiddleware (https://deno.land/x/[email protected]/middleware.ts:44:12)
    at Application.#handleRequest (https://deno.land/x/[email protected]/application.ts:389:34)
    at Application.listen (https://deno.land/x/[email protected]/application.ts:559:28)

App Structure

project-structure

src/

app.ts

import { Application } from 'https://deno.land/x/[email protected]/mod.ts';
import { router } from './routes.ts';
import 'https://deno.land/x/[email protected]/load.ts';

const app = new Application();

app.use(router.routes());
app.use(router.allowedMethods());

export { app };

routes.ts

import { Router } from 'https://deno.land/x/[email protected]/router.ts';
import { getAppInfosController } from './useCases/GetAppInfos/index.ts';

const router = new Router();

router.get('/', getAppInfosController.handle);

export { router };

server.ts

import { app } from './app.ts';

await app.listen({
    port: 3030,
});

src/useCases/

GetAppInfosController.ts

import { Context } from 'https://deno.land/x/[email protected]/context.ts';
import { GetAppInfosUseCase } from './GetAppInfosUseCase.ts';

export class GetAppInfosController {
    constructor(
        private getAppInfosUseCase: GetAppInfosUseCase,
    ) {}

    handle(context: Context) {
        context.response.status = 200;
        return context.response.body = this.getAppInfosUseCase.execute();
    }
}

GetAppInfosUseCase.ts

export class GetAppInfosUseCase {
    execute() {
        return {
            author: 'Jphn',
            github: 'github.com/Jphn',
        };
    }
}

index.ts

import { GetAppInfosUseCase } from './GetAppInfosUseCase.ts';
import { GetAppInfosController } from './GetAppInfosController.ts';

const getAppInfosUseCase = new GetAppInfosUseCase();

const getAppInfosController = new GetAppInfosController(getAppInfosUseCase);

export { getAppInfosController, getAppInfosUseCase };

CodePudding user response:

server.ts

import { Router } from 'https://deno.land/x/[email protected]/router.ts';
import { getAppInfosController } from './useCases/GetAppInfos/index.ts';

const router = new Router();

// router.get('/', getAppInfosController.handle);
router.get('/', (context) => {
  getAppInfosController.handle(context);
});

export { router };

CodePudding user response:

This question seems like you are in need of help debugging. I copied the data in your question to files in a directory on my file system to reproduce. Let's start with some basics:

The version of Deno I'm using:

% deno --version
deno 1.24.0 (release, x86_64-apple-darwin)
v8 10.4.132.20
typescript 4.7.4

Type-checking your code:

% deno check src/server.ts 
Download https://deno.land/x/[email protected]/load.ts
Download https://deno.land/x/[email protected]/mod.ts
Download https://deno.land/x/[email protected]/util.ts
error: Module not found "file:///Users/deno/so-73142682/src/useCases/GetAppInfos/index.ts".
    at file:///Users/deno/so-73142682/src/routes.ts:2:39

The first problem revealed is that there's an invalid module specifier in ./src/routes.ts:

import { Router } from 'https://deno.land/x/[email protected]/router.ts';
import { getAppInfosController } from './useCases/GetAppInfos/index.ts'; /*
                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unable to load a local module: "file:///Users/deno/so-73142682/src/useCases/GetAppInfos/index.ts".
  Please check the file path. deno(no-local) */

const router = new Router();

router.get('/', getAppInfosController.handle);

export { router };

The specifier just needs to be changed to "./useCases/index.ts". After making that change, let's type-check again:

% deno check src/server.ts
Check file:///Users/deno/so-73142682/src/server.ts

Now type-checking passes: great. Let's try running the module:

% deno run --allow-read=".env,.env.defaults" --allow-net="0.0.0.0:3030" src/server.ts

The process doesn't terminate, so something's happening. Let's try making a GET request to the host. In a separate terminal shell:

% deno eval --print "(await fetch('http://localhost:3030')).status"
500

The status of the request is 500. Let's switch back to the shell where the app is running and check if there's any output. There is:

[uncaught application error]: TypeError - Cannot read properties of undefined (reading 'getAppInfosUseCase')

request: { url: "http://localhost:3030/", method: "GET", hasBody: false }
response: { status: 200, type: undefined, hasBody: false, writable: true }

    at handle (file:///Users/deno/so-73142682/src/useCases/GetAppInfosController.ts:11:45)
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at https://deno.land/x/[email protected]/router.ts:1148:20
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at composedMiddleware (https://deno.land/x/[email protected]/middleware.ts:44:12)
    at dispatch (https://deno.land/x/[email protected]/router.ts:1154:28)
    at dispatch (https://deno.land/x/[email protected]/middleware.ts:41:13)
    at composedMiddleware (https://deno.land/x/[email protected]/middleware.ts:44:12)
    at Application.#handleRequest (https://deno.land/x/[email protected]/application.ts:389:34)
    at Application.listen (https://deno.land/x/[email protected]/application.ts:559:28)

Looking at the beginning of the stack trace shows that the error originated at this location:

at handle (file:///Users/deno/so-73142682/src/useCases/GetAppInfosController.ts:11:45)

The 11:45 means line 11, column 45. Let's check that position in the source module ./src/useCases/GetAppInfosController.ts:

import { Context } from 'https://deno.land/x/[email protected]/context.ts';
import { GetAppInfosUseCase } from './GetAppInfosUseCase.ts';

export class GetAppInfosController {
    constructor(
        private getAppInfosUseCase: GetAppInfosUseCase,
    ) {}

    handle(context: Context) {
        context.response.status = 200;
        return context.response.body = this.getAppInfosUseCase.execute(); /*
                                            ~~~~~~~~~~~~~~~~~~
                                      Here's where the error occurred.
                                  `this` was `undefined` at the call site */
    }
}

The stack doesn't make this obvious, but the actual problem is where you use the handle method in ./src/routes.ts:

import { Router } from 'https://deno.land/x/[email protected]/router.ts';
import { getAppInfosController } from './useCases/index.ts';

const router = new Router();

router.get('/', getAppInfosController.handle); /*
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                This is an unbound method call */

export { router };

Method binding has already been covered extensively on this site in other questions, so I won't reiterate the details here. You just need to bind the class instance object to the method so that when it's invoked as a function, the this value refers to the class instance object:

router.get('/', getAppInfosController.handle.bind(getAppInfosController));
//                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

After saving those changes, let's stop the app using ctrl c in the terminal and start it again:

^C

% deno run --allow-read=".env,.env.defaults" --allow-net="0.0.0.0:3030" src/server.ts

Now let's make another GET request to the host in a separate terminal shell, but this time we'll print the body as parsed JSON (because that's the expected response format):

% deno eval --print "await (await fetch('http://localhost:3030')).json()"
{ author: "Jphn", github: "github.com/Jphn" }

Now it looks like what's expected. Issue solved.

  • Related