What's the right way to publish Redis message from a Redis based NestJS Microservice - redis

I built a sample Redis based Microservice with NestJS. It's fantastic and works great. In my microservice, after processing the message received over Redis (pub/sub), we publish our result to another Redis channel for a different microservice to pick.
What's the right way to publish? Are there any samples?
For my work, I used Redis package and published it (as opposed to ClientProxyFactory). Works fine and gets the job done.
import {
ClientProxy,
ClientProxyFactory,
Transport,
} from '#nestjs/microservices';
import { Injectable, Logger } from '#nestjs/common';
import * as redis from 'redis';
import { NVResponseDTO } from "../dto/nv.dto";
#Injectable()
export class NVPersistService {
logger = new Logger('NVPersistService');
private client: redis.RedisClient;
constructor() {
this.client = redis.createClient({port: 6379, host: 'localhost'});
this.logger.log('Successfully created client for publish');
}
async publish(result: NVResponseDTO) {
const channel = 'persistence';
try {
await this.client.publish(channel, JSON.stringify(result));
this.logger.log(`Message sent`);
} catch (e) {
this.logger.error(e);
}
}
}
But is this the way to do it or should I use something like below
this.client = ClientProxyFactory.create({
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
}
});
await this.client.connect();
const channel = 'persistence';
const status = await this.client.send<string, NVResponseDTO>(channel, result);
this.logger.log(`Status of send - ${status}`);
Note: Above code did not work for me, hence used Redis directly. Any guidance would be much appreciated

Related

Server to Server events in Socket.io

I'm using Socket.io with Redis Adapter in GKE where I have two replicas of socket server I want to create something scale able that's why I'm using redis pub/sub but I also need to listen when something happen in other replica, is it possible to send events from one replica to another replica.
Server1.js
import { Server } from "socket.io";
import { createAdapter } from "#socket.io/redis-adapter";
import { createClient } from "redis";
const io = new Server();
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
io.adapter(createAdapter(pubClient, subClient));
io.listen(3000);
io.on("custom-event", () => {
console.log("Custom event occur");
});
From replica I want to fire event
import { Server } from "socket.io";
import { createAdapter } from "#socket.io/redis-adapter";
import { createClient } from "redis";
const io = new Server();
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
io.adapter(createAdapter(pubClient, subClient));
io.listen(3000);
io.on("connection", socket => {
socket.emit("custom-event"); // not working
io.emit("custom event") // not working
})
How is it possible or is there any alternative.

How can I return UUID using RabbitMQ

I would like to return the UUID(v4) created for each request in nestjs as a response to the request.
I want to return the UUID(v4) that nestjs creates for each request as the response to the request.
However, I am using rabbitmq.
Is there any other way to return the UUID after going through rabbitmq for each request?
With this method, I am creating a connection for each request.
I want to create the connection only once after nestjs is started.
Also, any other method using other libraries would be good.
import { Controller, Get } from '#nestjs/common';
import { v4 as uuidv4 } from 'uuid';
#Controller('cats')
export class CatsController {
#Get()
findAll(): string {
const sequence = uuidv4(); // I want to return this.
return 'This action returns all cats';
}
}
...
await channel.consume(queueName, async (data: any) => {
if (queueName === 'testQueue') {
// do something.
}
});

NestJS with node-redis

Wondering if anyone could help me out with this, as I'm new to NestJS.
I'm trying to use Redis in my existing NestJS service, rather than creating a separate microservice like Nest documents in their examples. When I import redis from node-redis it comes back as undefined.
token.service.ts
import { Injectable } from '#nestjs/common';
import { v4 as uuidv4 } from 'uuid';
import redis from 'redis';
#Injectable()
export class TokenService {
constructor() {
// create new redis client with default options
this.client = redis.createClient();
this.client.on('error', err => console.error(err));
}
...
}
The error I'm getting: Cannot read property 'createClient' of undefined
I've never seen undefined imports in Node, so I'm wondering if this a NestJS specific issue, or if it has to do with the redis package I'm using.
Any help is appreciated.
You're likely having an issue with the import statement.
Try replacing
import redis from 'redis';
with:
import * as redis from 'redis';
or
const redis = require('redis');

How to upload image to strapi?

How can I upload images to Strapi server?
I use the GraphQL plugin.
I prefer a single endpoint for all of API
I found an article about how to manage file uploads but I have some questions
Do I need to convert my image to a Base64 string?
My production server will be PostgreSQL. I was planning to store images as Blob. But now it turns out I can only send Form-Data to Strapi servers.
Do I need something like apollo-upload-client?
Note: Currently I use vue-apollo and nativescript-vue as frontend.
import VueApollo from "vue-apollo";
import { ApolloClient, InMemoryCache, HttpLink } from "apollo-boost";
import { setContext } from "apollo-link-context";
If you are trying to upload files with GraphQL, I suggest you check this gist - https://gist.github.com/alexandrebodin/fedc71c8513bfbb6283cc90ae62755c5
You should have all the information you need to achieve that.
Thanks to the answer of #Jim LAURIE I made my node work:
import { GraphQLClient, gql } from "graphql-request"
import { createReadStream } from "fs";
[...]
const endpoint = 'http://localhost:1337/graphql';
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
authorization: `Bearer ${jwt}`,
},
});
try {
const data = await graphQLClient.request( gql`
mutation($file: Upload!) {
upload(file: $file) {
id
name
}
}
`, {
file: createReadStream(__dirname + '/Strapi/test/picture.jpg') // ⚠ Ensure path is correct or the stream will never send data and you will have Socket Hang out error
});
console.info("ID of file:" + data.upload.id);
console.info(data)
console.info("data")
} catch (error) {
console.error(error)
console.error("error")
}
If you don't know how get the JWT check the units testing docs of Strapi, translate to GraphQL should be easy.

Ionic 3's PWA & Firebase Cloud Messaging registration

I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
What I did:
as advised in the article added FCM libraries into service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
Then created Firebase Messaging based provider here:
import { Injectable } from "#angular/core";
import * as firebase from 'firebase';
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
A bad HTTP response code (404) was received when fetching the script.
:8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
QUESTIONS:
- is there a fresh guide on Ionic 3's PWA & FCM?
- at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.
UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
Prerequisites:
I created ionic blank app (just a home page)
installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
created config (filename environment.ts in src folder):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
I modified app.module.ts to add firebase and angularfire2 this way:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
Here we also import our provider whose code is below:
in providers folder I created firebase-messaging.ts like this:
import { Injectable } from "#angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
index.html in src uncomment:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
OK almost the last thing - your manifest.json (in src) should have exact line:
"gcm_sender_id": "103953800507"
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with Postman)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
I hope this will help some learners in future.
I have successfully implemented the process and got success response on API calls. But no notification popup coming on my browser. Any idea?
api: https://fcm.googleapis.com/fcm/send
response got:
{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}
cheth the attached url of my console: