Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

pgr

This module aims to provide a structured and easy way to execute queries against a Postgres DB. It's a good fit if you want more support than using the pg module by itself but don't want to use an ORM. Its main features include a tagged template string based query helper and a small wrapper around pg's actual query methods.

Installation

yarn add pgr

Basic Example Usage

Once, in your application's entry point (before you want to run a query):

import{createPool}from'pgr'createPool('myPoolName',{// The options here are exactly what you can provide to pg, such ashost: 'localhost',user: 'Andre',password: '',database: 'mydb',})

If you only create one pool, you don't need to specify its name when running queries. For multiple pool support, check out the advanced usage section below.

Later on:

import{query,sql}from'pgr'constvalue=42constrows=awaitquery(sql` SELECT * FROM my_table WHERE some_col = ${value}`)

That's it! The sql tagged template string will run your statement through pgformat (always with %L) to properly escape any dangerous variables and invoke it with your previously created pool.

sql.if

I find that I often want to dynamically construct my statements based on the truthiness of a given variable. This allows for compact, powerful query methods similar to what you might find in an ORM. Enter sql.if:

Simple mode (your test variable and arg are the same)

Note: For purposes of sql.if, the number 0 is treated as truthy, and an empty array is treated as falsy.

import{query,sql}from'pgr'constfindUsers=async({ id, accountId, emails, roles })=>query(sql` SELECT * FROM users WHERE status = 'active'${sql.if('AND id = ?',id)}${sql.if('AND email = ?',email)}${sql.if('AND role IN (?)',roles)} `)
awaitfindUsers({id: 73})
SELECT*FROM users
WHERE status ='active'AND id ='73'

Your variable will get subbed in for the question mark in your expression. If there is no question mark, the variable will be used to test if the expression should be added as-is.

awaitfindUsers({accountId: 1,roles: ['admin','superadmin']})
SELECT*FROM users
WHERE status ='active'AND account_id ='1'AND role IN ('admin', 'superadmin')
awaitfindUsers({accountId: 1,roles: []})// An empty array is treated as falsy
SELECT*FROM users
WHERE status ='active'AND account_id ='1'

Complex mode (different test and arg variables, arg is optional)

import{query,sql}from'pgr'constSTATUSES=[1,2,3]constfindRelationships=async({ id, includeOngoing })=>{constcheckStatus= ... // External function returning true/falsereturnquery(sql` SELECT * FROM relationships WHERE from_id = ${id}${sql.if({test: includeOngoing,expr: 'AND end_date IS NULL'})}${sql.if({test: checkStatus,expr: 'AND status IN (?)',arg: STATUSES})} `)}
awaitfindRelationships({id: 1,includeOngoing: true})
(assuming checkStatus was true):
SELECT*FROM relationships
WHERE from_id =1AND end_date IS NULLAND status IN ('1','2','3')

sql.raw

You may have standard query fragments that you build up and inject into many queries. You might also have situations where pgformat's substitution doesn't achieve what you need. The escape hatch that you can use carefully is sql.raw.

constcurrentUser={purchasedItems: [10,20]}constfragment=sql`AND allowed_items IN (${currentUser.purchasedItems})`conststatement=sql`
SELECT *
FROM items
WHERE on_sale = true
${sql.raw(fragment)}
SELECT*FROM items
WHERE on_sale = true
AND allowed_items IN ('10','20')

Note that fragments must themselves be run through sql if you need escaping. Don't be like little Bobby Tables.

constname="Robert'); DROP TABLE Students; --"conststatement=sql`
SELECT *
FROM oh_no
WHERE name IN ('${sql.raw(fragment)}')
SELECT*FROM oh_no
WHERE name IN ('Robert'); DROPTABLEStudents; --')

query, query.one, query.transaction

We've seen the most simple form of query, but it can also take a second options argument:

constrows=awaitquery(sql`SELECT ...`,{debug: false,// Logs the statement to the console before running itdebugOnly: false,// Logs the statement to the console and does NOT run itpoolName: '',// Runs the query with a client of the specified pool namerowMapper: row=>{},// A (synchronous) function to run on every row in the result})constknownEmails=awaitquery(sql`SELECT email FROM users`,{rowMapper: row=>row.email,})

query.one

Invoked exactly like query, except that instead of returning an array of rows, it will return one object. If your query results in no rows, it will return a null. If your query returns more than one row, it will throw an Error. You can also use rowMapper here.

const{ email }=awaitquery.one(sql`SELECT email FROM users WHERE id = ${currentUserId}`)console.log(email)// 'apazzolini@test.test'

query.transaction

You can also run multiple queries inside of a transaction:

constresult=awaitquery.transaction(asynctquery=>{// Inside this function, you should take care to use tquery// instead of query or you may run into deadlocks.// tquery behaves exactly like query (and also has tquery.one)return'myResult'})console.log(result)// 'myResult'

Metrics

pgr stores average execution time for your queries along with the number of times the query has happened. This is done by taking the base query (pre variable insertion) and giving it an ID based on its hash. This allows aggregating metrics even if a query is executed multiple times with different arguments.

const{ metrics }=getPool('default')console.log(metrics.queries)// { [id]: { baseStatement: '...', count: 1, avgMs: 100 }}

License

MIT

About

A structured and easy way to execute queries against Postgres

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages