I'm facing the following issue. I have a service used by a controller. The service (in the snippets below QueueService) injects a provider imported from a package. I aim to reuse the QueueService across the controller methods, but I also need to dynamically specify which provider QueueService should use.
My question is, how can I achieve this behaviour?
import { PubsubService } from '#myorg/queue'
#Module({
imports: [
ConfigModule.forRoot({
SHARED_RESOURCES_PROJECT_ID: Joi.string().required()
})
})
],
controllers: [AppController],
providers: [
{
provide: 'PUBSUB',
useValue: new PubsubService()
},
{
provide: 'INTEGRATION_PUBSUB',
useValue: new PubsubService({ projectId: process.env.SHARED_RESOURCES_PROJECT_ID })
}
]
})
export class AppModule {}
#Controller()
export class AppController {
constructor(private queueService: QueueService) {}
#Post()
async create() {
...
// here I want to use queueService with `PUBSUB` injected
return this.queueService.sendMessage(...)
}
#Patch()
async update() {
...
// here I want to use queueService with `INTEGRATION_PUBSUB` injected
return this.queueService.sendMessage(...)
}
}
#Injectable()
export class QueueService {
constructor(
// how can I dynamically change `#Inject('PUBSUB')` to `#Inject('INTEGRATION_PUBSUB')`?
#Inject('PUBSUB') private readonly pubsubService: PubsubService
) {}
async sendMessage(payload): Promise<void> {
return this.pubsubService.sendMessage(payload)
}
}
dynamic inject is not possible after object(in this case controller) created . so you have two option
1- create two QueueService (one for PUBSUB and another for INTEGRATION_PUBSUB) and inject both to controller. use those in your controller functions. (i recommend this)
2- inject both PUBSUB and INTEGRATION_PUBSUB into QueueService and pass another param in sendMessage function . so check this param to choose between PUBSUB and INTEGRATION_PUBSUB
I have a mixin which contains beforeCreate lifecycle event.
I would like to import that mixin only into certain components, which are directly loaded through router. I don't want to go into each one of them and manually import the mixin, and I would also want to avoid loading it globally.
I believe that the proper way to do it is in route options, possibly overriding the component method, or by adding mixin option for the route (alongside props, meta...).
I requested this new feature, but I guess I was misunderstood, or I didn't understand the proposed solution.
I tried to create main Vue instance and extend it in my components, but the method only executed from the main component.
Is there any way to make this work?
Example of project code is here
Perhaps I've misunderstood what you're asking but I'd have thought you could achieve this by extending the component:
import Vue from 'vue'
import Router from 'vue-router'
import MyMixin from './mixins/MyMixin'
import MyList from './components/MyList'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/list',
name: 'list',
component: {
extends: MyList,
mixins: [MyMixin]
}
}
// ...
]
})
So rather than using MyList directly it's being extended and the mixin added in.
Or if you've got a lot of them and want to avoid duplication you could do something like this:
export default new Router({
routes: [
{
path: '/list',
name: 'list',
doMagic: true,
component: MyList
}
// ...
].map(route => {
if (route.doMagic) {
route.component = {
extends: route.component,
mixins: [MyMixin]
}
}
return route
})
})
Here I've used a flag called doMagic to determine which components to modify but if you just wanted to change all of them then you wouldn't need such a flag.
That doesn't take nested routes into account but it could be adapted if required.
Likewise if you're using async components then you'll have to fiddle around with the promises but the core principle should be exactly the same.
Update:
Based on the example code provided, the following seems to work with lazily loaded components:
const routes = [
// ... routes defined as usual
];
const newRoutes = routes.map(route => {
const originalComponent = route.component;
let component = null;
if (typeof originalComponent === 'object') {
// Components that aren't lazily loaded
component = wrap(originalComponent);
} else {
// Components that are lazily loaded
component = async () => {
const module = await originalComponent();
return wrap(module.default || module);
}
}
return {
...route,
component
};
function wrap (cmp) {
return {
extends: cmp,
mixins: [MyMixin]
}
}
});
export default new Router({
routes: newRoutes
});
I have a route that has its own model, which does not come from the Ember store (let's say it can come from "anywhere" for the sake of this question).
model() {
return RSVP.hash({
value: someCall()
});
}
this.owner.lookup('route:routeName').model() does not seem to work, neither does this.owner.lookup('controller:controllerName').get('model.X') or any of the other things I've tried.
Nor does it seem to be mentioned at https://guides.emberjs.com/v3.1.0/testing/testing-routes/
How would you retrieve a route's model in a test?
The Ember router doesn't appear to have any kind of public interface to get the model according to the official docs (https://emberjs.com/api/ember/3.1/classes/EmberRouter). It can access the model function internally though. This feels a bit hackish, but I was able to get it to work:
Router:
import Route from '#ember/routing/route';
import { hash } from 'rsvp';
export default Route.extend({
model() {
return hash({
value: 'wibble'
});
},
getMyModel: function() {
return this.get('model')();
}
});
Router test:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | test', function(hooks) {
setupTest(hooks);
test('do something with the router model...', function(assert) {
let route = this.owner.lookup('route:test');
assert.ok(route);
route.getMyModel().then(function(model) {
console.log(model);
assert.equal(model.value, 'wibble');
});
});
});
You can go with:
route.get('model')()
In my Angular app, if I load the home page / and then navigate to, say, /products, it works fine (it's a lazy-loaded module). But if now I reload the page, the browser makes a GET /products call to the server, which results in a 404.
The solution is to send index.html and the Angular app is back on rails. So in Express I do app.all("*", (req,res) => { res.sendFile("index.html") }) and it works.
How to do the same thing in Nest?
There is a #All decorator, but each controller in a given component handles a subroute, for instance #Controller("cats") will match /cats routes, so if I add #All in this controller, it will match only /cats/*, not *.
Must I really create a whole separate module with a controller, just for this? That's what I did
#Controller() // Matches "/"
export class GenericController {
#All() // Matches "*" on all methods GET, POST...
genericFunction(){
console.log("Generic route reached")
}
}
And in my main module :
#Module({
imports: [
ItemsModule, // Other routes like /items
GenericModule, // Generic "*" route last
],
})
It works, but it seems overkill. Is this the way to go or is there a simpler trick?
So, will be best to use global-scoped exception filter.
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
app.useGlobalFilters(new NotFoundExceptionFilter());
await app.listen(3000);
}
bootstrap();
NotFoundExceptionFilter:
import { ExceptionFilter, Catch, NotFoundException } from '#nestjs/common';
import { HttpException } from '#nestjs/common';
#Catch(NotFoundException)
export class NotFoundExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
// here return `index.html`
}
}
Maybe it will not work, will test later
You don't need to create a separated GenericModule. However, GenericController is fully valid and you approach is definitely a good one. The question is rather what would you like to achieve using this generic route. If handling "Route not found" error is your requirement, a better choice is an exception filter.
An alternative answer in 2022.
I've solved this by specifying the routes in the order I want them evaluated. In my instance I am using a fallback route to catch all requests, but if I need custom processing I want to create a route which superceeds the fallback route.
However, in defining a catchall route /api/:resource in the AppController, I found the fallback route would overwrite all other routes.
My solution to this is to define the fallback route in it's own module and ensure that it is appended to the list of modules. This way it is created last and will only catch what falls through.
#router.ts
import {RouterModule} from '#nestjs/core/router';
import {ContentBlockModule} from './content_block/content_block.module';
import {FallbackModule} from './fallback/fallback.module';
const APIRoutesWithFallbackRoute = RouterModule.register([
{
// This lets me avoid prepending my routes with /api prefixes
path: 'api',
// Overload the /api/content_blocks route and foward it to the custom module
children: [
{
path: 'content_blocks',
module: ContentBlockModule,
},
],
},
{ //Fallback Route catches any post to /api/:resource
path: 'api',
module: FallbackModule,
},
]);
#app.module
App module imports the fallback module. Important Ensure FallbackModule is the last module to be declaired or it will overwrite routes that are included after it.
import {Module} from '#nestjs/common';
import {AppService} from './app.service';
import {APIRoutesWithFallbackRoute} from './APIRoutesWithFallbackRoute';
import {ContentBlockModule} from './content_block/content_block.module';
import {FallbackModule} from './fallback/fallback.module';
// APIRoutes include first, Fallback Routes prepended.
#Module({
imports: [APIRoutesWithFallbackRoute, ContentBlockModule, FallbackModule],
controllers: [],
providers: [AppService],
})
export class AppModule {}
FallbackController
import {Controller, Post, Req, Res} from '#nestjs/common';
import {defaultHandler} from 'ra-data-simple-prisma';
import {FallbackService} from './fallback.service';
#Controller()
export class FallbackController {
constructor(private readonly prisma: FallbackService) {}
#Post(':resource')
fallback(#Req() req, #Res() res) {
// return this.appService.getData();
console.log('executing from the default fallback route');
return defaultHandler(req, res, this.prisma);
}
}
ContentBlockController
The content block controller, included here for completeness.
#Controller()
export class ContentBlockController {
constructor(
private readonly contentBlockService: ContentBlockService,
private readonly prisma: PrismaService,
) {}
#Post()
async create(
#Body() contentBlock: content_blocks,
#Req() req: Request,
#Res() res: Response,
): Promise<void> {
console.log('executing from the resource specific route');
// lean on my service to do heavy business logic
const [model, values] = await this.contentBlockService.createContentBlock(
contentBlock,
);
// inject custom logic...
const alteredRequest: CreateRequest = {
...req,
body: {
...req.body,
params: {
data: values,
},
},
};
return createHandler(alteredRequest, res, model);
}
}
Using this system I am able to define a single route to handle 90% of the routes necessary to expose my Prisma models to my private API. And if I need custom logic I have full control.
Been reading the docs and googling around for best practice to handle api calls in bigger projects without luck (or ateast not what Im searching for).
I want to create a service / facade for the backend that I can load in every component that needs it. For exampel.
I want to fetch historical data for weather in a service so in every component I need this I can just load the weather-serivce and use a getter to fetch the wanted data. I would like to end up with something like below. But I dosent get it to work. So I wonder, what is best practice for this in vue.js?
import WeatherFacade from './data/WeatherFacade.vue'
export default {
name: 'Chart',
created () {
console.log(WeatherFacade.getWeather())
},
components: {
WeatherFacade
}
}
ps. using vue 2.1.10
It could be easily done by creating some external object that will hold those data and module bundling.What I usually do in my projects is that I create services directory and group them in order I want.
Let's break it down - services/WeatherFascade.js (using VueResource)
import Vue from 'vue'
export default {
getWeather() {
return Vue.http.get('api/weather')
}
}
If you have to pass some dynamic data such as ID, pass it as just parameter
import Vue from 'vue'
export default {
getWeather(id) {
return Vue.http.get(`api/weather/${id}`)
}
}
Then in your component you can import this service, pass parameters (if you have them) and got data back.
import WeatherFascade from '../services/WeatherFascade'
export default {
data() {
return {
weatherItems: []
}
},
created() {
this.getWeatherData()
},
methods: {
getWeatherData() {
WeatherFascade.getWather(// you can pass params here)
.then(response => this.weatherItems = response.data)
.catch(error => console.log(error))
}
}
}
You can use any library for that you like, for instance axios is cool.