{"id":13607886,"url":"https://github.com/marpple/FxSQL","last_synced_at":"2025-04-12T14:31:24.368Z","repository":{"id":32918576,"uuid":"144729244","full_name":"marpple/FxSQL","owner":"marpple","description":"Node.js Functional SQL Query Builder \u0026 ORM","archived":false,"fork":false,"pushed_at":"2023-03-04T04:23:03.000Z","size":291,"stargazers_count":194,"open_issues_count":4,"forks_count":15,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-05-29T18:59:53.421Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marpple.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-08-14T14:09:34.000Z","updated_at":"2024-05-27T17:40:01.000Z","dependencies_parsed_at":"2024-01-14T05:00:27.013Z","dependency_job_id":"95c5359a-fb33-4da5-b2a3-d5757dd3e0de","html_url":"https://github.com/marpple/FxSQL","commit_stats":{"total_commits":194,"total_committers":8,"mean_commits":24.25,"dds":0.4020618556701031,"last_synced_commit":"b548c3e14f110971c4b8391254cc75f1ced06b16"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2FFxSQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2FFxSQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2FFxSQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marpple%2FFxSQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marpple","download_url":"https://codeload.github.com/marpple/FxSQL/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248581154,"owners_count":21128111,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T19:01:22.503Z","updated_at":"2025-04-12T14:31:23.878Z","avatar_url":"https://github.com/marpple.png","language":"JavaScript","readme":"# FxSQL - Node.js Functional SQL Query Builder \u0026 ORM\n\n[EN](https://github.com/marpple/FxSQL) | [KR](https://github.com/marpple/FxSQL/blob/master/README_kr.md)\n\n## Features\n - Tagged template literal\n - No models.\n - Only need functions and javascript data types.\n - Promise.\n - No cost for converting to JSON.\n - More freedom in using SQL syntax.\n - Preventing SQL-injection attacks.\n - Easy to use the latest operators provided in databases.\n - Simple transaction API.\n - No models for Associations.\n - Designed to work well with PostgreSQL, MySQL.\n\n## Overview\n  - [Installation](#installation)\n  - [Connect (pool)](#connect-pool)\n    - [PostgreSQL](#postgresql)\n    - [MySQL](#mysql)\n  - [Closing all the connections in a pool](#closing-all-the-connections-in-a-pool)\n  - [Simple query](#simple-query)\n  - [Subquery, Join](#subquery-Join)\n  - [Ready to be used](#ready-to-be-used)\n  - [Helper Function](#helper-Function)\n    - [EQ](#eq)\n    - [IN](#in)\n    - [NOT_IN](#not_in)\n    - [VALUES](#values)\n    - [SET](#set)\n    - [COLUMN, CL](#column-cl)\n    - [TABLE, TB](#table-tb)\n  - [Associations](#associations)\n    - [Common use](#common-use)\n    - [Polymorphic](#polymorphic)\n    - [Transaction](#transaction)\n    - [Many to many](#many-to-many)\n    - [ROW_NUMBER + PARTITION](#row_number--partition-postgresql)\n    - [Hook](#hook)\n    - [ASSOCIATE_MODULE](#associate_module)\n  - [Option](#option)\n  - [DEBUG](#debug)\n\n## Installation\n\n```\nnpm i fxsql\n```\n\n## Connect (pool)\n\n### PostgreSQL\n\n```javascript\nconst { PostgreSQL } = require(\"fxsql\");\nconst { CONNECT } = PostgreSQL;\nconst POOL = CONNECT({\n  host: 'localhost',\n  user: 'username',\n  password: '1234',\n  database: 'dbname'\n});\n```\n\n### PostgreSQL Connection option\n\nFxSQL is built on node-postgres. The parameter of CONNECT function is the same as node-postgres’. You can read the detail of [connection pool](https://node-postgres.com/api/pool) or [connecting to DB](https://node-postgres.com/features/connecting) on [node-postgres’ site](https://node-postgres.com/).\n\n### MySQL\n\n```javascript\nconst { MySQL } = require(\"fxsql\");\nconst { CONNECT } = MySQL;\nconst POOL = CONNECT({\n  host: 'localhost',\n  user: 'username',\n  password: '1234',\n  database: 'dbname'\n});\n```\n\n### MySQL Connection option\n\nFxSQL is built on node-postgres. The parameter of CONNECT function is the same as the MySQL’. You can read the detail of [connection pool](https://github.com/mysqljs/mysql#pool-options) or [connecting to DB](https://github.com/mysqljs/mysql#connection-options) on [MySQL's site](https://github.com/mysqljs/mysql).\n\n## Closing all the connections in a pool\n\n### PostgreSQL, MySQL\n\n```\nPOOL.END(); // Promise\n```\n\n## Simple query\n\n```javascript\nconst { QUERY } = POOL;\nconst id = 10;\nconst posts = await QUERY `SELECT * FROM posts WHERE id = ${id}`;\n// [{ id: 10, ... }]\n```\n\n## Subquery, Join\n\n```javascript\nconst type = 'TYPE1';\nconst limit = 10;\n\nQUERY `\n  SELECT * FROM table1 WHERE table2_id IN (\n    SELECT id FROM table2 WHERE type = ${type} ORDER BY id DESC LIMIT ${limit}\n  )\n`;\n\nconst status = 'STATUS1';\n\nQUERY `\n  SELECT *\n    FROM table1 AS t1, table2 AS t2\n    WHERE t1.id = t2.table1_id AND t1.status = ${status}\n    ORDER BY id DESC\n    LIMIT 10\n`;\n```\n\nQUERY achieved from CONNECT uses a connection pool.\n\n## Ready to be used\n\n```javascript\nconst POOL = CONNECT();\nconst {\n  VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, FxSQL_DEBUG,\n  QUERY,\n  ASSOCIATE,\n  LJOIN,\n  TRANSACTION,\n  END\n} = POOL;\n```\n\n## Helper-Function\n\n### EQ\n\n```javascript\nconst users = await QUERY `SELECT * FROM users WHERE ${EQ({\n  email: 'dev@marpple.com',\n  password: '1234'\n})}`;\n// [{ id: 15, email: 'dev@marpple.com', ... }]\n```\n\n### IN\n\n```javascript\nconst users = await QUERY `SELECT * FROM users WHERE ${IN('id', [15, 19, 20, 40])}`;\n// [{ id: 15, ...}, { id: 19, ...} ...]\n```\n\n### NOT_IN\n\n```javascript\nconst users = await QUERY `SELECT * FROM users WHERE ${NOT_IN('id', [2, 4])} ORDER BY ID LIMIT 3`;\n// [{ id: 1, ...}, { id: 3, ...}, { id: 5, ...}]\n```\n\n### VALUES\n\n```javascript\nconst post = { user_id: 10, body: 'hoho' };\nawait QUERY `\n  INSERT INTO posts ${VALUES(post)}\n`;\n// INSERT INTO posts (\"user_id\", \"body\") VALUES (10, 'hohoho')\n\nawait QUERY `\n  INSERT INTO coords ${VALUES([\n    {x: 20},\n    {y: 30},\n    {x: 10, y: 20}\n  ])}`;\n// INSERT INTO coords (\"x\", \"y\") VALUES (20, DEFAULT), (DEFAULT, 30), (10, 20)\n```\n\n### SET\n\n```javascript\nawait QUERY `\n  UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}\n`;\n// UPDATE posts SET \"body\" = 'yo!', \"updated_at\" = '2018-08-28T23:18:13.263Z' WHERE id = 10\n```\n\n### COLUMN, CL\n\n```javascript\nCOLUMN == CL; // true\n\nawait QUERY `\n  SELECT\n    ${COLUMN('id', 'bb as cc', 't2.name', 't2.name as name2', { a: 'c' }, { 't3.a': 'd' })}\n      ...\n`;\n// SELECT\n//   \"id\", \"bb\" AS \"cc\", \"t2\".\"name\", \"t2\".\"name\" AS \"name2\", \"a\" AS \"c\", \"t3\".\"a\" AS \"d\"\n//     ...\n```\n\n### TABLE, TB\n\n```javascript\nTABLE == TB; // true\n\nawait QUERY `\n  SELECT\n    ...\n    FROM ${TABLE('t1')}, ${TABLE('tt as t2')}\n`;\n// SELECT\n//   ...\n//   FROM \"t1\", \"tt\" AS \"t2\"\n```\n\n## Associations\n\n### Common use\n\nASSOCIATE uses Connection pool.\n\n```javascript\n/*\n* users\n*  - id\n*  - name\n*\n* posts\n*  - id\n*  - user_id\n*  - body\n\n* comments\n*  - id\n*  - user_id\n*  - post_id\n*  - body\n* */\n\nconst { ASSOCIATE } = POOL;\n\nconst posts = await ASSOCIATE `\n  posts\n    - user\n    \u003c comments\n      - user\n`;\n\nposts[0].body;\nposts[0]._.user.name\nposts[0]._.comments[0].body\nposts[0]._.comments[0]._.user.name\n```\n\n`-` of `- user` refers to \"Belongs to\", `\u003c` of `\u003c user` refers to \"Has many\".\n\n### Polymorphic\n\n```javascript\n/*\n* photos\n*  - attached_type\n*  - attached_id\n* */\n\nawait ASSOCIATE `\n  posts\n    - user\n      p - photo\n    p \u003c photos\n    \u003c comments\n      p \u003c photos\n`;\n// SELECT * FROM photos WHERE attached_id IN (${map($ =\u003e $.id, posts)}) AND attached_type = 'posts';\n// SELECT * FROM photos WHERE attached_id IN (${map($ =\u003e $.id, users)}) AND attached_type = 'users';\n// SELECT * FROM photos WHERE attached_id IN (${map($ =\u003e $.id, comments)}) AND attached_type = 'comments';\n```\n\n`p -` refers to Polymorphic + Has one, `p \u003c` refers to Polymorphic + Has many.\n\n### Many to many\n\n```javascript\n/*\n* books\n*  - id\n*  - title\n*\n* authors\n*  - id\n*  - name\n*\n* books_authors\n*  - author_id\n*  - book_id\n* */\n\nconst books = await ASSOCIATE `\n  books\n    x authors\n`;\n\nbooks[0]._.authors[0].name;\n\nconst authors = await ASSOCIATE `\n  authors\n    x books ${{ xtable: 'books_authors' }}\n`;\n\nauthors[0]._.books[0].title;\n```\n\n### Option\n\n```javascript\n/*\n* 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\n* users\n*  - id\n* posts\n*  - id\n*  - user_id\n* comments\n*  - id\n*  - post_id\n*  - user_id\n* likes\n*  - attached_type\n*  - attached_id\n*  - user_id\n* posts_tags\n*  - post_id\n*  - tag_id\n* tags\n*  - id\n* */\n\nASSOCIATE `\n  posts\n    - user\n    \u003c comments\n     - user\n     p \u003c likes\n      - user\n    p \u003c likes\n      - user\n    x tags\n`;\n\n/*\n* You can select columns or add conditions.\n* Even though you don’t select a foreign key or a primary key in the option like the below, they are included in ASSOCIATE.\n* */\n\nASSOCIATE `\n  posts ${SQL `WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}\n    - user\n    \u003c comments ${{\n      column: COLUMN('body', 'updated_at'),\n      query: SQL `WHERE is_hidden = false ORDER BY id DESC`\n    }}\n     - user\n     p \u003c likes\n      - user\n    p \u003c likes\n      - user\n    x tags\n`;\n\n\n/*\n* 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.\n* members\n*  - member_id\n* articles\n*  - id\n*  - writer_id\n* comments\n*  - id\n*  - article_id\n*  - writer_id\n* likes\n*  - parent_name\n*  - parent_id\n*  - member_id\n* tags_articles\n*  - article_id\n*  - tag_name\n* tags\n*  - name\n* */\n\nconst posts = await ASSOCIATE `\n  posts ${{\n    table: 'articles'\n  }}\n    - user ${{\n      left_key: 'writer_id',\n      key: 'member_id',\n      table: 'members'\n    }}\n    \u003c comments ${{\n      key: 'article_id'\n    }}\n      - user ${{\n        left_key: 'writer_id',\n        key: 'member_id',\n        table: 'members'\n      }}\n      p \u003c likes ${{\n        poly_type: { parent_name: 'comments' },\n        key: 'parent_id'\n      }}\n    p \u003c likes ${{\n      poly_type: { parent_name: 'articles' },\n      key: 'parent_id'\n    }}\n    x tags ${{\n      left_key: 'id',\n      left_xkey: 'article_id',\n      xtable: 'tags_articles',\n      xkey: 'tag_name',\n      key: 'name'\n    }}\n`;\n```\n\nIf you use VIEW in databases, it's much easier. Then, you don't need to insert all correct column and table names.\n\n### ROW_NUMBER + PARTITION (PostgreSQL)\n\nYou can set the `row_number` option to fetch only up to four comments each post. Internally use `ROW_NUMBER` and` PARTITION`.\n\n```javascript\nASSOCIATE `\n  posts ${SQL `WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}\n    \u003c comments ${{\n      row_number: [4, SQL `id DESC`]\n    }}\n`\n```\n\n### Hook\n\nYou can add virtual columns, sorting, filtering and etc by using Hook.\nWhen all the datas are gathered below “posts”, Hook is executed.\n\n```javascript\nconst users = await ASSOCIATE `\n  users ${{hook: users =\u003e users.map(u =\u003e\n    Object.assign({}, u, { _popular: !!u._.posts.find(p =\u003e p._is_best) })\n  )}}\n    \u003c posts ${{hook: posts =\u003e posts.map(\n      p =\u003e Object.assign({}, p, { _is_best: p._.comments.length \u003e 1 }))}}\n      - user\n      \u003c comments\n       - user\n`;\n\nusers[0]._popular; // true\nusers[0]._.posts[0]._is_best; // true\nusers[0]._.posts[1]._is_best; // false\n```\n\n### ASSOCIATE_MODULE\n\n`ASSOCIATE` allows you to modularize options for reuse. `ASSOCIATE_MODULE` in the function to be passed to` ASSOCIATE`.\n\n```javascript\nPost.rights = () =\u003e ASSOCIATE_MODULE `\n  - user\n    \u003c comments ${{\n      row_number: [4, SQL `id DESC`]\n    }}\n     - user\n     p \u003c likes\n      - user\n    p \u003c likes\n      - user\n    x tags\n`;\n\nASSOCIATE `\n  posts ${SQL `WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}\n    ${Post.rights}\n`;\n```\n\nUse currying to pass arguments.\n\n```javascript\nPost.rights = (limit = 4) =\u003e () =\u003e ASSOCIATE_MODULE `\n  - user\n    \u003c comments ${{\n      row_number: [limit, SQL `id DESC`]\n    }}\n     - user\n     p \u003c likes\n      - user\n    p \u003c likes\n      - user\n    x tags\n`;\n\nASSOCIATE `\n  posts ${SQL `WHERE is_hidden = false ORDER BY id DESC LIMIT ${10}`}\n    ${Post.rights(6)}\n`;\n```\n\n## Transaction\n\n```javascript\nconst { PostgreSQL } = require(\"fxsql\");\nconst { CONNECT } = PostgreSQL;\nconst POOL = CONNECT({\n  host: 'localhost',\n  user: 'username',\n  password: '1234',\n  database: 'dbname',\n  charset: 'utf8'\n});\nconst { TRANSACTION } = POOL;\nconst { QUERY, COMMIT, ROLLBACK } = await TRANSACTION();\n\nawait QUERY `\n  INSERT INTO posts ${VALUES(post)}\n`;\nawait QUERY `\n  UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}\n`;\nawait ROLLBACK();\n```\n\n\n## DEBUG\n\n\n```javascript\nFxSQL_DEBUG.LOG = true;\nQUERY `SELECT ${\"hi~\"} as ho`;\n\n// { text: 'SELECT $1 as ho', values: ['hi'] }\n```\n\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarpple%2FFxSQL","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarpple%2FFxSQL","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarpple%2FFxSQL/lists"}