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%20(MVC)/src/useCases/GetAppInfos/GetAppInfosController.ts:11:39)
at dispatch (https://deno.land/x/oak@v10.6.0/middleware.ts:41:13)
at https://deno.land/x/oak@v10.6.0/router.ts:1148:20
at dispatch (https://deno.land/x/oak@v10.6.0/middleware.ts:41:13)
at composedMiddleware (https://deno.land/x/oak@v10.6.0/middleware.ts:44:12)
at dispatch (https://deno.land/x/oak@v10.6.0/router.ts:1154:28)
at dispatch (https://deno.land/x/oak@v10.6.0/middleware.ts:41:13)
at composedMiddleware (https://deno.land/x/oak@v10.6.0/middleware.ts:44:12)
at Application.#handleRequest (https://deno.land/x/oak@v10.6.0/application.ts:389:34)
at Application.listen (https://deno.land/x/oak@v10.6.0/application.ts:559:28)
src/
import { Application } from 'https://deno.land/x/oak@v10.6.0/mod.ts';
import { router } from './routes.ts';
import 'https://deno.land/x/dotenv@v3.2.0/load.ts';
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
export { app };
import { Router } from 'https://deno.land/x/oak@v10.6.0/router.ts';
import { getAppInfosController } from './useCases/GetAppInfos/index.ts';
const router = new Router();
router.get('/', getAppInfosController.handle);
export { router };
import { app } from './app.ts';
await app.listen({
port: 3030,
});
src/useCases/
import { Context } from 'https://deno.land/x/oak@v10.6.0/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();
}
}
export class GetAppInfosUseCase {
execute() {
return {
author: 'Jphn',
github: 'github.com/Jphn',
};
}
}
import { GetAppInfosUseCase } from './GetAppInfosUseCase.ts';
import { GetAppInfosController } from './GetAppInfosController.ts';
const getAppInfosUseCase = new GetAppInfosUseCase();
const getAppInfosController = new GetAppInfosController(getAppInfosUseCase);
export { getAppInfosController, getAppInfosUseCase };
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/dotenv@v3.2.0/load.ts
Download https://deno.land/x/dotenv@v3.2.0/mod.ts
Download https://deno.land/x/dotenv@v3.2.0/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/oak@v10.6.0/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/oak@v10.6.0/middleware.ts:41:13)
at https://deno.land/x/oak@v10.6.0/router.ts:1148:20
at dispatch (https://deno.land/x/oak@v10.6.0/middleware.ts:41:13)
at composedMiddleware (https://deno.land/x/oak@v10.6.0/middleware.ts:44:12)
at dispatch (https://deno.land/x/oak@v10.6.0/router.ts:1154:28)
at dispatch (https://deno.land/x/oak@v10.6.0/middleware.ts:41:13)
at composedMiddleware (https://deno.land/x/oak@v10.6.0/middleware.ts:44:12)
at Application.#handleRequest (https://deno.land/x/oak@v10.6.0/application.ts:389:34)
at Application.listen (https://deno.land/x/oak@v10.6.0/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/oak@v10.6.0/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/oak@v10.6.0/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.