Nestjs rabbitmq publisher, consumer and hhtp API in one application - rabbitmq

I need to know if it's possible to have all these three in single application. I have seen many examples where there are two different project one for RabbitMQ publisher and one for subscriber/consumer.
I just need to know if there's a possibility to implement something like that. I failed to find any example consisting both in one app.
I have tried to implement something similar but it did not work.
folder structure :
src
--main.ts
--app.service.ts
--app.resolver.ts
--app.module.ts
main.ts file
async function bootstrap() {
let RUNPORT = process.env.PORT ? process.env.PORT : 3000;
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.RMQ,
options: {
urls: [`amqp://guest:guest#localhost:5672`],
queue: 'email-subscribers',
queueOptions: {
durable: true,
},
noAck: false,
// Get one by one
prefetchCount: 1,
},
});
app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();
app.module.ts file
providers: [
AppService,
AppResolver,
{
provide: 'GREETING_SERVICE',
useFactory: (configService: ConfigService) => {
const user = configService.get('RABBITMQ_USER');
const password = configService.get('RABBITMQ_PASSWORD');
const host = configService.get('RABBITMQ_HOST');
const queueName = configService.get('RABBITMQ_QUEUE_NAME');
return ClientProxyFactory.create({
transport: Transport.RMQ,
options: {
urls: [`amqp://${user}:${password}#${host}`],
queue: queueName,
queueOptions: {
durable: true,
},
},
});
},
inject: [ConfigService],
},
],
app.resolver.ts
#Resolver()
export class AppResolver {
constructor(
private readonly appService: AppService,
#Inject('GREETING_SERVICE') private client: ClientProxy,
) {}
#Public()
#Query(() => String)
async getHello() {
// this.client.emit('new_message', { text: 'Myu data' });
this.client.send('new_message', 'boom baby').subscribe();
return await this.appService.getHello();
}
#EventPattern('new_message')
async getDataPublish(data) {
console.log(data);
// return this.appService.publishEvent(data);
}

Related

How can I set Next-Auth callback url? and next-auth session return null

I want to set login, logout callback url.
So, I set the callback url like this.
//signIn
const signInResult = await signIn("credentials", {
message,
signature,
redirect: false,
callbackUrl: `${env.nextauth_url}`,
});
//signOut
signOut({ callbackUrl: `${env.nextauth_url}`, redirect: false });
But, When I log in, I look at the network tab.
api/auth/providers, api/auth/callback/credentials? reply with
callbackUrl(url) localhost:3000
It's api/auth/callback/credentials? reply.
It's api/auth/providers reply
and api/auth/session reply empty object.
When I run on http://localhost:3000, everything was perfect.
But, After deploy, the login is not working properly.
How can I fix the error?
I added [...next-auth] code.
import CredentialsProvider from "next-auth/providers/credentials";
import NextAuth from "next-auth";
import Moralis from "moralis";
import env from "env.json";
export default NextAuth({
providers: [
CredentialsProvider({
name: "MoralisAuth",
credentials: {
message: {
label: "Message",
type: "text",
placeholder: "0x0",
},
signature: {
label: "Signature",
type: "text",
placeholder: "0x0",
},
},
async authorize(credentials: any): Promise<any> {
try {
const { message, signature } = credentials;
await Moralis.start({
apiKey: env.moralis_api_key,
});
const { address, profileId } = (
await Moralis.Auth.verify({ message, signature, network: "evm" })
).raw;
if (address && profileId) {
const user = { address, profileId, signature };
if (user) {
return user;
}
}
} catch (error) {
console.error(error);
return null;
}
},
}),
],
pages: {
signIn: "/",
signOut: "/",
},
session: {
maxAge: 3 * 24 * 60 * 60,
},
callbacks: {
async jwt({ token, user }) {
user && (token.user = user);
return token;
},
async session({ session, token }: any) {
session.user = token.user;
return session;
},
async redirect({ url, baseUrl }) {
// Allows relative callback URLs
if (url.startsWith("/")) return `${baseUrl}${url}`;
// Allows callback URLs on the same origin
else if (new URL(url).origin === baseUrl) return url;
return baseUrl;
},
},
secret: env.nextauth_secret,
});

Send message from service to other components (React Native)

I'm thinking about the best way how to implement rabbitmq or mqtt sending messages from callback(queue.on) to chat component and last dialogs component, where I will update dialogs and chats. I heard about eventemitter, but I don't know best way,try to use eventemitter or make own handlers. I am very new in react.
MessageBrokerService:
import {Connection, Exchange, Queue} from "react-native-rabbitmq";
import uuid from 'react-native-uuid';
export default class RabbitMQService {
startEventSubscribe = (exchange) => {
exchangeName = exchange;
connection.connect();
}
}
const config = {
host: '',
port: 5671,
username: '',
password: '',
virtualhost: '/',
ttl: 10000,
ssl: true,
};
let connection = new Connection(config);
let queue;
let exchangeName;
connection.on('connected', event => {
queue = new Queue(
connection,
{
name: uuid.v4(),
durable: true,
},
{
// queueDeclare args here like x-message-ttl
},
);
let exchange = new Exchange(connection, {
name: exchangeName,
});
queue.bind(exchange, '');
// Receive messages
queue.on('message', message => {
console.log(message);
**I want from here send messages to chat component and last dialogs from here**
queue.basicAck(message.delivery_tag);
});
});
connection.on('error', event => {
console.log('fail');
console.log(event);
});
const publishMessage = message => {
let routing_key = 'message-exchange';
let properties = {
//header authorization jwt
};
let exchangeSendMessages = new Exchange(connection, {
name: 'message-exchange',
type: 'direct',
durable: true,
});
exchangeSendMessages.publish(message, routing_key, properties);
};
Last Dialogs screen:
const LastDialogsScreen = () => {
**Want here to receive messages here from callback in MessageBrokerService**
return ()
}
Chat screen:
const ChatScreen = () => {
**Want here to receive messages in here too from callback in MessageBrokerService**
return ()
}

Nest.js e2e test Cannot read property 'findOne' of undefined in service dependencies

I try to run my e2e test for Nest.js server, but seems my application doesn't create dynamically instances
describe('UserModule', () => {
let app: INestApplication;
let service: UserService;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
UserModule,
ConfigModule.forRoot({ load: [config], isGlobal: true }),
TypeOrmModule.forRootAsync({
useFactory: () => ({
...testConfig
})
})
],
})
.compile();
app = moduleRef.createNestApplication();
await app.init();
// service = app.get(SftpService); // In that case I got the same error as in main test
service = await moduleRef.resolve(SftpService);
});
it(`/GET users`, async () => {
console.log(await service.getUser(1)); //it works correct
await request(app.getHttpServer()).get('/users').expect(200).expect({
data: [{ id: 1, name: 'user' }]
});
});
afterAll(async () => {
await app.close();
});
});
my service lucks like
#Injectable()
export class UserService {
constructor(
#InjectRepository(UserRepository) private readonly userRepository: Repository<User>,
#Inject(OtherService) private readonly otherService: OtherService,
) {}
async getUser(userId: number): Promise<User> {
return this.userRepository.findOne(id);
}
}
On running test I get such error:
TypeError: Cannot read property 'findOne' of undefined
at UserService.<anonymous> (/Users/user/src/modules/user/service.ts:23:28)
The same for other injected services of repositories in the user service.

Restarting express server in esbuild

I am trying to create a simple express server with esbuild. These are my code
import express from "express";
const app = express();
const port = 3000;
const stopServer = {
stop: () => {},
};
export const createServer = async () => {
app.get("/", async (req, res) => {
res.json({
first: "Hello",
});
});
const server = app.listen(port, () => {
console.log(`Listening on port: ${port}`);
});
stopServer.stop = () => {
server.close();
};
};
export const stop = () => {
stopServer.stop();
stopServer.stop = () => {};
};
esbuild.config.js
const esbuild = require("esbuild");
const path = require("path");
const restartPlugin = () => {
return {
name: "restart-express",
setup(build) {
build.onEnd(async (res) => {
const { stop, createServer } = await import("../dist/server.js");
stop();
createServer();
});
},
};
};
const run = async () => {
await esbuild.build({
entryPoints: [path.resolve(__dirname, "../src/server.ts")],
outdir: path.resolve(__dirname, "../dist"),
platform: "node",
sourcemap: true,
format: "cjs",
watch: {
onRebuild: async (err, res) => {
if (err) {
console.error(err);
} else {
console.log("There is some change");
}
},
},
plugins: [restartPlugin()],
});
};
run();
Reference for plugin : https://github.com/evanw/esbuild/issues/1258#issuecomment-834676530
If you were to run this application It i will work initially but when you change the code, the server wont get updated even if you refresh the page.
I am not really sure where I am making mistake, Any help please
The problem is that node cache the import("..dist/server.js"), as a result it will never return new module. To solve this problem we will write a function
const purgeAppRequireCache = (buildPath) => {
for (let key in require.cache) {
if (key.startsWith(buildPath)) {
delete require.cache[key];
}
}
};
Which will remove the cache from the node. We can also use this function in this manner. Which solves my problem
const esbuild = require("esbuild");
const path = require("path");
const startPlugin = () => {
return {
name: "startPlugin",
setup(build) {
build.onEnd((res) => {
const serverPath = path.resolve(__dirname, "../dist/server.js");
const { stop } = require("../dist/server.js");
stop();
purgeAppRequireCache(serverPath);
purgeAppRequireCache(path.resolve(__dirname, "../src"));
const { listen } = require("../dist/server");
listen();
});
},
};
};
const run = async () => {
await esbuild.build({
entryPoints: [path.resolve(__dirname, "../src/server.tsx")],
outdir: path.resolve(__dirname, "../dist"),
platform: "node",
sourcemap: true,
format: "cjs",
watch: true,
bundle: true,
plugins: [startPlugin()],
});
};
run();
const purgeAppRequireCache = (buildPath) => {
for (let key in require.cache) {
if (key.startsWith(buildPath)) {
delete require.cache[key];
}
}
};
If you not reload runtime, the global's object and sub require(xxx) maby have same error.
You can use kill and fork cluster when change you code, it's same fast like require(xxx), there have example codes: https://github.com/ymzuiku/bike/blob/master/lib/index.js
If you need see kill and fork cluster example, here's a same feature package, also use esbuild, but it use fs.watch: https://www.npmjs.com/package/bike
Hope there could help you :)
#es-exec/esbuild-plugin-serve or #es-exec/esbuild-plugin-start are two alternative esbuild plugins that you can try. They run your bundles or any command line script for you after building your project (supports watch mode for rebuilding on file changes).
The documentation can be found at the following:
#es-exec/esbuild-plugin-serve
#es-exec/esbuild-plugin-start
Disclaimer: I am the author of these packages.

How to handle message sent from server to client with RSocket?

I try to use RSocketRequester to send a message from the server to the specific client, but I don't know how to handle it on the frontend. The server is Spring Webflux with the controller like this:
data class Message(val message: String)
#Controller
class RSocketController {
private val log = LoggerFactory.getLogger(RSocketController::class.java)
#MessageMapping("say.hello")
fun sayHello(message: String): Flux<Message> {
log.info("say hello {}", message)
return Flux.just(Message("server says hello"))
}
#MessageMapping("say.hi")
fun sayHi(message: String, rSocketRequester: RSocketRequester): Flux<Message> {
log.info("say hi {}", message)
rSocketRequester
.route("say.hello")
.data(Message("server says hi hello ;)"))
.send()
.subscribe()
return Flux.just(Message("server says hi!!"))
}
}
On the frontend I use rsocket-js. The sayHello method works just fine (request-stream), but when I call the sayHi method I want to send two messages from the server. The first one to say.hello endpoint, and the second to say.hi endpoint. I've got rsocket-js implementation like this:
sayHello() {
console.log("say hello");
this.requestStream("say.hello");
},
sayHi() {
console.log("say hi");
this.requestStream("say.hi");
},
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
transport
});
client.connect().subscribe({
onComplete: socket => {
this.socket = socket;
console.log("complete connection");
},
onError: error => {
console.log("got connection error");
console.error(error);
},
onSubscribe: cancel => {
console.log("subscribe connection");
console.log(cancel);
}
});
},
requestStream(url) {
if (this.socket) {
this.socket
.requestStream({
data: url + " from client",
metadata: String.fromCharCode(url.length) + url
})
.subscribe({
onComplete: () => console.log("requestStream done"),
onError: error => {
console.log("got error with requestStream");
console.error(error);
},
onNext: value => {
// console.log("got next value in requestStream..");
console.log("got data from sever");
console.log(value.data);
},
// Nothing happens until `request(n)` is called
onSubscribe: sub => {
console.log("subscribe request Stream!");
sub.request(2147483647);
// sub.request(3);
}
});
} else {
console.log("not connected...");
}
}
I can see both messages in Google Chrome DevTools -> Network -> rsocket. So the client receives them but I can't catch in the code the one sent by RSocketRequester.
It seems that the server uses fireAndForget method. How to handle it on the client side?
As #VladMamaev said, we can provide a responder to the client like in this example https://github.com/rsocket/rsocket-js/blob/master/packages/rsocket-examples/src/LeaseClientExample.js#L104
For me, fireAndForget method is enough.
export class EchoResponder {
constructor(callback) {
this.callback = callback;
}
fireAndForget(payload) {
this.callback(payload);
}
}
import { EchoResponder } from "~/assets/EchoResponder";
...
const messageReceiver = payload => {
//do what you want to do with received message
console.log(payload)
};
const responder = new EchoResponder(messageReceiver);
connect() {
const transport = new RSocketWebSocketClient({
url: "ws://localhost:8080/rsocket"
});
const client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: "application/json",
metadataMimeType: "message/x.rsocket.routing.v0"
},
responder: responder,
transport
});