Repository files navigation

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 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

FxSQL - Node.js Functional SQL Query Builder & ORM

EN | KR

Features

  • Tagged template literal
  • No models.
  • Only need functions and javascript data types.
  • Promise.
  • No cost for converting to JSON.
  • More freedom in using SQL syntax.
  • Preventing SQL-injection attacks.
  • Easy to use the latest operators provided in databases.
  • Simple transaction API.
  • No models for Associations.
  • Designed to work well with PostgreSQL, MySQL.

Overview

Installation

npm i fxsql

Connect (pool)

PostgreSQL

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

PostgreSQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of connection pool or connecting to DB on node-postgres’ site.

MySQL

const{ MySQL }=require("fxsql");const{CONNECT}=MySQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname'});

MySQL Connection option

FxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of connection pool or connecting to DB on MySQL's site.

Closing all the connections in a pool

PostgreSQL, MySQL

POOL.END(); // Promise

Simple query

const{QUERY}=POOL;constid=10;constposts=awaitQUERY`SELECT * FROM posts WHERE id = ${id}`;// [{ id: 10, ... }]

Subquery, Join

consttype='TYPE1';constlimit=10;QUERY` SELECT * FROM table1 WHERE table2_id IN ( SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit} )`;conststatus='STATUS1';QUERY` SELECT * FROM table1 AS t1, table2 AS t2 WHERE t1.id = t2.table1_id AND t1.status = ${status} ORDER BY id DESC LIMIT 10`;

QUERY achieved from CONNECT uses a connection pool.

Ready to be used

constPOOL=CONNECT();const{VALUES,IN,NOT_IN,EQ,SET,COLUMN,CL,TABLE,TB,SQL, FxSQL_DEBUG,QUERY,ASSOCIATE,LJOIN,TRANSACTION,END}=POOL;

Helper-Function

EQ

constusers=awaitQUERY`SELECT * FROM users WHERE ${EQ({email: 'dev@marpple.com',password: '1234'})}`;// [{ id: 15, email: 'dev@marpple.com', ... }]

IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${IN('id',[15,19,20,40])}`;// [{ id: 15, ...}, { id: 19, ...} ...]

NOT_IN

constusers=awaitQUERY`SELECT * FROM users WHERE ${NOT_IN('id',[2,4])} ORDER BY ID LIMIT 3`;// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]

VALUES

constpost={user_id: 10,body: 'hoho'};awaitQUERY` INSERT INTO posts ${VALUES(post)}`;// INSERT INTO posts ("user_id", "body") VALUES (10, 'hohoho')awaitQUERY` INSERT INTO coords ${VALUES([{x: 20},{y: 30},{x: 10,y: 20}])}`;// INSERT INTO coords ("x", "y") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)

SET

awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;// UPDATE posts SET "body" = 'yo!', "updated_at" = '2018-08-28T23:18:13.263Z' WHERE id = 10

COLUMN, CL

COLUMN==CL;// trueawaitQUERY` SELECT${COLUMN('id','bb as cc','t2.name','t2.name as name2',{a: 'c'},{'t3.a': 'd'})} ...`;// SELECT// "id", "bb" AS "cc", "t2"."name", "t2"."name" AS "name2", "a" AS "c", "t3"."a" AS "d"// ...

TABLE, TB

TABLE==TB;// trueawaitQUERY` SELECT ... FROM ${TABLE('t1')}, ${TABLE('tt as t2')}`;// SELECT// ...// FROM "t1", "tt" AS "t2"

Associations

Common use

ASSOCIATE uses Connection pool.

/** users* - id* - name** posts* - id* - user_id* - body* comments* - id* - user_id* - post_id* - body* */const{ASSOCIATE}=POOL;constposts=awaitASSOCIATE` posts - user < comments - user`;posts[0].body;posts[0]._.user.nameposts[0]._.comments[0].bodyposts[0]._.comments[0]._.user.name

- of - user refers to "Belongs to", < of < user refers to "Has many".

Polymorphic

/** photos* - attached_type* - attached_id* */awaitASSOCIATE` posts - user p - photo p < photos < comments p < photos`;// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, posts)}) AND attached_type = 'posts';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, users)}) AND attached_type = 'users';// SELECT * FROM photos WHERE attached_id IN (${map($ => $.id, comments)}) AND attached_type = 'comments';

p - refers to Polymorphic + Has one, p < refers to Polymorphic + Has many.

Many to many

/** books* - id* - title** authors* - id* - name** books_authors* - author_id* - book_id* */constbooks=awaitASSOCIATE` books x authors`;books[0]._.authors[0].name;constauthors=awaitASSOCIATE` authors x books ${{xtable: 'books_authors'}}`;authors[0]._.books[0].title;

Option

/** If the tables are formed like the example below, the ASSOCIATE automatically creates the necessary table and column names for queries. the necessary names for the tables and columns for queries* users* - id* posts* - id* - user_id* comments* - id* - post_id* - user_id* likes* - attached_type* - attached_id* - user_id* posts_tags* - post_id* - tag_id* tags* - id* */ASSOCIATE` posts - user < comments - user p < likes - user p < likes - user x tags`;/** You can select columns or add conditions.* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.* */ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} - user < comments ${{column: COLUMN('body','updated_at'),query: SQL`WHERE is_hidden = false ORDER BY id DESC`}} - user p < likes - user p < likes - user x tags`;/** If the names of the tables and columns does not follow the ASSOCIATE rules, you need to manually insert the correct names of the tables and columns.* members* - member_id* articles* - id* - writer_id* comments* - id* - article_id* - writer_id* likes* - parent_name* - parent_id* - member_id* tags_articles* - article_id* - tag_name* tags* - name* */constposts=awaitASSOCIATE` posts ${{table: 'articles'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} < comments ${{key: 'article_id'}} - user ${{left_key: 'writer_id',key: 'member_id',table: 'members'}} p < likes ${{poly_type: {parent_name: 'comments'},key: 'parent_id'}} p < likes ${{poly_type: {parent_name: 'articles'},key: 'parent_id'}} x tags ${{left_key: 'id',left_xkey: 'article_id',xtable: 'tags_articles',xkey: 'tag_name',key: 'name'}}`;

If you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.

ROW_NUMBER + PARTITION (PostgreSQL)

You can set the row_number option to fetch only up to four comments each post. Internally use ROW_NUMBER and PARTITION.

ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`} < comments ${{row_number: [4,SQL`id DESC`]}}`

Hook

You can add virtual columns, sorting, filtering and etc by using Hook. When all the datas are gathered below “posts”, Hook is executed.

constusers=awaitASSOCIATE` users ${{hook: users=>users.map(u=>Object.assign({},u,{_popular: !!u._.posts.find(p=>p._is_best)}))}} < posts ${{hook: posts=>posts.map(p=>Object.assign({},p,{_is_best: p._.comments.length>1}))}} - user < comments - user`;users[0]._popular;// trueusers[0]._.posts[0]._is_best;// trueusers[0]._.posts[1]._is_best;// false

ASSOCIATE_MODULE

ASSOCIATE allows you to modularize options for reuse. ASSOCIATE_MODULE in the function to be passed to ASSOCIATE.

Post.rights=()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [4,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights}`;

Use currying to pass arguments.

Post.rights=(limit=4)=>()=>ASSOCIATE_MODULE` - user < comments ${{row_number: [limit,SQL`id DESC`]}} - user p < likes - user p < likes - user x tags`;ASSOCIATE` posts ${SQL`WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}${Post.rights(6)}`;

Transaction

const{ PostgreSQL }=require("fxsql");const{CONNECT}=PostgreSQL;constPOOL=CONNECT({host: 'localhost',user: 'username',password: '1234',database: 'dbname',charset: 'utf8'});const{TRANSACTION}=POOL;const{QUERY,COMMIT,ROLLBACK}=awaitTRANSACTION();awaitQUERY` INSERT INTO posts ${VALUES(post)}`;awaitQUERY` UPDATE posts ${SET({body: 'yo!',updated_at: newDate()})} WHERE id = ${post.id}`;awaitROLLBACK();

DEBUG

FxSQL_DEBUG.LOG=true;QUERY`SELECT ${"hi~"} as ho`;// { text: 'SELECT $1 as ho', values: ['hi'] }

About

Node.js Functional SQL Query Builder & ORM

Resources

Stars

199 stars

Watchers

15 watching

Forks

Releases

Packages

Used by

Contributors

Languages