How to use connection as standalone object with types? - node.js

Not working code just to illustrate what I'm trying to achieve
Some connection file
import { ConnectionManager } from 'typeorm';
const c = new ConnectionManager();
// user ormconfig.conf file
export const connection = c.createAndConnect();
using in some model
#Entity()
#Table("annual_incomes")
export class AnnualIncome
{
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 75 })
variant: string;
#Column("int")
sort: number;
#Column()
is_active: boolean;
}
Later somewhere in the code, I want to get connection with all methods, something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.getRepository(AnnualIncome).find();
});
Usually, I'm getting an error from tsc that .getRepository() method was not found in connection. However if I do something like:
import { connection } from 'someconnection';
import { AnnualIncome } from 'entities';
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await connection.then(async connection => {
return await connection.getRepository(AnnualIncome).find();
}
});
the above code works with definitions and tsc does not complain about not-existing methods.
I'd like to avoid an extra definition connection.then() and get plain connection with all methods defined in <Connection> type.

just use createConnection method to create your connection when you bootstrap your application. Later you can access your connection from anywhere using getConnection() method:
import { AnnualIncome } from 'entities';
import { createConnection, getConnection } from 'typeorm';
// somewhere in your app, better where you bootstrap express and other things
createConnection(); // read config from ormconfig.json or pass them here
// some code here
api.get('/incomes', async(ctx) => {
ctx.body = await getConnection().getRepository(AnnualIncome).find();
});
Also you can simply use getRepository method also avalible from anywhere:
import { AnnualIncome } from 'entities';
import { getRepository } from 'typeorm';
// some code here
api.get('/incomes', async (ctx) => {
ctx.body = await getRepository(AnnualIncome).find();
});

Related

Error: Cannot get orders: TypeError: Cannot read properties of undefined (reading 'connect')

I'm trying to build an API with express and pg. Whenever I try to access an endpoint that is related to a query to the database I get the error above.
My handler function is as follows:
import { Request, Response, Router, NextFunction } from 'express';
import { Orders } from '../models/order';
const orders = new Orders;
const index = async (_req: Request, res: Response, next: NextFunction) => {
try {
const ordersList = await orders.index();
res.json(ordersList);
} catch (err) {
next(err)
}
}
const ordersRoute = Router();
ordersRoute.get('/', index);
This handler refers to the following model:
import { Pool } from 'pg';
client = new Pool({
host: POSTGRES_HOST,
database: POSTGRES_DB,
user: POSTGRES_USER,
password: POSTGRES_PASSWORD,
port: parseInt(POSTGRES_PORT as string, 10)
export class Orders {
async index(): Promise<Order[]> {
try {
const conn = await client.connect();
const sql = 'SELECT * FROM orders';
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Cannot get orders: ${err}`);
}
}
}
anytime I try to access the endpoint I get
Error: Cannot get orders: TypeError: Cannot read properties of
undefined (reading 'connect')
in the console.
any idea how to fix ?
So how things work in your case.
All modules are read by nodejs starting from your index one by one from top to bottom.
In your script, you declare client and then export a class. In that case, your client is setup, then you export a class, and that file is completed, meaning that the only thing that remains is the exported thing. So when you try to use the exported class, you'll not have the same context as in your module.
You need to export the client too and import it where you need that class or to include client definition inside the class

Mocha, Supertest and Mongo Memory server, cannot setup hooks properly

I have a Nodejs, Express server that uses Mongodb as the Database. I am trying to write some tests but I cannot get it configured properly. I need to create a mongoose connection for each block ( .test.ts file) once and then clean up the db for the other tests. Depending on how I approach this I get two different behaviours. But first my setup.
user.controller.test.ts
import { suite, test, expect } from "../utils/index";
import expressLoader from "#/loaders/express";
import { Logger } from "winston";
import express from "express";
import request from "supertest";
import { UserAccountModel } from "#/models/UserAccount";
import setup from "../setup";
setup();
import { app } from "#/server";
let loggerMock: Logger;
describe("POST /api/user/account/", function () {
it("it should have status code 200 and create an user account", async function () {
//GIVEN
const userCreateRequest = {
email: "test#gmail.com",
userId: "testUserId",
};
//WHEN
await request(app)
.post("/api/user/account/")
.send(userCreateRequest)
.expect(200);
//SHOULD
const cnt: number = await UserAccountModel.count();
expect(cnt).to.equal(1);
});
});
And my other test post.repository.test.ts
import { suite, test, expect } from "../../utils/index";
import { PostModel } from "#/models/Posts/Post";
import PostRepository from "#/repositories/posts.repository";
import { PostType } from "#/interfaces/Posts";
import { Logger } from "winston";
#suite
class PostRepositoryTests {
private loggerMock: Logger;
private SUT: PostRepository = new PostRepository({});
#test async "Should create two posts"() {
//GIVEN
const given = {
id: "jobId123",
};
//WHEN
await this.SUT.CreatePost(given);
//SHOULD
const cnt: number = await PostModel.count();
expect(cnt).to.equal(1);
}
}
And my setup
setup.ts
import { MongoMemoryServer } from "mongodb-memory-server";
import mongoose from "mongoose";
export = () => {
let mongoServer: MongoMemoryServer;
before(function () {
console.log("Before");
return MongoMemoryServer.create().then(function (mServer) {
mongoServer = mServer;
const mongoUri = mongoServer.getUri();
return mongoose.connect(mongoUri);
});
});
after(function () {
console.log("After");
return mongoose.disconnect().then(function () {
return mongoServer.stop(true);
});
});
};
With the above setup I get
1) "before all" hook in "{root}":
MongooseError: Can't call `openUri()` on an active connection with different connection strings. Make sure you aren't calling `mongoose.connect()` multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections
But if I don't import the setup.ts and I rename it to setup.test.ts, then it works but the data isn't cleared after each run so I actually have 10 new users created instead of 1. Every time I run it, it works and doesn't clear the data after it's finished.
Also I have a big issue where the tests hang and don't seem to finish. I am guessing that is because of the async, await in the tests or because of the hooks hanging.
What I want to happen is:
Each test should have it's own setup, the mongo memory server should be clean every time.
The tests should use async and await and not hang.
Somehow export the setup from 1) as a utility function so that I can reuse it in my code

How to create a custom health check for Prisma with #nestjs/terminus?

Since #nestjs/terminus doesn't provide a health check for Prisma, I'm trying to create it based on their Mongoose health check.
When I try:
import * as Prisma from 'prisma';
...
...
private getContextConnection(): any | null {
const {
getConnectionToken,
// eslint-disable-next-line #typescript-eslint/no-var-requires
} = require('prisma') as typeof Prisma;
try {
return this.moduleRef.get(getConnectionToken('DatabaseConnection') as string, {
strict: false,
});
} catch (err) {
return null;
}
}
...
...
const connection = options.connection || this.getContextConnection();
if (!connection) {
throw new ConnectionNotFoundError(
this.getStatus(key, isHealthy, {
message: 'Connection provider not found in application context',
}),
);
}
I always seem to get: "message": "Connection provider not found in application context".
There is a problem with the connection or I don't really understand how the health check actually works
This question helped me build a Prisma health check for NestJS.
Here's what I made:
import { Injectable } from "#nestjs/common";
import { HealthCheckError, HealthIndicator, HealthIndicatorResult } from "#nestjs/terminus";
import { PrismaService } from "./prisma.service";
#Injectable()
export class PrismaHealthIndicator extends HealthIndicator {
constructor(private readonly prismaService: PrismaService) {
super();
}
async isHealthy(key: string): Promise<HealthIndicatorResult> {
try {
await this.prismaService.$queryRaw`SELECT 1`;
return this.getStatus(key, true);
} catch (e) {
throw new HealthCheckError("Prisma check failed", e);
}
}
}
This injects a PrismaService exactly as it is shown in the NestJS docs. https://docs.nestjs.com/recipes/prisma#use-prisma-client-in-your-nestjs-services
You could alternatively replace prismaService with new PrismaClient().
A naive copy of the mongoose implementation isn't going to work because there are differences between the NestJSMongoose type/module and Prisma. In particular, getConnectionToken does not exist inside the Prisma package.
I can't comment on what the best way would be to extend terminus to support prisma. You might have to dig a bit into the terminus interface for that. However, a simple way to get a health check/ping in Prisma is to use the following query:
prisma.$queryRaw`SELECT 1`

Nodejs Mongoose 'Operation `XXX.find()` buffering timed out after 10000ms'

index.ts is the entry point of this NodeJS program.
This is the code in index.ts:
import JobWorker from "./worker";
import { SwitchPlan } from "./jobs";
const worker = new JobWorker();
worker.addJob(SwitchPlan);
This is worker.ts:
import { CronJob } from "cron";
import mongoose from "mongoose";
import Config from "./config";
import logger from "./logger";
export default class JobWorker {
private jobs: CronJob[];
private config: {
NAME: string;
MONGO_URL: string;
};
constructor() {
this.config = Config;
this.connectDB();
this.jobs = [];
}
public async connectDB(): Promise<void> {
try {
await mongoose.connect(this.config.MONGO_URL,
{ useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true },
);
logger.info("\nMONGODB has been connected\n");
} catch(err) {
logger.error("ERROR occurred while connecting to the database");
}
}
addJob(cronJob: CronJob) {
this.jobs.push(cronJob);
}
}
This is jobs.ts:
import moment from "moment";
import {
DatabaseOperations, Vehicle,
Plan1Doc, Plan1, VehicleDoc
} from "common-lib";
import logger from "../logger";
import { CronJob } from "cron";
const vehicleOps = new DatabaseOperations(Vehicle);
const SwitchPlan = new CronJob("25 * * * * *", async (): Promise<void> => {
const date: Date = moment(new Date()).startOf("date").toDate();
const expiringVehicles: VehicleDoc[] = vehicleOps.getAllDocuments(
{ "inspection.startTime": {
"$gte": date, "$lte": moment(date).startOf("date").add(1, "day").toDate()
}
},
{}, { pageNo: 0, limit: 0 }
).then((result: any) => {
logger.info("dsada");
}).catch((err: any) => {
logger.info("ssd");
});
});
SwitchPlan.start();
export { SwitchPlan };
I have omitted parts of code which are irrelevant to this problem. I ran this code through a debugger and there's no issue with the config. MonggoDB connected is getting printed at the start of the program. However the then block after getAllDocuments in jobs.ts is never reached and it always goes in the error block with the message, Operation vehicleinventories.find() buffering timed out after 10000ms. The getAllDocuments uses MongoDB's find() method and is working correctly because I am using this method in other projects where I have no such issues.
So far I have tried, deleting Mongoose from node_modules and reinstalling, tried connecting to MongoDB running on localhost, but the issue remains unsolved.
EDIT: DatabaseOperations class:
import { Model, Schema } from "mongoose";
class DatabaseOperations {
private dbModel: Model<any>;
constructor(dbModel: Model<any>) {
this.dbModel = dbModel;
}
getAllDocuments(
query: any,
projections: any,
options: { pageNo: number; limit: number },
sort?: any
): any {
const offset = options.limit * options.pageNo;
return this.dbModel
.find(query, projections)
.skip(offset)
.limit(options.limit)
.sort(sort ? sort : { createdAt: -1 })
.lean();
}
}
in your jobs.ts file you have the following line
SwitchToTier1Plan.start();
This line is called the moment you required the class file, hence before mongoose is connected, and all the models defined. Could this be the issue?
Another thing I noted is u are using mongoose.connect which may be wrong since mongoose.connect creates a global connection.
which means each new Worker you will be attempting to override the mongoose property with previous connection
Though i'm not sure what the implication is, but it could be because your .find could be using the old connection.
Since you are writing class, I would recommend using mongoose.createConnection which creates a new connection for each class initiation.

NestJS testing with Jest custom repository (CassandraDB)

The code I am trying to test the driver / repository for my nodeJS project:
import { Injectable, OnModuleInit } from '#nestjs/common';
import { mapping, types } from 'cassandra-driver';
import { Products } from './poducts.model';
import { CassandraService } from '../database/cassandra/cassandra.service';
import Uuid = types.Uuid;
#Injectable()
export class ProductsRepository implements OnModuleInit {
constructor(private cassandraService: CassandraService) {}
productsMapper: mapping.ModelMapper<Products>;
onModuleInit() {
const mappingOptions: mapping.MappingOptions = {
models: {
Products: {
tables: ['products'],
mappings: new mapping.UnderscoreCqlToCamelCaseMappings(),
},
},
};
this.productsMapper = this.cassandraService
.createMapper(mappingOptions)
.forModel('Products');
}
async getProducts() {
return (await this.productsMapper.findAll()).toArray(); // <-----Breaks here with findAll()
}
}
I am trying to write something like this:
describe('product repository get all', () => {
it('calls the repository get all', async () => {
const await productsRepository.getProducts();
expect().DoSomething()
});
});
This is the error I am getting:
Cannot read property 'findAll' of undefined
How would I accomplish a meaning-full test with Jest to get proper code coverage?
When I try to use jest to spy on the this.products.Mapper.findAll() it seems to break every time.

Resources