Friday, 30 August 2019

transaction for prepared statements using Node mssql

I want to create an Express REST API and will use MSSQL for the database part. I use the mssql module and created a database class first

import sql, { ConnectionPool, PreparedStatement, IProcedureResult } from 'mssql';
import { injectable } from 'inversify';

import { IDatabase } from './IDatabase';
import { InternalServerErrorException } from '../../interfaceAdapters/httpExceptions/InternalServerErrorException';
import * as databaseConfig from '../../config/DatabaseConfig';

@injectable()
export class Database implements IDatabase {
    public connectionPool: ConnectionPool;

    constructor() {
        this.connectionPool = new sql.ConnectionPool(databaseConfig);
    }

    public connect = async (): Promise<void> => {
        try {
            await this.connectionPool.connect();
        } catch (error) {
            throw error;
        }
    }

    public query = async (builder: Function) : Promise<IProcedureResult<any>> => {
        try {
            const rawStatement: PreparedStatement = new sql.PreparedStatement(this.connectionPool);

            const queryInfo: any = builder(rawStatement);
            const { preparedStatement, queryString, queryParams = {} }: { preparedStatement: PreparedStatement, queryString: string, queryParams: object } = queryInfo;

            await preparedStatement.prepare(queryString);
            const queryResult: IProcedureResult<any> = await preparedStatement.execute(queryParams);
            await preparedStatement.unprepare();

            return queryResult;
        } catch (error) {
            throw new InternalServerErrorException(error.message);
        }
    }
}

As you can see I created a query function with a builder parameter. I have multiple queries from multiple classes and don't want to write the same code over and over again so I pass in only the things that differ from query to query. A basic example would be my fetchUserById example

public fetchUserById = async (params: any[]): Promise<QueryResult> => {
    try {
        const queryResult: IProcedureResult<any> = await this.database.query((preparedStatement: PreparedStatement) => {
            preparedStatement.input('userId', sql.Numeric);

            const queryString: string = `
                SELECT *
                FROM users
                WHERE id = @userId
            `;

            const queryParams: object = {
                userId: params[0]
            };

            return {
                preparedStatement, 
                queryString,
                queryParams,
            };
        });

        return new QueryResult(queryResult.recordset, queryResult.rowsAffected.length);
    } catch (error) {
        throw error;
    }
}

The database provides the preparedStatement object and the query passes back the updated statement, the query string and the parameters.

This code works fine but doesn't provide the possibility for transactional queries.

I found some information about prepared statements here and about transactions here but don't know how to execute transactional queries using prepared statements.

Would someone mind explaining how to use the mssql module to provide the possibility using prepared statements within transactions? My database query function should provide a functionality giving access to write something like this in my query file

  • begin transaction
  • run insertQuery one
  • run insertQuery two
  • end transaction


from transaction for prepared statements using Node mssql

No comments:

Post a Comment