Dynamically generating SQL queries using Node.js

Dynamically generating SQL queries using Node.js

Introduction to building dynamic SQL queries safely in Node.js using Slonik.

Ever since I have released Slonik (PostgreSQL client for Node.js) and written a controversial Stop using Knex.js article (tl;dr; query builders are designed to be building blocks for ORMs; they do not add value when majority of the query is static.), I have been asked a lot – then how do I generate dynamic queries? I will answer this by sharing a couple of real-life examples.

All of the queries in this article are actual queries used in real-life business, Applaudience which heavily relies on PostgreSQL.

Disclaimer: (1) All examples discuss only SQL injection threats. Authorization logic (e.g. whitelisting columns user is authorized to access) is not in the scope of this article. (2) All statements assume there are no bugs in Slonik implementation.

Static query with dynamic value bindings

If your query logic does not change depending on user's input, then simply construct SQL query using sql tagged template literal, e.g.

sql`
  SELECT c1.country_id
  FROM cinema_movie_name cmn1
  INNER JOIN cinema c1 ON c1.id = cmn1.cinema_id
  WHERE cmn1.id = ${cinemaMovieNameId}
`;

If you are using Slonik, it is safe to pass values as template literal placeholderssql will interpret all placeholder tokens and construct final SQL query. In this case, the only dynamic part of the query is the value bindings themselves, therefore the final query is:

SELECT c1.country_id
FROM cinema_movie_name cmn1
INNER JOIN cinema c1 ON c1.id = cmn1.cinema_id
WHERE cmn1.id = $1

Query and bound values will be sent to PostgreSQL separately: no risk of SQL injection.

Binding a list of values

When your query input is a list of values (e.g. such as when retrieving rows matching multiple identifiers), then you may use sql.valueList, e.g.

sql  SELECT m1.*   FROM movie m1   WHERE m1.id IN (${sql.valueList(movieIds)});

This will generate a query with dynamic set of value bindings, i.e. if movieIds is [1, 2, 3] the query that is sent to PostgreSQL will be:

SELECT m1.*
FROM movie m1
WHERE m1.id IN ($1, $2, $3)

However, despite this being a common pattern, I do not advise to use this pattern. Instead, use sql.array, e.g.

sql  SELECT m1.*   FROM movie m1   WHERE m1.id = ANY(${sql.array(movieIds, 'int4')});

This will generate a fixed-length query that does not change based on its inputs, i.e.

SELECT m1.*
FROM movie m1
WHERE m1.id = ANY($1::"int4"[])

Continue reading sql.array vs sql.valueList.

Query with dynamic columns

If your query result refers to columns that depend on user's input, then use sql.identifier to generate SQL that identifies those columns, e.g.

(Note: Not an actual query used in business. See next paragraph.)

sql  SELECT m1.id, ${sql.identifier(['m1', movieTableColumnName])}   FROM movie m1   WHERE     m1.id = ${moveId};

This query will produce a query that selects exactly 1 dynamically identified column. There is no risk of SQL injection, i.e. even if logic leading to generation of movieTableColumnName was somehow compromised, the worst that can happen is that query attacker will be able to return any column under m1 alias or execute query with invalid column identifier values (both carry risk; business logic is not in scope of this article).

Just because you can do this, you probably shouldn't. When your application requires to return different columns depending on user's query, it is better to select all columns that are in scope of the business logic and pick value of the needed column, i.e. If the intent of the latter query was to return a different movie identifier based on movieTableColumnName, then it is better to write a static query:

sql  SELECT     m1.id,     m1.foreign_comscore_id,     m1.foreign_imdb_id,     m1.foreign_metacritic_id     m1.foreign_rottentomatoes_id,     m1.foreign_tmdb_id,     m1.foreign_webedia_id   FROM movie m1   WHERE     m1.id = ${moveId};

The latter has does return some superfluous data on every query, but it has several advantages:

  1. It reduces risk of SQL injection (regardless of how much you trust code generation logic, static code is always safer than dynamic code).
  2. It produces only one entry pg_stat_statements. You will learn to appreciate as few as possible queries in pg_stat_statements as your application scales.
Query with multiple dynamic columns

Same as the above, but sql.identifierList.

Nesting dynamic SQL queries

sql tagged template literals can be nested, e.g.

(Note: Simplified version of an actual query used in business.)

const futureEventEventChangeSqlToken = sql  SELECT     ec1.event_id,     ec1.seat_count,     ec1.seat_sold_count   FROM event_change_future_event_view ec1;

sql SELECT event_id, seat_count, seat_sold_count FROM ( ${futureEventEventChangeSqlToken} ) AS haystack WHERE ${paginatedWhereSqlToken} ORDER BY ${orderSqlToken} LIMIT ${limitSqlToken}

This allows to pass pre-bound SQL queries as first-class citizens across your program. This is handy when the intent is to isolate SQL generation logic for testing or when large SQL fragments are shared between queries or when the intent is to simply reduce concentration of code complexity in one place.

Injecting dynamic SQL fragments

sql.raw is used to inject dynamic SQL fragments, i.e.

sql  SELECT ${sql.raw('foo bar baz')}

translates to (invalid) query:

SELECT foo bar baz

Unlike the previous example using sql tagged template, sql.raw is not safe – it allows to create dynamic SQL using user input.

There are no known use cases for generating queries using sql.raw that aren't covered by nesting bound sql expressions (described in "Nesting dynamic SQL queries") or by one of the other existing query building methodssql.raw exists as a mechanism to execute externally stored static (e.g. queries stored in files).

Query with a dynamic comparison predicate members or operator

If an operator of a comparison predicate present in your query is dynamic, then use sql.comparisonPredicate, e.g.

(Note: Not an actual query used in business.)

sql   SELECT     c1.id,     c1.nid,     c1.name   FROM cinema c1   WHERE     ${sql.comparisonPredicate(       sqlc1.name,       nameComparisonOperator,       nameComparisonValue     )} ;

nameComparisonOperator can be values such as =><, etc. Assuming nameComparisonOperator is "=", then the resulting query is going to be:

SELECT
c1.id,
c1.nid,
c1.name
FROM cinema c1
WHERE
c1.name = $1

The latter is an extremely rare use case, reserved almost entirely to building higher level SQL abstraction tools (such as ORMs). It may be useful for "advance search" scenarios, however continue reading to familiarise with alternative patterns (see sql.booleanExpression).

Query with dynamic WHERE clause members

If presence of WHERE clause members is dynamic, then use sql.booleanExpression.

const findCinemas = (root, parameters, context) => {
const booleanExpressions = [
sqlTRUE,
];

if (parameters.input.query) {
const query = parameters.input.query;

if (query.countryId !== undefined) {
  booleanExpressions.push(
    sql`c2.id = ${query.countryId}`
  );
}

if (query.nid !== undefined) {
  booleanExpressions.push(
    sql`c1.nid % ${query.nid}`
  );
}

if (query.name !== undefined) {
  booleanExpressions.push(
    sql`c1.name % ${query.name}`
  );
}

}

const whereSqlToken = sql.booleanExpression(
booleanExpressions,
'AND'
);

return context.pool.any(sql SELECT c1.id, c1.nid, c1.name, c2.code_alpha_2 country_code, c2.name country_name FROM cinema c1 INNER JOIN country c2 ON c2.id = c1.country_id WHERE ${whereSqlToken} );
},

findCinemas is an implementation of a GraphQL resolver. WHERE clause of the query is constructed using a combination of 3 possible boolean expressions. As is the case with all the other query building methods in Slonik, all expressions can be nested: you can have other boolean expressions as members of a boolean expression or even SQL expression constructed using sql tagged template literal.

Summary

These examples cover every common dynamic SQL building scenario and provide enough knowledge of how Slonik works to enable reader to continue journey of familiarising with other query building methods provided by Slonik. The primary intent of this article was to demonstrate that Slonik provides a safe abstraction for constructing SQL queries keeping the static parts of the query intact.

If you value my work and want to see Slonik and many other of my Open-Source projects to be continuously improved, then please consider becoming a patron:

Originally published by Gajus Kuizinas at medium.com

=========================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn more

How To Build a Blog with Nest.js, MongoDB, and Vue.js

MongoDB, Express, Vue.js 2, Node.js (MEVN) and SocketIO Chat App

First CRUD Node Express Js Mysql Example

Angular7 CRUD with nodejs and mysql example


Top Vue.js Developers in USA

Top Vue.js Developers in USA

Vue.js is an extensively popular JavaScript framework with which you can create powerful as well as interactive interfaces. Vue.js is the best framework when it comes to building a single web and mobile apps.

We, at HireFullStackDeveloperIndia, implement the right strategic approach to offer a wide variety through customized Vue.js development services to suit your requirements at most competitive prices.

Vue.js is an open-source JavaScript framework that is incredibly progressive and adoptive and majorly used to build a breathtaking user interface. Vue.js is efficient to create advanced web page applications.

Vue.js gets its strength from the flexible JavaScript library to build an enthralling user interface. As the core of Vue.js is concentrated which provides a variety of interactive components for the web and gives real-time implementation. It gives freedom to developers by giving fluidity and eases the integration process with existing projects and other libraries that enables to structure of a highly customizable application.

Vue.js is a scalable framework with a robust in-build stack that can extend itself to operate apps of any proportion. Moreover, vue.js is the best framework to seamlessly create astonishing single-page applications.

Our Vue.js developers have gained tremendous expertise by delivering services to clients worldwide over multiple industries in the area of front-end development. Our adept developers are experts in Vue development and can provide the best value-added user interfaces and web apps.

We assure our clients to have a prime user interface that reaches end-users and target the audience with the exceptional user experience across a variety of devices and platforms. Our expert team of developers serves your business to move ahead on the path of success, where your enterprise can have an advantage over others.

Here are some key benefits that you can avail when you decide to hire vue.js developers in USA from HireFullStackDeveloperIndia:

  • A team of Vue.js developers of your choice
  • 100% guaranteed client satisfaction
  • Integrity and Transparency
  • Free no-obligation quote
  • Portal development solutions
  • Interactive Dashboards over a wide array of devices
  • Vue.js music and video streaming apps
  • Flexible engagement model
  • A free project manager with your team
  • 24*7 communication with your preferred means

If you are looking to hire React Native developers in USA, then choosing HireFullStackDeveloperIndia would be the best as we offer some of the best talents when it comes to Vue.js.

Develop this one fundamental skill if you want to become a successful developer

Throughout my career, a multitude of people have asked me&nbsp;<em>what does it take to become a successful developer?</em>

Throughout my career, a multitude of people have asked me what does it take to become a successful developer?

It’s a common question newbies and those looking to switch careers often ask — mostly because they see the potential paycheck. There is also a Hollywood level of coolness attached to working with computers nowadays. Being a programmer or developer is akin to being a doctor or lawyer. There is job security.

But a lot of people who try to enter the profession don’t make it. So what is it that separates those who make it and those who don’t? 

Read full article here