NodeJS + RxDB + Electron w/ persistent data - node.js

I have been playing around with PouchDB (using express-pouchdb and pouchdb-find) for a while and realized that I needed to work with some streams through Rxjs for my Angular 5/Electron app.
For my current setup, I run express-pouchdb to set up a localized PouchDB for persistence and then in Angular I have the following:
#Injectable()
export class PouchdbService {
private _localdb = 'http://localhost:3000/main';
private _pouchDb: any;
constructor() {
this._pouchDb = new PouchDB(this._localdb);
PouchDB.plugin(PouchDBFind);
this.createIndexes();
}
private createIndexes(): void {
this._pouchDb.createIndex({
index: {
fields: [ 'tags' ]
}
});
this._pouchDb.createIndex({
index: {
fields: [ 'date' ]
}
})
this._pouchDb.createIndex({
index: {
fields: [ 'title' ]
}
});
}
This provides my service to the express app through _localdb property. I could keep the PouchDB express app and just call it through RxDB by rewriting my service and refactoring the code. However, I guess that I need have to call sync on every collection.
Though, I would rather drop the express application if there was a way to run with persistence with RxDB directly.

I ended up using the websql adapter which apparently stores information between application loads and dropped express-pouchdb.

Related

Angular Universal app not honoring 'useAbsoluteUrl' or HTTP Interceptor on server-side

Our Angular 14 app is configured to use Angular Universal for SSR, and is integrated with our .NET 6 back end. The Universal guide incorrectly states (per this bug) that relative URLs will "automatically" be converted to absolute if you're using Express, but we still get relative URL errors from SSR even after updating our app.server.module.ts to include the necessary setting .useAbsoluteUrl = true (and providing the base URL), which fixes the issue for some per the bug thread:
export class AppServerModule {
constructor(
#Inject(INITIAL_CONFIG) private config: PlatformConfig,
private baseUrlService: BaseUrlService
) {
this.config.baseUrl = this.baseUrlService.getBaseUrl();
this.config.useAbsoluteUrl = true;
}
}
We've also tried implementing a custom HTTP Interceptor to handle the conversion, which doesn't seem to work either when declared as a provider in app.server.module.ts. This must be a misconfiguration issue, right? Our main.server.ts file imports the aspnet-prerendering package and has a default createServerRenderer() method, which calls renderModule(AppServerModule, options), but is something else missing with this approach? The main.server.ts is:
....
export default createServerRenderer(params => {
const { AppServerModule } = (module as any).exports;
const options = {
document: params.data.originalHtml,
url: params.url,
extraProviders: [
{ provide: APP_BASE_HREF, useValue: params.baseUrl },
{ provide: 'BASE_URL', useValue: params.origin }
]
};
// Bypass ssr api call cert warnings in development
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
const renderPromise = renderModule(AppServerModule, options);
return renderPromise.then(html => ({ html }));
});
export { renderModule } from '#angular/platform-server';
export { AppServerModule } from './app/app.server.module';
Microsoft's now-deprecated NodeServices implementation(?) apparently requires the use of commonjs for Node (in tsconfig.server.json), if that's relevant.

How to reference the app instance in a module in Nest.js

I'm working on a project that's using multiple Nest repos, around 4.
Every repo needs to implementing logging to log things like
Server lifecycle events
Uncaught errors
HTTP requests/responses
Ideally, I'd like to package everything up into a module which I can publish to my company's NPM organization and just consume directly in each of my projects.
That way, it would take very minimal code to get logging set up in each project.
One of the things I'd like to log in my server lifecycle event is the server's url.
I know you can get this via app.getUrl() in the bootstrapping phase, but it would be great to have access to the app instance in a module's lifecycle hook like so.
#Module({})
export class LoggingModule implements NestModule {
onApplicationBootstrap() {
console.log(`Server started on ${app.getUrl()}`)
}
beforeApplicationShutdown() {
console.log('shutting down')
}
onApplicationShutdown() {
console.log('successfully shut down')
}
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggingMiddleware).forRoutes('*')
}
}
Is this possible?
There's no way (besides hacky ones, maybe) to access the app itself inside modules.
As you can see here, app.getUrl() uses the underlying HTTP server. Thus I guess you can retrieve the same data using the provider HttpAdapterHost.
Ï thought I'd chime in and offer one of the hacky solutions. Only use this, if there is absolutely no other way or your deadline is coming in an hour.
Create a class that can hold the application instance
export class AppHost {
app: INesApplication
}
And a module to host it
#Module({
providers: [AppHost]
exports: [AppHost]
})
export class AppHostModule {}
In your bootstrap() function, retrieve the AppHost instance and assign the app itself
// after NestFactory.create() ...
app.select(AppHostModule).get(AppHost).app = app;
Now, the actual application will be available wherever you inject AppHost.
Be aware, though, that the app will not be available inside AppHost before the whole application bootstraps (in onModuleInit, onApplicationBootstrap hooks or in provider factories), but it should be available in shutdown hooks.
Not sure is that hacky... I'm using this to prevent the server from starting in case of pending migrations.
// AppModule.ts
export class AppModule implements NestModule {
app: INestApplication;
async configure(consumer: MiddlewareConsumer) {
if (await this.hasPendingMigrations()) {
setTimeout(()=> {
this.logger.error("There are pending migrations!")
process.exitCode = 1;
this.app.close();
}, 1000);
}
//...
}
public setApp(app: INestApplication) {
this.app = app;
}
//...
}
//main.ts
const app = await NestFactory.create(AppModule, {
logger: config.cfgServer.logger,
});
app.get(AppModule).setApp(app);

Storing GET from my Node API to my Angular service

I'm sorry if this has already been asked, I've been through tons of posts and docs, but nothing quite sounds like what I'm trying to do, even if I think that it's rather simple.
I'm building a tiny web app. It has 2 directories : api (node, working on :8080) and app (angular, working on :4200). They work pretty well separately, my database is ready, everything is fine. Thanks to Json Proxy (I don't use CORS), my API routes are working in my Angular app, for instance :
localhost:4200/api/users URL will GET all my users, no problem. I thought I've done the hardest part, but now I'm stuck on one simple thing : How the hell do I use these API routes in TypeScript for my Angular Service. Like, from this service, I want to manipulate the datas already accessible from localhost:4200/api/users. Nothing more. How to use/call this URL/route in code ?
I'm confused by the lack of doc on this matter, sounds like such a basic thing to me. If anyone can point me to the right direction, I'd be glad. Thanks a lot !
Angular and nodejs routes are different. localhost:4200/api/users
//services.service.ts
callapifunction(name: string, pass: string, confirmPass: string): Observable<any> {
const body = new HttpParams()
.set('username', name)
.set('password', pass)
return this.http.post(
`localhsot:8080/api/users`,
body
)
.pipe(
tap(_ => console.log(_)),
catchError(this.handleError<any>('verify'))
);
}
You can call it any component. Thats it. Angular routers declared in router.
//app-routing.module.ts
import {Route} from '#angular/router';
import { HomeComponent } from './home/home.component';
export const routerConfig: Route[] = [
{
path: '',
children:[
//admin
{
path: 'api/users',
component: DashboardComponent
},
{
path: 'main/login',
component: LoginComponent
},
]},
{
path: '**',
redirectTo: '/'
}
];

How to split Nest.js microservices into separate projects?

Let's say I want to create a simplistic cinema-management platform. It needs few microservices: movies, cinemas, payments, etc.
How would you go about doing it in Nest.js? I don't want them in the same big folder as that feels like making a monolith. I want them to be separate Nest.js projects with their own git repositories so I can orchestrate them with Kubernetes later on.
How? How to connect from service cinemas to service movies if they are two separate projects and only share, let's say, Redis?
Edit:
This is not a question about microservices in general. This is a question Nest.js specific. I read the documentation, I know there are decorators like #Client for connecting to the transport layer. I just want to know where to use that decorator and maybe see a short snippet of code on "having two separate Nest.js repositories how to connect them together so they can talk to each other".
I don't care about the transport layer, that thing I can figure out myself. I just need some advice on the framework itself as I believe the documentation is lacking.
I got it working. Basically the way to do it is to create two separate projects. Let's say - one is a createMicroservice and another is just an HTTP app (but could easily be another microservice). I used a "normal" app just so I can call it easily for testing.
Here is the main.ts file that creates microservice.
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { Transport } from '#nestjs/common/enums/transport.enum';
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
},
});
await app.listen(() => console.log('MoviesService is running.'));
}
bootstrap();
And one of the controllers:
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#MessagePattern({ cmd: 'LIST_MOVIES' })
listMovies(): string[] {
return ['Pulp Fiction', 'Blade Runner', 'Hatred'];
}
}
Now - in the microservice you declare to what kinds of events should controllers react to (#MessagePattern). While in the "normal" service you do this in the controller when you want to ask other microservices for something (the main.ts is the simplest example that you get when you create a new project using #nestjs/cli.
The controller code:
#Controller()
export class AppController {
private readonly client: ClientProxy;
constructor(private readonly appService: AppService) {
this.client = ClientProxyFactory.create({
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
},
});
}
#Get()
listMovies() {
const pattern = { cmd: 'LIST_MOVIES' };
return this.client.send<string[]>(pattern, []);
}
}
So as long a client is connected to the same transport layer as the microservice - they can talk to each other by using the #MessagePattern.
For nicer code you can move the this.client part from a constructor to a provider and then use dependency injection by declaring the provider in the module.

App Object in Electron Module and getAppPath throws Error

I have a strange problem with my application. I get an error, and I can't solve it. First at all, I installed a new project, so everything is clean. Someone sent me this repo to use for an Angular, Electron and Nodejs Application. Everything worked fine, but then I decided to install an embedded database like sqlite3. For this I found NeDB, and the module is perfect for my needs. First I had the problem, has nothing to do with my general problem, that I can't create a database file. So I read that I can only create files in my application path, because something about Electron and that's working in a browser.
I found the getAppPath() method that is implemented in my app object from the Electron module. Here starts the problem. For hours I tried to get the application path from this object. Finally, I wrote this code.
import { Injectable } from '#angular/core';
var nedb = require('nedb');
import { app } from 'electron';
import * as path from 'path';
#Injectable()
export class DatabaseService {
app: typeof app;
Database: any;
constructor() {
this.app = window.require("electron").app;
this.Database = new nedb({ filename: path.join(this.app.getAppPath(), '/diary.db'), autoload: true, timestampData: true });
var scott = {
name: 'Scott',
twitter: '#ScottWRobinson'
};
this.Database.insert(scott, function(err, doc) {
console.log('Inserted', doc.name, 'with ID', doc._id);
});
}
}
And I get this error.
I found this posting, but I don't really understand what the post is trying to tell me. I followed the links, but nothing seems to help. Anyone have an idea?

Resources