I want my service to constantly print all incoming messages from a RabbitMQ queue to the console.
For this, I am using the #golevelup/nestjs-rabbitmq package and I have the RabbitMQModule configured the following way:
{
uri: 'amqp://localhost:5672',
exchanges: [
{
name: 'the_cats_exchange',
type: 'direct',
options: {
durable: true
}
}
],
channels: {
'channel-1': {
default: true
}
}
}
I also have a service that should print the messages to the console:
import { RabbitSubscribe } from "#golevelup/nestjs-rabbitmq";
import { Injectable } from "#nestjs/common";
#Injectable()
export class RabbitMQMessagingService {
#RabbitSubscribe({
exchange: 'the_cats_exchange',
routingKey: 'test_routing_key',
queue: 'cats',
queueOptions: { durable: true }
})
async handleMessage(msg: {}) {
console.log(msg);
}
}
For some reason, even though I am publishing messages to "the_cats_exchange" with the routing key "test_routing_key" no messages are being printed to the console.
I made sure the queue "cats" is bound to "the_cats_exchange" with the "test_routing_key" routing key, so this can't be the problem.
Related
I have a problem with using multiple ClientKafka in one service, here is my implementation:
#Controller()
export class ApiController implements OnModuleInit {
constructor(
#Inject("ACCOUNT_SERVICE") private readonly accountKafkaClient: ClientKafka,
#Inject("WORKSPACE_SERVICE") private readonly workspaceKafkaClient: ClientKafka
) { }
async onModuleInit() {
const requestPatterns = [
'topic'
];
requestPatterns.forEach((pattern) => {
this.accountKafkaClient.subscribeToResponseOf(`account.${pattern}`);
});
await this.accountKafkaClient.connect();
}
async onModuleDestroy() {
await this.accountKafkaClient.close();
}
#Get()
async sendMessage() {
const data = {
msg: "account.topic"
}
const kafkaResponse = this.accountKafkaClient.send<any>('account.topic', JSON.stringify(data));
const response = await firstValueFrom(kafkaResponse);
const kafkaResponse2 = this.workspaceKafkaClient.send<any>('workspace.topic', JSON.stringify(response )) //THIS IS NOT RUNNING, WORKSPACE_SERVICE NOT RECEIVE ANY MESSAGE
return await firstValueFrom(kafkaResponse2);
}
}
can someone tell me why workspaceKafkaClient is not sending any message to WORKSPACE_SERVICE microservice? I try with passing this client in onModule... functions like accountKafkaClient but it didn't help me,
here is also my settings in module:
#Module({
imports: [
ClientsModule.register([
{
name: 'ACCOUNT_SERVICE',
transport: Transport.KAFKA,
options: {
client: {
clientId: 'account_service',
brokers: ['localhost:29092'],
},
consumer: {
groupId: 'account-consumer',
},
},
},
{
name: 'WORKSPACE_SERVICE',
transport: Transport.KAFKA,
options: {
client: {
clientId: 'workspace_service',
brokers: ['localhost:29092'],
},
consumer: {
groupId: 'workspace-consumer',
},
},
},
]),
],
controllers: [ApiController],
providers: [
ApiService,
// KafkaProducerProvider,
],
})
export class ApiModule {}
thanks for any help!
You only need one producer client per application, but Kafka producers never immediately send data to brokers.
You need to flush them for that to happen, which is what await firstValueFrom(...) should do, but you've not shown that method.
Otherwise, you seem to be trying to get the reponse from one topic to send to another, which is what a consumer should be used for, rather than blocking on one producer request.
I'm trying out a event sourced NestJS application.
I'm stuck at the following point:
In my GamesModule I'm setting up the Eventstore connection to my stream.
In these options there are write and read functions which are called by the library to update/ read the stream's last checkpoint position.
I'd like to call my service methods that write and read to/from the database from these functions.
I've tried injecting the service in the constructor, but because the register function is and has to be a static method, I don't have access to whatever is injected in the constructor.
Is it possible to use a service or repository in the Dynamic module's options?
Service writing to and reading from DB:
#Injectable()
export class EventStoreStateService {
private readonly logger = new Logger(EventStoreStateService.name);
constructor(
#InjectRepository(EventStoreState)
private eventStoreStateRepository: Repository<EventStoreState>,
) {}
updateCheckpoint(stream: string, position: number) {
const updated = this.eventStoreStateRepository.update(
{ lastCheckpoint: position },
{ streamName: stream },
);
this.logger.log({ updated });
return updated;
}
getLastCheckpoint(stream: string) {
const last = this.eventStoreStateRepository.findOne({
where: { streamName: stream },
});
this.logger.log({ last });
return last;
}
}
The module where I setup the event-store connection. In the useFactory store.write(key: string, value: number) I'd like to call my service methods
#Module({
imports: [EventStoreStateModule],
})
export class GamesModule {
constructor(
// no access to this service in the static method
//
#Inject(EventStoreStateService)
private readonly eventStoreStateService: EventStoreStateService,
) {}
static register(): // updateCheckpoint: (key: string, value: number) => Promise<number>,
// getLastCheckpoint: (key: string) => Promise<number>,
DynamicModule {
return {
module: GamesModule,
imports: [
CqrsModule,
EventStoreModule.registerFeatureAsync({
type: 'event-store',
useFactory: async (...args) => {
console.log({ args });
return {
featureStreamName: '$ce-game',
type: 'event-store',
subscriptions: [
{
type: EventStoreSubscriptionType.CatchUp, // research various types
stream: '$ce-game',
resolveLinkTos: true,
},
],
eventHandlers: EventStoreInstanciators,
store: {
storeKey: 'game',
write: async (key: string, value: number) => {
// TODO: on every new event for stream x this function
// is called with the last position number
// problem: we need access to the service that connects
// to ORM, but it's a static method so no access to whatever
// is injected in the constructor
//
},
read: async (key: string) => {
// same as write function
//
},
clear: () => null,
},
};
},
}),
TypeOrmModule.forFeature([GameProjection]),
],
controllers: [GamesController],
providers: [
GamesResolver,
GamesService,
GamesRepository,
...CommandHandlers,
...EventHandlers,
],
};
}
}
Using this library for event-store connection: https://github.com/juicycleff/nestjs-event-store.
Key things to know in NestJs.
If you want to access service within the same module, make sure you inject service in your constructor like constructor( private readonly eventStoreStateService: EventStoreStateService){}
If you want to access service from another module then you have to export the service in .module.ts exports: [EventStoreStateService] and then also inject in service or controller where you want to use it.
hey i am trying to implement nest js microservice with grpc.
it looks impossible to use nestjs microservice with struct type for plain objects.
how can i use compiled protos with nestjs ?
imports: [
ClientsModule.register([
{
name: 'HERO_PACKAGE',
transport: Transport.GRPC,
options: {
package: 'hero',
protoPath: join(__dirname, 'hero/hero.proto'),
},
},
]),
];
how can i load the compiled protos. something like https://medium.com/#waynelpu/how-to-pass-plain-javascript-object-with-gprc-94a91906ab1c
I'm maybe late but I found an answer.
Imagine you have the following protobuf
// task.proto
syntax = "proto3";
// Area API
package task;
// Imports
import "google/protobuf/struct.proto";
import "google/protobuf/empty.proto";
message Task {
string name = 1;
// Any additional metadata required to execute the task
google.protobuf.Struct params = 2;
}
service TaskService {
rpc CreateTask(CreateTaskRequest) returns (Task)
}
message CreateTaskRequest {
string name = 1;
google.protobuf.Struct params = 2;
}
You will need to add an option in your module to correctly load object
await app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: [ 'task' ],
protoPath: [ __dirname + '/protos/task.proto' ],
url: `${host}:${port}`,
loader: {
objects: true // This property
}
},
});
And then in the resolver, you will retrieve a structure that his gRPC Struct compliant, you can find more information here.
Now imagine that you execute a rpc call to CreateTask (Postman has a great beta client for example)
{
"name": "fugiat qui laboris dolore",
"params": {
"fields": {
"foo": {
"stringValue": "bar"
},
"bar": {
"numberValue": 4
},
"baz": {
"boolValue": false
}
}
}
}
You will correctly retrieve it in your handler
async createTask(#Payload(new ValidationPipe({ whitelist: true })) req: CreateTaskRequest): Promise<Task> {
console.log(req.params)
...
}
Result
[Nest] 36410 - 02/09/2022, 3:33:33 PM LOG [NestMicroservice] Nest microservice successfully started +55ms
[Nest] 36410 - 02/09/2022, 3:33:33 PM LOG Workflow gRPC service listening on localhost:8000
[Nest] 36410 - 02/09/2022, 3:33:36 PM LOG Received rpc call to /area.task.TaskService/CreateTask with data: '{"workflowId":"1f5a0501-ce63-437d-b70f-a0ea3c22f0ff","name":"fugiat qui laboris dolore","type":0,"action":0,"nextTask":"Ut mollit","params":{"fields":{"foo":{"stringValue":"bar"},"bar":{"numberValue":4},"baz":{"boolValue":false}}}}'
{
fields: {
foo: { stringValue: 'bar' },
bar: { numberValue: 4 },
baz: { boolValue: false }
}
}
The only thing you have to do now is creating a simple function to correctly wrap struct send by your client.
You can find more information with a real example on that repository.
Don't hesitate if you need anything else or if you found a better way to do it :D
Currently, I am using #ApiExcludeEndpoint() ### on top of all methods to hide the end-point in the swagger-ui, like this:
import { Controller, Get, Query, Param } from '#nestjs/common';
import { ResourceService } from './resource.service';
import { Auth } from 'src/auth/auth.decorator';
import {
ApiTags,
ApiSecurity,
ApiOkResponse,
ApiForbiddenResponse,
ApiCreatedResponse,
ApiExcludeEndpoint
} from '#nestjs/swagger';
#Controller()
#ApiTags('Resources')
#ApiSecurity('apiKey')
export class ResourceController {
constructor(private readonly resourceService: ResourceService) {}
#Get('get_url')
#ApiExcludeEndpoint()
#Get()
#ApiOkResponse({
description: 'Resources list has succesfully been returned',
})
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findAll(#Query() query: any): any {
......
}
#Get('get_url/:id')
#ApiExcludeEndpoint()
#ApiOkResponse({ description: 'Resource has succesfully been returned' })
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findById(#Param('id') id: string, #Query() query: any): any {
......
}
}
I Need to know is there a way to hide all the end-point in the controller using a single decorator, I checked some documents it says to use #ApiIgnore() and #Hidden() but I can't find those in nestjs-swagger. Please comment on this
One possibility is to explicitly include the modules that you'd like to include in the swagger docs instead of just "including all modules" by default. Example:
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const catDocument = SwaggerModule.createDocument(app, options, {
include: [LionsModule, TigersModule], // don't include, say, BearsModule
});
SwaggerModule.setup('api/cats', app, catDocument);
Without the explicit include:[] property, LionsModule, TigersModule, and BearsModule would be automatically included.
To hide all the end-point in the controller.ts, you must use ApiExcludeController instead of ApiExcludeEndpoint as in the example.
https://docs.nestjs.com/openapi/decorators
import { Controller, Get, Query, Param } from '#nestjs/common';
import { ResourceService } from './resource.service';
import { Auth } from 'src/auth/auth.decorator';
import {
ApiTags,
ApiSecurity,
ApiOkResponse,
ApiForbiddenResponse,
ApiCreatedResponse,
ApiExcludeController
// ApiExcludeEndpoint
} from '#nestjs/swagger';
#Controller()
#ApiTags('Resources')
#ApiSecurity('apiKey')
#ApiExcludeController()
export class ResourceController {
constructor(private readonly resourceService: ResourceService) {}
#Get('get_url')
// #ApiExcludeEndpoint()
#Get()
#ApiOkResponse({
description: 'Resources list has succesfully been returned',
})
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findAll(#Query() query: any): any {
......
}
#Get('get_url/:id')
// #ApiExcludeEndpoint()
#ApiOkResponse({ description: 'Resource has succesfully been returned' })
#ApiForbiddenResponse({ description: 'You are not allowed' })
#Auth(...common_privileges)
findById(#Param('id') id: string, #Query() query: any): any {
......
}
}
I'm stating learning about Nestjs and i start a basic "task app", but i start using mongoose on my project and show an error:
> Potential solutions:
- If TaskModel is a provider, is it part of the current AppModule?
- If TaskModel is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing TaskModel */ ]
})
i try diffrents ways for solving it, but im soo noob on this tool,so is soo frustrating can't found how to solve this error.. pls help a noob on this framework
tasks.service.ts
import { InjectModel } from "#nestjs/mongoose";
import { Injectable } from '#nestjs/common';
import { Task } from './interface/ITasks';
import { Model } from "mongoose";
#Injectable()
export class TasksService {
constructor(#InjectModel('Task') private taskModel: Model<Task>) { }
async getTasks() {
return await this.taskModel.find()
}
async getTask(id: string) {
return await this.taskModel.findById(id)
}
}
task.module.ts
import { TaskSchema } from './schema/task.schema';
import { MongooseModule } from '#nestjs/mongoose';
import { Module } from '#nestjs/common';
import { TasksController } from './tasks.controller';
import { TasksService } from './tasks.service';
#Module({
imports: [MongooseModule.forFeature([
{ name: 'Task', schema: TaskSchema }
], 'Tasks'),
],
controllers: [TasksController],
providers: [TasksService]
})
export class TasksModule { }
task.controller.ts
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './DTO/create-task.dto';
import { Controller, Get, Post, Put, Delete, Body, Param } from '#nestjs/common';
import { Task } from './interface/ITasks';
#Controller('tasks')
export class TasksController {
constructor(private taskService: TasksService) { }
#Get() // Decorador
getTasks(): Promise<Task[]> {
return this.taskService.getTasks()
}
#Get(':id') // Decorador
getTask(#Param('id') id: string) {
return this.taskService.getTask(id)
}
#Post('/newTask')
postTask(#Body() task: CreateTaskDto): { title: string, description: string, done: boolean } {
console.log(task.title, task.description, task.done)
return task
}
#Put(':id')
updateTask(#Body() task: CreateTaskDto, #Param('id') id) {
console.log(task)
console.log(id)
let action = task
return action
}
#Delete(':taskId')
deleteTask(#Param('taskId') taskId): { status: string, id: string } {
let action = { status: "Deleted task", id: taskId }
console.log(action);
return action
}
}
task.schema.ts
import { Schema } from "mongoose";
export const TaskSchema = new Schema({
title: String,
decription: String,
done: Boolean
})
In the future, please show the entire error as it will be more helpful. This partial message is enough for some, but maybe not for everyone.
According to the error, a potential solution would be to add the TaskModel to the AppModule. This leads me to believe you have TasksService in your AppModule's providers array. If that is the case, what is happening is Nest is trying to instantiate two instances of your TasksService, one for the TasksModule and one for the AppModule. You probably didn't mean to do this, and the way to resolve it would be to remove the TasksService from the AppModule's providers array and only have it in the TasksModule's providers array.