How to run schema creation sql script in mssql with nodejs? - sql-server

I have my code deployed in Azure Function App and using NodeJS I am trying to execute a sql script on SQL Server database. To work this out I have used NPM package sqlcmd-runner.
Here is the code I am using:
module.exports.dumpScriptinDatabase = (dbConfig, context) => {
return new Promise((resolve, reject) => {
sqlcmd({
server: dbConfig.server,
database: dbConfig.options.database,
username: dbConfig.userName,
password: dbConfig.password,
encryptedConnection: true,
inputFiles: [`${__dirname}\\db.sql`]
})
.catch((error) => {
reject('Error dumping script!!')
})
.done(() => {
resolve('Dumping script completed.')
});
});
I am getting all the database details in dbConfig variable. Also db.sql creates the tables, views etc. in the schema.
The issue I am facing is this module never returns me error in case of failure and it also does not executes successfully. The server returns 502 error code on execution.
One thing I am able to debug is that the npm package code uses sqlcmd to execute the script.
Can someone help me on this?

So I've tested this using several source files, it's working ok, scripts are run successfully.
index.js
const dbConfig = require('./dbconfig.json');
const dumpScript = require('./dumpScript.js');
console.log('Running script');
dumpScript.dumpScriptinDatabase(dbConfig, 'context').then(() => {
console.log('Script complete');
}).catch ( (err) => {
console.error('Script error: ', err);
});
dumpScript.js
const sqlcmd = require('sqlcmd-runner');
module.exports.dumpScriptinDatabase = (dbConfig, context) => {
return sqlcmd({
server: dbConfig.server,
database: dbConfig.options.database,
username: dbConfig.userName,
password: dbConfig.password,
encryptedConnection: true,
inputFiles: [`${__dirname}\\db.sql`]
});
}
dbConfig.json
{
"server": "sql_server",
"options": {
"database": "test_db"
},
"userName": "user",
"password": "password"
}
I've simplified the dumpScript.js file, we can simply return the promise from the sqlcmd function rather than using .then and .catch.
We then handle this in index.js.
A couple of things I'd mention, you need the sqlcmd utility of a fairly recent version on PATH for this to work.
Also, you might need to modify the index.js script, I'm loading dbConfig from a .json file, you're probably doing something different.

Related

Connect to an SQL Server RDS database from a Lambda function (SAM App)?

var sql = require("mssql");
const config = {
user: 'username',
password: 'password',
server: 'mssql-instance.cmujwb4lclgy.us-east-1.rds.amazonaws.com',
database: 'mssql-instance',
port: 1433
};
exports.clock = async(event) => {
console.log('Connecting to the rds..')
let message = "Not Connected"
sql.connect(config, function (err) {
if (err) console.log(err);
let sqlRequest = new sql.Request();
let sqlQuery = "SELECT * from InexistantTable where InexistantId = 0;"
message = "Connected"
sqlRequest.sqlQuery(sqlQuery, function(err, data) {
if (err) console.log(err)
console.table(data.recordset);
message = "Connected, attempted query."
sql.close();
});
});
const response = {
statusCode: 200,
body: JSON.stringify("SQL Server: " + message)
}
return response;
}
I get this log
2022-01-25T21:47:01.796Z c3c94b7f-92a9-4b90-8cc4-fe208fff611a INFO ConnectionError: Failed to connect to mssql-instance.cmujwb4lclgy.us-east-1.rds.amazonaws.com:1433 in 15000ms
at /var/task/node_modules/mssql/lib/tedious/connection-pool.js:71:17
at Connection.onConnect (/var/task/node_modules/tedious/lib/connection.js:1043:9)
at Object.onceWrapper (events.js:520:26)
at Connection.emit (events.js:400:28)
at Connection.emit (/var/task/node_modules/tedious/lib/connection.js:1071:18)
at Connection.connectTimeout (/var/task/node_modules/tedious/lib/connection.js:1530:10)
at Timeout._onTimeout (/var/task/node_modules/tedious/lib/connection.js:1475:12)
at listOnTimeout (internal/timers.js:557:17)
at processTimers (internal/timers.js:500:7) {
code: 'ETIMEOUT',
originalError: ConnectionError: Failed to connect to mssql-instance.cmujwb4lclgy.us-east-1.rds.amazonaws.com:1433 in 15000ms
at ConnectionError (/var/task/node_modules/tedious/lib/errors.js:13:12)
at Connection.connectTimeout (/var/task/node_modules/tedious/lib/connection.js:1530:54)
at Timeout._onTimeout (/var/task/node_modules/tedious/lib/connection.js:1475:12)
at listOnTimeout (internal/timers.js:557:17)
at processTimers (internal/timers.js:500:7) {
code: 'ETIMEOUT'
}
}
Its in a default VPC so I thought I wouldn't need to do any security group or roles. Looking into this is quite confusing too.
If I do have to set it up, where exactly do I do it? In the VPC dashboard? Directly in the Lambda function or on the RDS Instance itself? And do I associate it with the RDS or the Lambda? Inbound or Outbound?
Also, I am using the database instance name to connect. I used another guide to create a MySQL RDS and there was an place where I also gave it a database name too, which is the one I used to connect to, but for SQL Server RDS I had no such option when setting up the express standard configuration database.
You need to make a role for the lambda. Go to(console) IAM->Roles->Create role->(select)Lambda->(in permissions)AWSLambdaVPCAccessExecutionRole->tags->name..etc.
Your 'Trust relationships'(after you create the role) should be similar to
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Now go to your lambda and in Configuration tab, edit Execution role and give arn of the role you created.
Edit: Now in the Configuration tab, go to VPC->Edit->select vpc (where your RDS is situated)->select subnet(where your RDS is situated)->select a security group(select any, maybe the one attached to the DB)*->Save.
If I am not mistaken any security group will be fine, Lambda doesn't need whitelisting.
Further details can be found here. I hope this solves your issue.
I ended up making it work. The problem was not Security Group or VPC. It was the async part. Since i was returning the response to the Lambda before the connection was made, it never knew it succeeded so it timed out
I had to make sure the connection was established first, but my code feels really messy. I would like to improve it
const sql = require('mssql');
exports.mssql = (event, context, callback) => {
sql.connect({
user: 'user',
password: 'pass',
server: 'host',
database: 'testdb',
port: 1433,
},
err => {
if (err) {
console.log("Fail")
callback('connect failed', err);
} else {
let sqlRequest = new sql.Request();
let sqlQuery = "SELECT * from TestTable;"
sqlRequest.query(sqlQuery, function(err, data) {
if (err) {
console.log(err)
}
console.table(data.recordset);
sql.close();
});
callback(null, sqlRequest);
response = {
statusCode: 200,
body: JSON.stringify({
message: "Query returned",
})
}
return response;
}
})
}

expo-sqlite don't keep data inserted in react native

i create a program that save data to database in react native but when reopen expo and run again program
database were deleted and create new empty database
async save() {
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
}
await FileSystem.downloadAsync(
Asset.fromModule(require('../assets/factor.db')).uri,
FileSystem.documentDirectory + 'SQLite/factor.db'
);
const db = SQLite.openDatabase('factor.db');
db.transaction((tx) => {
tx.executeSql("INSERT INTO factors (created_at,customer,sender,pay,off,description,customer_id)
values(?,?,?,?,?,?,1)",
[this.state.date, this.state.customer, this.state.sender, this.state.payment, this.state.offset,
this.state.description],
(trans, rows) => {
alert('factor added');
console.log(rows);
db._db.close();
},
(err, msg) => {
console.log(msg);
});
tx.executeSql(
"SELECT * FROM factors",
[],
(trans, res) => {
console.log(res.rows);
db._db.close();
},
(err, msg) => {
console.log(msg);
});
}, null);
}
and after run select sql return one row and dont show prev rows added before
Responding as I had this issue last week and it took me a while to find the answer. When running expo on my device the database changes are made to the local database on the device, not the pre-populated or blank database included as an asset in my code. Reloading the app without re-running "expo start" preserves the database on the device but stopping the server and re-running "expo start" will initiate a new app build which will overwrite any changes made in previous instances.

Can't figure out where to initiate CronJob in react app

I have a react app, which must perform a weekly task every Monday #7:58 am. The task is setup as a separate function "notification()". And I want to use the 'CRON' package from NPM to call notification() at the appropriate time.
I have CRON wrapped inside of a function like this:
let mondayNotif = () => {
new CronJob('* 58 7 * * 2', function() {
notification()
}, null, true, 'America/Los_Angeles');
};
My question: where should I call the function mondayNotif(), to make sure that the CronJob is initiated correctly? I thought at first it must be on the backend, but the NPM package doesn't seem to support server-side. But if I call mondayNotif() on the client side, will the CronJob still happen if the site is inactive?
From what I know React JS is front end - it runs on client side. You need a server. In this case a node.js based server. Theroetically if nobody opens the website nothing will be fired up in react js. Look up how to schedule cron jobs on node.js
enter link description here
I found my own answer. But first, a few insights on CronJobs that would have helped me:
CronJobs are essentially a third-party function with an embedded clock. Once they are "initiated", you don't have to call them. The third-party calls them remotely, based on the time that you scheduled in the parameters (ie: "30 6 * * 5").
There is some discrepancy in different blogs about the CRON time. For instance some blogs insisted there are 6 time variables, but I found it worked with 5.
CronJobs should be in a separate file from the body of your main code, typically at the top of your folder structure near your "package.json" & "server.js" files.
It seems to be cleanest to setup all of your CronJob utilities directly inside the cronjob.js file. For instance: I used a separate database connection directly in cronjob.js and by-passed the api routes completely.
CronJobs should be initiated exactly once, at the beginning of the app launch. There are a few ways to do this: package.json or server.js are the most obvious choices.
Here is the file structure I ended up using:
-App
--package.json
--server.js
--cronjob.js
--/routes
--/src
--/models
--/public
...And then I imported the cronjob.js into "server.js". This way the cronjob function is initiated one time, when the server.js file is loaded during "dev" or "build".
For reference, here's the raw cronjob.js file (this is for an email notification):
const CronJob = require('cron').CronJob;
const Department = require('./models/department.js');
const template_remind = require('./config/remindEmailTemplate.js');
const SparkPost = require('sparkpost');
const client = new SparkPost('#############################');
const mongoose = require("mongoose");
const MONGODB_URI =
process.env.MONGODB_URI || "mongodb://localhost:27017/app";
mongoose.Promise = Promise;
// -------------------------- MongoDB -----------------------------
// Connect to the Mongo DB
mongoose.connect(MONGODB_URI, { useNewUrlParser: true }, (err, db) => {
if (err) {
console.log("Unable to connect to the mongoDB server. Error:", err);
} else {
console.log("Connection established to", MONGODB_URI);
}
});
const db = mongoose.connection;
// Show any mongoose errors
db.on("error", error => {
console.log("Mongoose Error: ", error);
});
// Once logged in to the db through mongoose, log a success message
db.once("open", () => {
console.log("Mongoose CRON connection successful.");
});
// ------------------------ Business Logic --------------------------
function weekday(notifications) {
Department.find({"active": true, "reminders": notifications, "week": {$lt: 13}}).distinct('participants', function(err, doc) {
if(err){
// console.log("The error: "+err)
} else {
console.log("received from database... "+JSON.stringify(doc))
for(let i=0; i<doc.length; i++){
client.transmissions.send({
recipients: [{address: doc[i]}],
content: {
from: 'name#sparkmail.email.com',
subject: 'Your email notification',
html: template_remind()
},
options: {sandbox: false}
}).then(data => {})
}
}
})
}
function weeklyNotif() {
new CronJob('45 7 * * 1', function() {weekday(1)}, null, true, 'America/New_York');
new CronJob('25 15 * * 3', function() {weekday(2)}, null, true, 'America/New_York');
new CronJob('15 11 * * 5', function() {weekday(3)}, null, true, 'America/New_York');
}
module.exports = weeklyNotif()
As you can see, I setup a unique DB connection and email server connection (separate from my API file), and ran all of the logic inside this one file, and then exported the initiation function.
Here's what appears in server.js:
const cronjob = require("./cronjob.js");
All you have to do here is require the file, and because it is exported as a function, this automatically initiates the cronjob.
Thanks for reading. If you have feedback, please share.
Noway, do call CronJob from client-side, because if there are 100 users, CronJob will be triggered 100 times. You need to have it on Server-Side for sure

How to connect to SQL Server with windows authentication from Node.JS using mssql module

Hi I'm unable to connect to SQL server that is using windows authentication in node js. I'm using the mssql module. The error message is :
[ConnectionError: Login failed for user ''. The user is not associated with a trusted SQL Server connection.]
name: 'ConnectionError',
message: 'Login failed for user \'\'. The user is not associated with a trusted SQL Server connection.',
code: 'ELOGIN' }
Here is my code:
config = {
server : "localhost\\MSSQLSERVER",
database : "mydatabase",
port : 1433
}
function loadDepts() {
var conn = new sql.Connection(config);
var request = sql.Request(conn);
conn.connect(function(err) {
if (err) {
console.log(err);
return;
}
request.query("select deptid, deptname from departments", function(err, table) {
if (err) {
console.log(err);
return;
}
else {
console.log(table);
}
conn.close();
});
});
}
loadDepts();
Since this is a fairly visible answer, I wanted to add in a code snippet that worked for me with Trusted Connection. Got to it from getglad's edited answer.
const sql = require("mssql");
require("msnodesqlv8");
const conn = new sql.Connection({
database: "db_name",
server: "server_name",
driver: "msnodesqlv8",
options: {
trustedConnection: true
}
});
conn.connect().then(() => {
// ... sproc call, error catching, etc
// example: https://github.com/patriksimek/node-mssql#request
});
Using trusted connection, I was able to execute stored procedures, log the output, and close the connection without any trouble, and msnodesqlv8 has been updated more recently than any of the other drivers (latest release was October 2016 as of 11/3/2016), so that seems to be a safe choice as well.
And here's an example using mssql#4.0.4. The only changes are the initial require, which pull in msnodesqlv8 from within mssql, and sql.Connection is now sql.ConnectionPool. You will also need to change your stored procedure calls since the response is different, noted here. Credit to Jon's answer since he updated mine before I did!
const sql = require("mssql/msnodesqlv8");
const conn = new sql.ConnectionPool({
database: "db_name",
server: "server_name",
driver: "msnodesqlv8",
options: {
trustedConnection: true
}
});
conn.connect().then(() => {
// ... sproc call, error catching, etc
// example: https://github.com/patriksimek/node-mssql#request
});
I have been struggling too for some time about how to use mssql + Windows Auth, here is how i got it to work on my project.
As pointed out in the mssql documentation, you need msnodesqlv8 installed too.
npm install msnodesqlv8
Now, following on Aaron Ballard's answer, you use it like this:
const sql = require('mssql/msnodesqlv8')
const pool = new sql.ConnectionPool({
database: 'database',
server: 'server',
driver: 'msnodesqlv8',
options: {
trustedConnection: true
}
})
pool.connect().then(() => {
//simple query
pool.request().query('select 1 as number', (err, result) => {
console.dir(result)
})
})
As a note, i tried to add this as a comment on Aaron's answer, as mine is just a complement/update to his, but i don't have enough reputation to do so.
I have never been able to get mssql + windows auth to work for any of my projects. Try edge and edge-sql - it has worked for me. Be sure you install all the required packages.
https://github.com/tjanczuk/edge
https://github.com/tjanczuk/edge-sql
From there, it's pretty steamlined.
var edge = require('edge');
var params = {
connectionString: "Server=YourServer;Database=YourDB;Integrated Security=True",
source: "SELECT TOP 20 * FROM SampleData"
};
var getData = edge.func( 'sql', params);
getData(null, function (error, result) {
if (error) { console.log(error); return; }
if (result) {
console.log(result);
}
else {
console.log("No results");
}
});
EDIT
Well... 10 days after my original answer, apparently mssql added Windows Auth to the package. They heard our cries :) See here. I have not tested it yet, but it is officially in my backlog to test integration. I will report back.
FWTW, if mssql fits your needs, I would go with it, as 1) edge-sql has been dormant for 2 years and 2) the primary contributor has said he has left projects like this "in the caring hands of Microsoft", since he no longer works there.
EDIT 2
This keeps getting upvotes and there are comments saying some of the other answers' code examples either aren't working or aren't working on Windows.
This is my code using mssql, working on Windows, with msnodesqlv8 also installed:
var sql = require('mssql/msnodesqlv8');
var config = {
driver: 'msnodesqlv8',
connectionString: 'Driver={SQL Server Native Client XX.0};Server={SERVER\\NAME};Database={dbName};Trusted_Connection={yes};',
};
sql.connect(config)
.then(function() {
...profit...
})
.catch(function(err) {
// ... connect error checks
});
I've tried many variations and this is my complete solution.
I'm using SQL server Express.
I'm connecting, in the first instance, to the MASTER database only.
You only NEED to change "YOURINSTANCE\\SQLEXPRESS".
(Be sure to maintain the double-slash above!!!)
I'm using INTEGRATED SECURITY too.
The query relies on nothing at all (in your database).
You need to add your node packages
==> NPM INSTALL MSSQL and
==> NPM INSTALL msnodesqlv8
Hopefully, your connection issues will be a thing of the past.
Maybe.
Please.
// More here -> https://www.npmjs.com/package/mssql
var sql = require('mssql/msnodesqlv8');
var config = {
connectionString: 'Driver=SQL Server;Server=YOURINSTANCE\\SQLEXPRESS;Database=master;Trusted_Connection=true;'
};
sql.connect(config, err => {
new sql.Request().query('SELECT 1 AS justAnumber', (err, result) => {
console.log(".:The Good Place:.");
if(err) { // SQL error, but connection OK.
console.log(" Shirtballs: "+ err);
} else { // All is rosey in your garden.
console.dir(result);
};
});
});
sql.on('error', err => { // Connection borked.
console.log(".:The Bad Place:.");
console.log(" Fork: "+ err);
});
For me
I used connection setting as below
"server":"",
"domain":"", //sepcify domain of your user
"port": ,
"user":"", // enter username without domain
"password":"",
"database":""
and the TS code
import * as sql from 'mssql';
const pool = await new sql.ConnectionPool(connection).connect();
const result = await pool.request()
.query(`SELECT count(idpart) part_computed FROM demo.PARTs;`);
pool.close();
return Promise.resolve(result.recordset);
I could only get a Trusted Connection working using msnodesqlv8 (limited to Windows environments) with a connection string (rather than a config object).
const sql = require("msnodesqlv8");
const connectionString = function(databaseName) {
return "Server=.;Database=" + databaseName + ";Trusted_Connection=Yes;Driver={SQL Server Native Client 11.0}";
}
sql.query(connectionString("DatabaseName"), "SELECT * FROM dbo.Table1" , (err, recordset) => {
if(err) {
// Do something with the err object.
return;
}
// else
// Do something with the recordset object.
return;
});
Below code is working for me......
const sql = require('mssql/msnodesqlv8')
// config for your database
var config = {
driver: 'msnodesqlv8',
server: 'serverNAme\\SQLEXPRESS',
database: 'Learn' ,
options: {
trustedConnection: true
}
};
It worked for me
need to install msnodesqlv8 and mssql. also .......:)
var dbConfig = {
driver: 'msnodesqlv8',
server: "DESKTOP-66LO4I3",
database: "FutureHealthCareWeb",
user: "sa",
password: "pass#123",
options: {
trustedConnection: true
},
debug: true,
parseJSON: true
};
var sql = require('mssql/msnodesqlv8');
sql.connect(dbConfig, function (err) {
if (err) { console.log(JSON.stringify(err)+'..............') }
else {
console.log('Connected')
}
}
);
this worked for me
const sql = require("mssql/msnodesqlv8");
const conn = new sql.ConnectionPool({
database: "DB name",
server: "server name",
driver: "msnodesqlv8",
options: {
trustedConnection: true
}
});
conn.connect().then((err) => {
if(err) throw err;
else console.log("connected");
const req = new sql.Request(conn)
req.query("select * from table", function(error, res){
console.log(res)
})
});
I struggled to connect with mssql server which run in remote windows server using windows authentication mode . Then i found the solution just used like below code.
sql.connect("Data Source=172.25.x.x,1433;User Id=CSLx\\Name;Password=xxxxxx1234;Initial Catalog=giveTHedataabseNamel;Integrated Security=True",function(err){ }
I've just add domain: "DNAME", in config, and as result this config helps me connect to MS SQL with windows auth.
const config = {
driver: 'msnodesqlv8',
domain: "DNAME",
user: 'username',
password: 'pass',
server: '7.6.225.22',
database: 'DBNAME',
requestTimeout: 3600000, //an hour
options: {
trustedConnection: true
},
debug: true,
parseJSON: true
};
This version doesn't need a username or password.
To use windows authentication I installed mssql and msnodesqlv8.
Then in my app.js file:
const mssql = require('mssql/msnodesqlv8');
Note it is mssql not sql if you're using this example.
var config = {
database:'YOUR DATABASE NAME',
server: 'localhost\\SQLEXPRESS',
driver: 'msnodesqlv8',
options: {
trustedConnection: true,
enableArithAbort: true
}
};
You need to change the database name in config. Other than that it should work. My example:
app.get('/', function (req, res) {
mssql.connect(config, function (err) {
if (err) console.log(err);
var request = new mssql.Request();
request.query('select * from dbo.visit', function (err, result) {
if(err) console.log(err);
console.log(result);
});
});
});

create/drop database task for gulp/knex

I have an Express.js web applications which uses Knex.js as the SQL query builder and migrations engine.
While Knex.js has methods for creating, dropping, and altering tables, it doesn't have methods for creating/dropping the database itself.
I was wondering if there is an extension for Knex.js or even a gulp task that allows you to create/drop a database. I couldn't find any.
I'm using a PostgreSQL database.
I'm not sure about PostgreSQL, but I hit the same problem with MySQL. I discovered you can use knex to connect without selecting a database, create the database with raw SQL, then re-connect selecting the new database.
Here is a stand-alone script that creates a new database with a single-column table:
var conn = {
host: '127.0.0.1',
user: 'user',
password: 'pass',
charset: 'utf8',
};
// connect without database selected
var knex = require('knex')({ client: 'mysql', connection: conn });
knex.raw('CREATE DATABASE my_database').then(function () {
knex.destroy();
// connect with database selected
conn.database = 'my_database';
knex = require('knex')({ client: 'mysql', connection: conn });
knex.schema
.createTable('my_table', function (table) {
table.string('my_field');
})
.then(function () {
knex.destroy();
});
});
This works (well enough for me for now) but I'm interested to hear of other solutions.
var knex = require('knex')({
client: 'pg',
connection: {
host: HOST,
user: USERNAME,
password: PASSWORD,
database: 'postgres',
charset: 'utf8'
}
});
knex.raw('CREATE DATABASE DB_NAME;')
.then(function() {
return knex.raw('DROP DATABASE DB_NAME;')
})
.finally(function () {
console.log("Done");
});
You can add https://www.npmjs.org/package/gulp-shell
This should work:
var gulp = require('gulp')
var shell = require('gulp-shell')
gulp.task('example', function () {
return gulp.src('*.js', {read: false})
.pipe(shell([
'psql DROP DATABASE dbname;',
'psql CREATE DATABASE dbname;'
], {
templateData: {
f: function (s) {
return s.replace(/$/, '.bak')
}
}
}))
})
Postgres will accept the application's connection string to create the application database.
env/development
DATABASE_URL=postgres://user:password#localhost:5432/sample_development
package.json
"scripts": {
"db:create:development": "env-cmd env/development psql ${DATABASE_URL} -c 'CREATE DATABASE sample_test'",
"db:create:test": "env-cmd env/test psql ${DATABASE_URL} -c 'CREATE DATABASE sample_test'"
}
Then run npm run-script db:create:development.
(This uses the env-cmd package but it's not required.)
Loosely related (not a Gulp issue), but this is my solution to create the database in a Knex app. I simply ignore the error and NOT integrate with knexfile or the seed, so not getting called constantly, instead I call this from package.json. dropdb and createdb executables comes with Postgres:
// $ yarn node ./lib/createdb.mjs
import { exec } from 'child_process';
if (process.env.NODE_ENV === 'development') {
exec(`createdb ${dbName} --template=template0`, (error, stdout, stderr) => {
if (!error) return console.log(`"${dbName}" database created!`);
return console.error('createdb stderr:', stderr);
});
}
knex-db-manager is an add-in package that can do this.

Resources