How can I use axios or any other http request client package in my Adonisjs Controller - node.js

I want to call to a external API to get some data while I'm inside the controller.
Let's say, There is two different project, one is hosted in example.com and the other one is in a.example.com.
Now, when I am on a.example.com sub-domain, I want the user data who has logged in example.com in that sub-domain.
For testing purpose, I set an API url for the in example.com which will return the expected json data.
My Problem is, I want to call axios to that API from the a.example.com domain. But I can not require the axios package inside my controller.
Here is my controller of a.example.com:
'use strict'
const axios = require('axios')
class UserController {
async getAllUser({ request, auth, response, axios }) {
await axios('http://a.example.com/getUser')
.then(response => {
return response;
});
}
}
export default UserController
But I get this error :
'import' and 'export' may appear only with 'sourceType: module' export default UserController
I don't know how to solve this, can anyone help me fix this?

In Adonis, you use "use" instead of require.
const Axios = use('Axios');

Instead of
export default UserController
Use
module.exports = UserController

Related

Problem with instance of class while creating reusable controller in node.js

I want to reuse all my servise and controllers and I was achieving this with extending class.
I'm calling the class of Controller and service all the way up from a router.
everything looks fine while creating instance of class,
but when I send request to sign in route it says there is no this.servise or this.dto which is i passed to the constructor while creating this instance of object
import express from "express";
const router = express.Router();
import UserController from "../../controllers/user.controller";
import UserService from "../../services/user.service.js";
import {UserDto} from "../../dtos/user.dto.js";
import User from "../../models/User.js";
const userService = new UserService(User);
console.log(userService, UserDto); // successfully logged
const userController = new UserController(userService, UserDto);
console.log(userController); // successfully logged
router.post('/signup', userController.signup);
router.post('/signin', userController.signIn);
router.post('/signout', userController.signOut);
router.get('/all', userController.getAll);
router.route("/:id")
.get(userController.get)
.post(userController.create)
.patch(userController.update)
.delete(userController.delete);
export default router;
export default class UserController extends MainController {
async signIn(req, res) {
try {
const {email, password} = req.body;//await this.dto.login(req.body);
const result = await this.service.signIn(email, password); // TypeError: Cannot read properties of undefined (reading 'service')
return res.status(201).json({message: "successfully signed in", token: result.token, user: this.dto.output(result.user)});
}
catch(err){
sendError(res, err);
}
}
I've reviewed my knowledge of how nodejs modules work, but I can't figure out what the problem is.
Who can explain me this situation, please!?
Thank you in advance!
You forgot to use .bind() when passing the references to the methods to the Express router. So who knows what the value of this will be when Express tries to call them (in JS, this doesn't work like other variable names, it doesn't use lexical scoping, what the value will be depends on the calling code).
Change your code to e.g.
router.post('/signup', userController.signup.bind(userController));
This will ensure that this is set to your userController object when the function gets called (bind() is a method available on all function objects that returns a new function with a fixed this value).

Use TypeORM repository or method on a service from outside any module

I have a nestjs app that has an AuthService which has these parts:
export class AuthService {
constructor(
#InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async updateFromInternally () {
...
}
I have another file which is, crucially, outside of any module, which contains a number of helpful functions relating to Google oauth. For example, this file initiates Google's oauth2 client like so:
export const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_CLIENT_REDIRECT
);
This file also has a listener function which I found in Google's documentation as a way to catch when my use of Google's oauth2 client automatically uses a refresh token to obtain a new access token:
oauth2Client.on('tokens', async (tokens) => {
[****]
})
At [****], I need to query in my database for a particular user and update them. Either of these conceptually work:
I somehow get userRepo into this file right here and use it to query + update
I somehow call updateFromInternally in AuthService from here
But I don't know how to interact with either TypeORM repositories or methods within services from outside of any module in nestjs! Can I do either of these?
The first question is why you're using nestjs?
You can access the internals of nestjs by using the instantiated app;
In your main.ts file you have something like this:
const app = await NestFactory.create(AppModule);
You can access the services or DataSource from the app by using the get method.
import {DataSource} from 'typeorm'
const dataSource = app.get<DataSource>(DataSource)
// or custom service
const someServices = app.get<SomeService>(SomeService)
you just need a way to export app from main.ts and import it in your outside world.
for example:
// main.ts
let app;
async bootstrap() {
app = await NestFactory.create(AppModule);
}
export const getApp = () => app;
bootstrap()
and in your other file
// outside.ts
import {getApp} from './main.ts'
const app = getApp()
I didn't write the whole logic, but I think it would give you an idea of what you need to do.
But in my opinion, it's the worst thing you can do. You're using 'nestjs` and try to respect its philosophy.
Just write a module that handles all the work you want.
I figured out #1:
import {getRepository} from "typeorm";
oauth2Client.on('tokens', async (tokens) => {
const gaRepo = getRepository(Googleauth);
// Can now use gaRepo like you do in a service
})

Override NestJS route within controller

I want to completely override a controller route. E. g:
#Controller('shipments')
export class ShipmentsController {
#Post('/create')
async find(): Promise<Activities> {
return service.find()
}
}
In order to make a request to the previous example, The URL will be: http://localhost:8080/shipments/create
I want to change that URL without moving the controller to another class. For example, I want the URL for that specific function to be http://localhost:8080/whatever/i/want.
Is this possible?
This is not possible, and goes against the ideas of the framework of having easy to configure routes with structure and uniformity. If you want a route like that, you can use express on it;s own, or technically add the route in the bootstrap file like so
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.getHttpServer().get('/whatever/you/want', (req, res, next) => {});
await app.listen(3000);
}
But now you don't have (easy) access to services, testing this is a pain, and generally it's confusing, not to mention no use of any Nest enhancers like interceptors or pipes.

Access model method inside express route (Loopback 4)

I will show you an example of what i'm trying to do :
server.ts
export class ExpressServer {
public readonly app: express.Application;
public readonly lbApp: ImportedApp;
private server?: Server;
constructor(options: ApplicationConfig = {}) {
this.app = express();
this.lbApp = new ImportedApp(options);
this.app.get('/hello', async function (_req: Request, res: Response) {
//Here i'd like to call a model like User.findById() but can't figure out how to do it..
});
}
}
As you see in the comment i'm trying to access my models method to use them in my route (Like showing users informations on my view) But can't figure out how to do it. I'v already tryed to import the DataSource, the model, the controller but nothing's containing my methods (FindById, Create etc..)
If i find nothing i will have to use something like Axios or Request to request the ressource from the api instead of inside my code like await request('api/users/myusername)
In LoopBack 4, we use Repository design patter for accessing data. In order to find a user instance by its id, you need to obtain an instance of UserRepository via dependency injection. Quoting from https://loopback.io/doc/en/lb4/Repository.html:
Repositories are adding behavior to Models. Models describe the shape of data, Repositories provide behavior like CRUD operations. This is different from LoopBack 3.x where models implement behavior too.
UPDATED SOLUTION
To obtain an instance of a Repository class, you can use the Service Locator design pattern and get the instance from the per-request Context object provided by LoopBack's REST layer.
import {MIDDLEWARE_CONTEXT, RequestContext} from '#loopback/rest';
import {UserRepository} from '../repositories';
function expressHandler(req, res, next) {
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
const userRepo = await ctx.get<UserRepository>('repositories.UserRepository');
const users = await userRepo.find({limit: 10});
// render your view
}
We are discussing how to make this use case easier to implement in GitHub pull request loopback-next#6793, feel free to join the discussion there.
ORIGINAL ANSWER
Instead of writing an Express route for your rendered pages, I recommend you to write a LoopBack 4 Controller instead; and inject Express Response object to allow you to render the HTML view, as explained in https://loopback.io/doc/en/lb4/Accessing-http-request-response.html#inject-http-response
import {Response, RestBindings, oas} from '#loopback/rest';
import {inject} from '#loopback/core';
import {UserRepository} from '../repositories';
export class PingController {
constructor(
#inject(RestBindings.Http.RESPONSE)
private response: Response
#repository(UserRepository)
public userRepository: UserRepository,
) {}
// Hide this endpoint from OpenAPI spec generated for the app
#oas.visibility('undocumented')
#get('/users')
list(): Response {
// Access User data via this.userRepository API
const users = await this.userRepository.find({limit: 10});
// Access the response object via `this.response`
this.response.render('users', {users});
// Return the HTTP response object so that LoopBack framework skips the
// generation of HTTP response
return this.response;
}
}
Having said that, if you already know how to access DataSource instances from your LB4 app in your Express routes, then you can instantiate Repository classes manually from your routes too:
const db = // your datasource
this.app.get('/hello', async function (_req: Request, res: Response) {
const repo = new UserRepository(db);
const users = await this.userRepository.find({limit: 10});
});
To me the solution is not working. Started from the express-composition example, i just need to access lb repositories from a generic express route outside of the lb4 request handler:
constructor(options: ApplicationConfig = {}) {
this.app = express();
this.lbApp = new NoteApplication(options);
this.lbApp.basePath('')
// Expose the front-end assets via Express, not as LB4 route
this.app.use('/api', this.lbApp.requestHandler);
this.app.get('/hello', async (req: Request, res: Response) => {
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
const userRepo = await ctx.get('repositories.UserRepository');
res.send('Hello world!');
});
}
the ctx in the line
const ctx = (req as any)[MIDDLEWARE_CONTEXT];
is always undefined.
My main goal is to have routes not under /api that can still access lb4 repositories.

Are controllers inside the application layer or infrastructure layer? Should I even use controllers in Clean Architecture?

As far as I can understand, Clean Architecture/DDD states that your use cases can be triggered by anything, let it be a HTTP request or GUI, correct?
I am trying to emulate that, but I am not really sure if I am doing it correctly.
Inside my infrastructure folder, I have routers. For example:
import express from 'express'
import UserController from '../controllers/user_controller.js'
import ExpressRouterAdapter from './ExpressRouterAdapter.js'
export default function UsersRouter () {
const router = express.Router()
router.route('/:username').get(ExpressRouterAdapter.adapt(UserController.getUser))
return router
}
(ExpressRouterAdapter is just an adapter that transforms Express requests into a simple httpRequest JS object)
And here is my GetUser controller:
export class GetUser {
constructor ({ FindUserService }) {
this.findUser = FindUserService
}
async handle (httpRequest = {}) {
try {
const { username } = httpRequest.params
if (!username) {
return {
statusCode: 400,
body: 'Missing username parameter.'
}
}
const user = await this.findUser.execute(username)
// ...
I have a few questions:
Should I even have controllers? Should the Router direct it to the use-case/service directly?
^^ The reason I ask that is because my controllers are really HTTP centered. For example some of them are called: PostUser, GetUser, DeleteUser. So I am guessing they should be inside the infrastructure folder, right?
I am guessing that controllers are ONLY used if your delivery mechanism is a web app, right?
You're right. There's nothing really to do with DDD because DDD is about contexts and language, but for clean architecture and ports and adapters that's the correct thought.
Normally, you would have the structure like this:
So, your application exposes an API that represents a port and you can connect different edge components that implement a physical delivery protocol of different kinds to talk to your application.

Resources