How to change PrismaClient database connection at runtime? - javascript

I have .env file like
DATABASE_URL="sqlserver://srv:50119;initial catalog=mydb;user=aaa;password=bbb;"
and then schema.prisma like
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["microsoftSqlServer"]
}
I generate a client using:
npx prisma generate
and then Prisma works great in my express app using:
const prisma = new PrismaClient();
Say I wanted to use a different db for user for multi-tenancy, how can I achieve this? Ideally I'd want to switch the db connection at runtime but it seems that DATABASE_URL is only read during prisma generate and not at runtime so the generated client ends up with a hardcoded db url.

You can use the datasource property to create a new PrismaClient instance and pass a dynamic URL.
datasources
Programmatically overrides properties of the datasource block in the schema.prisma file - for example, as part of
an integration test. See also: Data sources

Related

Swagger UI always shows up in NestJS

I am currently learning my way around in NestJS. Now I am experimenting with the Swagger feature. I followed the explanations on the NestJS site and I am getting a nice Swagger page displayed. However, now I am no longer able to use my controller pathes.
Example:
I have a path /api/users that will return a list of user records. After adding the Swagger feature I get the Swagger UI on /api. When I try to request /api/users I also get the swagger UI, this time empty.
When I click the "Try it out" button for the "user" API /users instead of /api/users will be executed, of course with a 404 response.
What am I doing wrong? Please help.
I'm assuming you have set the global prefix of your app to /api. Then you also have to set the base path for swagger accordingly. Also, you should mount your swagger docs to an unused path:
// Set the global prefix for all controllers to /api
app.setGlobalPrefix('api');
const document = SwaggerModule.createDocument(
app,
new DocumentBuilder()
// Set the base path for swagger accordingly
.setBasePath('api')
.build(),
);
// Choose an unused path to mount your swagger module
SwaggerModule.setup('docs', app, document);
// ^^^^^^
I was able to achieve this by below code:
app.setGlobalPrefix("api");
setupSwagger(app);
app.setGlobalPrefix("");
setupSwagger definition:
export const setupSwagger = (app: INestApplication) => {
const options = new DocumentBuilder()
.setTitle(SWAGGER_API_NAME)
.setDescription(SWAGGER_API_DESCRIPTION)
.setVersion(SWAGGER_API_CURRENT_VERSION)
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup(SWAGGER_API_ROOT, app, document);
};

create script to insert seed data into mongodb in node.js

I'm using mongoose and node.js (express), and I wish to insert seed data using script. Like when I do node scripts/createNotifications.js I can insert a data into my db.
My code
//createNotifications.js
const mongoose = require('mongoose')
const Notification = require('../api/models/notificationModel')
mongoose.Promise = global.Promise
module.exports = (async () => {
try {
const new_notification = await new Notification({
"userId" : mongoose.Types.ObjectId("5a3e76ce914e1d1bd854451d"),
"msg" : "Something"
}).save()
} catch(e) {
console.log('Error creating notifications. ', e)
}
})()
When I run the code I don't see any data been inserted. I have my server started in port 3000, do I have to connect to mongodb too in this file? since this file has nothing to do with my express app, it's just a separated file.
If you want to see this module running make sure the following
Make sure you've made connection with the database like mongoose.connect('mongodb://IP/DBName')
What you've posted above is just a module definition. It won't execute on its own. You'll have to require this module in your mail file, the file you're running with node for example node server.js and call the method. Something like
var notification = require(path/to/createNotifications);
notification();

Send data on configuration

I want to send asynchronous data to the node on configuration. I want to
perform a SQL request to list some data in a .
On node creation, a server side function is performed
When it's done, a callback send data to the node configuration
On node configuration, when data is received, the list is created
Alternatively, the binary can request database each x minutes and create a
cache that each node will use on creation, this will remove the asynchronous
part of code, even if it's no longer "live updated".
In fact, i'm stuck because i created the query and added it as below :
module.exports = function(RED) {
"use strict";
var db = require("../bin/database")(RED);
function testNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
// Store local copies of the node configuration (as defined in the
.html
var node = this;
var context = this.context();
this.on('input', function (msg) {
node.send({payload: true});
});
}
RED.nodes.registerType("SQLTEST",testNode);
}
But I don't know how to pass data to the configuration node. I thought of
Socket.IO to do it, but, is this a good idea and is it available? Do you know any solution ?
The standard model used in Node-RED is for the node to register its own admin http endpoint that can be used to query the information it needs. You can see this in action with the Serial node.
The Serial node edit dialog lists the currently connected serial devices for you to pick from.
The node registers the admin endpoint here: https://github.com/node-red/node-red-nodes/blob/83ea35d0ddd70803d97ccf488d675d6837beeceb/io/serialport/25-serial.js#L283
RED.httpAdmin.get("/serialports", RED.auth.needsPermission('serial.read'), function(req,res) {
serialp.list(function (err, ports) {
res.json(ports);
});
});
Key points:
pick a url that is namespaced to your node type - this avoids clashes
the needsPermission middleware is there to ensure only authenticated users can access the endpoint. The permission should be of the form <node-type>.read.
Its edit dialog then queries that endpoint from here: https://github.com/node-red/node-red-nodes/blob/83ea35d0ddd70803d97ccf488d675d6837beeceb/io/serialport/25-serial.html#L240
$.getJSON('serialports',function(data) {
//... does stuff with data
});
Key points:
here the url must not begin with a /. That ensures the request is made relative to wherever the editor is being served from - you cannot assume it is being served from /.

Using NodeJS with Express 3.x and Jade templates is it possible to just re-render one item for a previously rendered list?

I have been trying to find any post that can explain if it is possible to re-render one 'new' item (append) to a jade template list.
Say that we have a list of log-entries and upon first request we render a fetched list from a MongoDB collection 'logs', using res.render and Jades each functionality.
Since we like to retrieve updates from the database we also have a MongoWatch attached to that collection that listens for changes. Upon update can we execute some code that appends to that first list in the Jade-template?
/* app.js */
/*
Display server log
*/
app.get ('/logs', function(req, res, next) {
// Using Monk to retrieve data from mongo
var collection = db.get('logs');
collection.find({}, function(e,docs){
// watch the collection
watcher.watch('application.logs', function(event){
// Code that update the logs list with the new single entry event.data?
});
// Request resources to render
res.render('logs', { logs: docs } );
});
});
<!-- logs.jade -->
extends layout
block content
div
each log in logs
div.entry
p.url= log.url
Maybe i should use the template engine in another fashion, i am quite new to Express, Jade and really appreciate all you guys that spends your time answering problems like these..
// Regards
Ok, so i have looked up the suggestion from Jonathan Lenowski, thanks by the way!, and i came up with a solution to my problem. Thought i'd follow up and perhaps help someone else along the way..
Basically i am now using as suggested socket.io
So first install the socket.io npm module by adding it to package.json and run npm install, i used 'latest' as version.
Next to use the 'socket.io.js' on the client-side you actually have to copy the file from the installed socket.io module to your javascript folder.
Path (seen from project root is): 'node_modules/socket.io/node_modules/socket.io-client/dist/'
Setup DB, Watcher, Webserver, Socket and controller on server-side
/*
SETUP DATABASE HANDLE
in app.js
*/
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:'+app.get('port')+'/application');
/* SETUP DATABASE UPDATE WATCH */
var watcher = new MongoWatch({ format: 'pretty', host: 'localhost', port: app.get('port') });
/* START WEBSERVER AND SETUP WEBSOCKET */
var server = Https.createServer({key: certData.serviceKey, cert: certData.certificate}, app);
var io = require('socket.io').listen(server);
server.listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
/*
Display server log - controller
*/
app.get ('/logs', function(req, res, next) {
// Using Monk to retrieve data from mongo
var collection = db.get('logs');
collection.find({}, function(e,docs){
// watch the collection logs in database application
watcher.watch('application.logs', function(event){
io.sockets.emit('logs', { log: event.data });
});
// Request resources to render
res.render('logs', { logs: docs } );
});
});
Include the socket.io javascript in layout
/*
Add client side script
in layout.jade
*/
script(type='text/javascript' src='/javascripts/socket.io.js')
Use the client
/*
SETUP DATABASE HANDLE
in logs.jade
*/
extends layout
block content
script.
var socket = io.connect('https://localhost:4431');
socket.on('logs', function (data) {
console.log(data.log);
// Here we use javascript to add a .log-entry to the list
// This minor detail i leave to the developers own choice of tools
});
div.row#logs
div.col-sm-12
div.header-log Some application
div.logs-section
each log in logs
div.log-entry.col-sm-12(data-hook=log.status)
p.method= log.method
p.url= log.url
p.status(style='color: #'+log.color+' !important')= log.status
p.response-time= log.time
p.content-length= log.length
p.datetime= log.date
Use the functionality, remember that this flow is triggered by actually adding a row in the database 'application' and the collection 'logs'.
I use ssl thus with regular http we create a 'http' server instead and connect from the client with a standard address prefix of http://...
Also as an additional note, in order to use MongoWatch it is required of you to setup the MongoDB with replication set. Which is a mirror database that can be used as a fallback (dual purpose).
Cheers! And once again thanks to Jonathan!

Express JS Integration testing with Supertest and mock database

Is it possible to test an Express JS REST API using supertest but replacing the actual database connection with a mock database object? I have unit tests covering the database models and other parts of the application as well as functional tests of the API endpoints making actual database connections, but I have a weird requirement to create integration tests that are like the functional tests but use mock database connections. A sample endpoint controller is below:
var model = require('../../../lib/models/list');
module.exports = {
index: function(req, res) {
var data = { key: 'domains', table: 'demo.events'};
var dataModel = new model(data);
dataModel.query().then(function(results) {
res.respond({data: results}, 200);
}).fail(function(err) {
console.log(err);
res.respond({message: 'there was an error retrieving data'}, 500);
});
}
};
And the index for the URI is
var express = require('express'), app, exports;
app = exports = module.exports = express();
exports.callbacks = require('./controller');
app.get('/', exports.callbacks.index);
The list model used in the controller connects to the database and retrieves the data that is output. The challenge is mocking that actual database call while still using supertest to make the request and retrieve the data from the URI
Any information would be helpful including if you think this is a bad or pointless idea
I have had limited success with 2 approaches:
1) use rewire to replace the database driver library like mongodb with a mocked one, perhaps using the spy/stub/mock capabilities of sinon
2) Set your db as an app setting via app.set('mongodb', connectedDb) for dev/prod but in test environment set a mock database instead. This requires your db-accessing code (models typically) to get the DB from the app, or otherwise be mock-friendly or designed with a dependency injection pattern.
Neither of these make everything clean and painless, but I have gotten some utility out of them.

Categories

Resources