imago-sql
Version:
Small library to work with Azure SQL and MySQL via a single interface.
231 lines (165 loc) • 6.94 kB
Markdown
# Imago SQL #
Small module for working with SQL (including Azure SQL) in Node.js. Based on the `mssql` module in NPM.
## Installation ##
`npm i --save imago-sql`
## Usage ##
```js
const Sql = require('imago-sql');
let sql = new Sql(process.env.DB_CONNECTION_STRING);
let results = await sql.selectAll('My_Table_Name'); // select all records
```
### Important notes ###
Minimum recommended Node.js version: 8.9.
For best performance and to avoid establishing a new connection for each call,
run the constructor at the top level of app.js, cache it in a global variable,
and before each SQL call, make sure that the connection has been established.
```js
// app.js
global.sql = new Sql(DB_CONNECTION_STRING);
async function readFromDatabase() {
// Make sure connection has been established:
await global.sql.connection;
// Query database, reusing the global connection pool:
let results = await global.sql.query('SELECT * FROM Customers');
}
```
For simplicity, examples below do not reuse a global connection pool.
## Methods ##
### Constructor ###
Example constructor call:
```js
let sql = new Sql(connectionString, options);
```
The constructor takes the connection string in the URL format:
```
mssql://login:password@database-url.database.windows.net/Database_Name?encrypt=true
```
#### Constructor Options ####
The second argument to the constructor, `options,` is not required. It must be
an object that can contain the following keys:
- `debug`: print the SQL statements to the console. Default: `false`.
- `swallowExceptions`: instead of throwing an exception when an SQL transaction
fails, swallow the exception and return null. Default: `false`.
- `engine`: Available options are 'azure' and 'mysql'. Default is 'azure'. If
the connection string starts with 'mysql://', the engine will be autodetected
as `mysql`.
- `templatePath`: The folder containing the SQL statement template files. See
documentation for `getStatement()` below.
Sample initialization for MySQL:
```js
let sql = new Sql(connectionString, { engine: 'mysql' });
```
### async selectAll(tableName) ###
Selects all rows from the specified table.
### async query(statement) ###
Executes an SQL query, returns only the results.
### async runSqlQuery(statement) ###
Same as `query()`, but returns raw results in `mssql`'s format, meaning the actual results are either under `recordset` (for single result) or under `recordsets` (for multiple result sets).
### async runParameterizedQuery(statement, parameters) ###
Executes a query with parameters, which is more efficient and safe.
Example:
```js
const Sql = require('imago-sql');
let sql = new Sql(DB_CONNECTION_STRING);
let statement = `UPDATE My_Table_Name
SET
licenseName=@licenseName,
description=@description
WHERE
id=3`;
let parameters = [
{ name: 'licenseName', type: sql.db.mssql.NVarChar, value: 'Test name' },
{ name: 'description', type: sql.db.mssql.NVarChar, value: 'Sample license' },
];
let results = await sql.runParameterizedQuery(statement, parameters);
```
The column types, such as `NVarChar`, are available inside `sql.db.mssql`. Previously
we were using this code which caused unexpected errors due to requiring
the `mssql` package twice:
```js
// Do not do that.
// Require the mssql package again when it's already required inside
// `imago-sql` will cause random errors:
const Azure = require('mssql');
// ...
type = Azure.NVarChar;
// ...
```
### async execute(procedure, parameters) ###
Executes a stored procedure, optionally with parameters.
Arguments:
- `procedure` - the name of the stored procedure.
- `parameters` (optional) - the data object containing the arguments to the procedure.
For example, if the stored procedure takes two parameters (`name` and `email`),
the data object should be `{ name: 'Anton', email: 'test@imago.ai' }`. In other
words, keys in the object must be the same as stored procedure parameter names.
The data type will be guessed automatically.
Example:
```js
const STORED_PROCEDURE_NAME = 'save_log';
const Sql = require('imago-sql');
let sql = new Sql(DB_CONNECTION_STRING);
let results = await sql.execute(STORED_PROCEDURE_NAME, {
timestamp: Date.now(),
ipAddress: '8.8.4.4',
message: 'test logging',
});
```
### async insertMultipleRows(table, columns, rows) ###
Inserts multiple rows into a table simultaneously. Returns 'OK' if successful.
- `table` - The name of the table to insert the data into.
- `columns` - Names of columns to insert the data into. The order of columns in `rows` must be the same as here.
- `rows` - An array of rows, where each row is an array of values in columns. The columns are the same as described in `columns`.
### escapeString(str) ###
Escapes a string to make it SQL-safe.
### async truncate(tableName) ###
Truncates the specified table (removes all rows, resets the auto-increment
counter).
### async save(table, data) ###
Saves the entire object as a row into the database. Tries to guess the parameter types.
The object's property names must match the column names.
- `table` - The name of the SQL table.
- `data` - An object to be saved as an SQL row.
### async getTemplate(name, params) ###
Reads an SQL statement template from a file, replaces syntax like `{{user.name}}`
with values from inside `params`, and returns that SQL statement.
The SQL statement templates must be in the folder specified in
`options.templatePath`. The filename should end with `.sql` or `.sql.template`.
Example:
```js
const ImagoSql = require('imago-sql');
const sql = new ImagoSql({
...
templatePath: './sql-templates',
});
const params = {
table: 'Users'
};
// Reads the statement from the file `./sql-templates/get_users.sql`, and
// replaces instances of {{table}} with 'Users':
const statement = await sql.getTemplate('get_users', params);
// Now we can run this statement:
const data = await sql.query(statement);
```
### async bulkInsert(table) ###
`At the moment, this function will just apply on Azure/MS SQL`
To handle if the program needs to insert huge of data at the same time,
then you can create a table object which the schema MUST be same as the table in DB,
push the table object into bulkInsert.
Example:
```js
const ImagoSql = require('imago-sql');
const sql = new Sql(CONNECTION_STRING);
const table = new Azure.Table(process.env.TABLE_NAME_PRODUCTS_WHITELIST);
table.create = true;
table.columns.add('field-1', Azure.Int, { nullable: false});
table.columns.add('field-2', Azure.Int, { nullable: false});
...
for (var j = 0; j < data.length; j++){
table.rows.add(
data[j]["field-1"], data[j]["field-2"]
....
);
}
await sql.bulkInsert(table);
```