#Nesting resources

1 messages Β· Page 1 of 1 (latest)

abstract escarp
#

Hey guys, I have several different variants of the same "resource", is it possible to nest these variants under the top-level resource directory?

e.g. say i have an "items" resource, with 2 variants "foo", "bar", could I have my project layout look something like: ```text
root/
src/
items/
foo/
foo.controller.ts
foo.service.ts
...
bar/
bar.controller.ts
bar.service.ts


and then in the top level "items" controller/module, just import those child variants?
pallid crypt
abstract escarp
#

ideally there would be a single controller/service/module at the top level (i.e. a resource), but from what I understand I need to add those all as dependencies in my root app.module?

#

didn't phrase that as well as I'd like

#

basically in the above example, "items" would be the "api" resource, and then inside that i would have subdirectories for each api

#

I don't really understand enough about how nest is structured to explain this clearly unfortunately

#

at the url level, you'd have something like /endpoints/{:api_id}/...

pallid crypt
# abstract escarp So the use case is I have several 3rd party apis that I want to wrap, and it did...

If there's enough unique logic for the transactions back and forth between those APIs I'll often create modules which each have their own dedicated HttpModule configuration and related services that are exported and imported into the "main" app module. Something for example like:

modules/
┣ flight-data-enrichment/
┃ ┣ providers/
┃ ┃ β”— services/
┃ ┃   ┣ flight-data-enrichment/
┃ ┃ ┃ ┃ ┣ flight-data-enrichment.service.spec.ts
┃ ┃ ┃ ┃ β”— flight-data-enrichment.service.ts
┃ ┃   β”— index.ts
┃ ┣ flight-data-enrichment.module.ts
┃ β”— index.ts
β”— sam-communication/
  ┣ providers/
┃ ┃ β”— services/
┃ ┃   ┣ sam-communication/
┃ ┃ ┃ ┃ ┣ sam-communication.service.spec.ts
┃ ┃ ┃ ┃ β”— sam-communication.service.ts
┃ ┃   β”— index.ts
  ┣ index.ts
  β”— sam-communication.module.ts
abstract escarp
#

interesting... are the direct children of modules/ resources?

#

I haven't really internalized what modules, services, etc do yet

#

only generally that controllers reference services, which themselves return the data of interest

#

just to be clear, when i say "api" I mean 3rd party apis that I'm fetching data from

pallid crypt
#

In this particular case they're only services.

Controllers should simply direct traffic that's hitting a route, most logic should be handled by another provider, most often a service. (Traffic Controllers is a fun way to remember)

Modules are groupings of resources. These can be pipes, services, controllers, etc. One of the big benefits I find with modules is the ability to have contextual instances. So for example in the sam-communication module HttpModule is imported and has a configuration unique to hitting that API. Same for the flight-data-enrichment module. So when those services (that are defined as providers in their respective modules and exported) have their HttpService injected that service is configured for the endpoints that's relevant to them.

pallid crypt
# abstract escarp just to be clear, when i say "api" I mean 3rd party apis that I'm fetching data ...

yeah that's exactly what the flight-data-enrichment service is for: it hits an internal API to grab additional data which might not have come through from an AMQP message and merges the two data objects. The the sam-communication service sends transformed data to an external API. Both of them have different configuration requirements: Obviously different base URLs, different content-types, different authentication, and different accept values. Packaging them in modules gives me the flexibility to do that and not think about it much after it's all set.

abstract escarp
#

ah ok, yeah that sounds exactly like what I'm trying to do. I'm guessing your code isn't open source, but is there a repo that utilises a similar structure that I can look at, so I can better internalise what you're saying?

pallid crypt
abstract escarp
#

That would be greatly appreciated, thanks for that

pallid crypt
#

While I'm looking, the basic Modules docs will make your life easier https://docs.nestjs.com/modules

abstract escarp
#

Thanks, taking a look

pallid crypt
#

example-nested-module.ts

import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ExampleCommunicationService } from '@example-integration/modules/xample-communication/providers/services';
import { ExampleHttpConfiguration } from '@example-integration/utils/config';

@Module({
    imports: [HttpModule.registerAsync({
        imports: [ConfigModule],
        useClass: ExampleHttpConfiguration
      }),
      ConfigModule
    ],
    providers: [ExampleCommunicationService],
    exports: [ExampleCommunicationService]
})
export class ExampleCommunicationModule {}

example-http-service-configuration.ts

import { HttpModuleOptions, HttpModuleOptionsFactory } from "@nestjs/axios";
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { ExampleConfiguration } from "@example-integration/data";

@Injectable()
export class ExampleHttpConfiguration implements HttpModuleOptionsFactory {
    private readonly apiKey: string;
    private readonly baseUrl: string;
    private readonly contentType: string = 'application/xml';
    
    constructor(private readonly configService: ConfigService) {
        this.apiKey = this.configService.getOrThrow<string>(ExampleConfiguration.ApiKey);
        this.baseUrl = this.configService.getOrThrow<string>(ExampleConfiguration.Url);
    }

    createHttpOptions(): HttpModuleOptions|Promise<HttpModuleOptions> {
        return {
            baseURL: `https://${this.baseUrl}`,
            headers: {
                "Content-Type": this.contentType,
                "X-API-Key": this.apiKey,
                "Accept": this.contentType,
                "Host": this.baseUrl
            }
        }
    }
};

example-communication.service.ts

import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { firstValueFrom } from 'rxjs';
import { ExampleXMLDocument, ExampleResponse } from '@example-integration/data';

@Injectable()
export class ExampleCommunicationService {

    private readonly resourcePath: string = '/example';

    constructor(private readonly exampleHttpService: HttpService) {}

    public async sendXml(xmlRequestData: ExampleXMLDocument): Promise<ExampleResponse> {
        return await firstValueFrom(this.exampleHttpService.post(this.resourcePath, xmlRequestData));
    }
}
#

And then to use the ExampleCommunicationService in another part of the application we simply import the ExampleCommunicationModule into the appropriate module (since it's exported) in my case I'm importing it right into the root app.module.ts

abstract escarp
#

Awesome man, thanks for sharing that. It's so weird, for some reason my nest.js looks so foreign and difficult to understand. I've come mostly from mvc frameworks (e.g. phoenix), but the terminology that nest uses and how it separates logic is very different from what I'm used to

pallid crypt
#

lol don't ask why we have to explicitly set Host 🀣

pallid crypt
abstract escarp
#

Ah ok, yeah never used spring boot before and my only angular experience is with version 1.x

#

(and I imagine it's changed a huge amount architecturally speaking since then)

pallid crypt
abstract escarp
#

oh well there you go lol

pallid crypt
pallid crypt
abstract escarp
#

didn't Angular evolve from angular.js though?

pallid crypt
abstract escarp
#

and I know of it Angular, never used it before though. I'd just assumed it was an attempt to rebrand angular js after people started hating on it

pallid crypt
# abstract escarp and I know of it Angular, never used it before though. I'd just assumed it was a...

lol nah it's very different; I learned AngularJS back in the day when it was the new hotness then jumped to React after a while until somewhere around Angular 4 then I was doing both; but there was a huge learning curve for sure - it's not really at all like AngularJS, they really scrapped everything down and restarted and came out with an entirely different framework (and honestly I'd say it's the only frontend framework that's truly a full-fledged framework as opposed to just a library with a large ecosystem)

#

ironic enough, I started working really heavily with Angular and got a job that wanted me to do some work on their legacy AngularJS and I just couldn't for the life of me get back into the groove with it 🀣 (well that and the fact that it was really poorly written, but to be fair it sounds like they whipped it up in like 4 days and founded their company on it)

pallid crypt
# abstract escarp php?

nah, I can't stand PHP but it's not that difficult to work with. I don't want to say because I'm not really even supposed to know, it's one of those ~things~. I'll just say that it's a low-level language that a fair amount of people sort of know but very very few are actually good at.