MQL - Node.js PostgreSQL 쿼리 빌더
특징
- INSERT, UPDATE, WHERE 절 등에 필요한 복잡한 쿼리를 자바스크립트 객체를 통해 쉽게 생성할 수 있습니다.
- 일반적인 SQL 문법을 유지할 수 있어, SQL을 세밀하게 튜닝하고 발전시키기 쉽습니다.
- PostgreSQL의 다양한 Operator 들을 쉽게 사용할 수 있습니다.
- SQL Injection 공격이 불가능합니다.
- Associations을 위해 모델을 미리 구성해둘 필요가 없습니다.
- Transaction을 사용하기 쉽습니다.
- 인자와 결과 값을 자바스크립트의 기본 값으로만(object, array, string, number, true, false, null) 구성하여, 조합성이 높고 JSON 변환 비용이 없습니다.
설치
npm i mql2
연결
const { CONNECT } = require('mql2');
const POOL = await CONNECT({
host: 'localhost',
user: 'username',
password: '1234',
database: 'dbname',
charset: 'utf8'
});
간단한 쿼리
const { QUERY } = POOL;
const id = 10;
const posts = await QUERY `SELECT * FROM posts WHERE id = ${id}`;
CONNECT
를 통해 얻은 객체를 통해 얻은 QUERY
는 connection pool을 이용합니다.
지원하는 tags
EQ
const users = await QUERY `SELECT * FROM users WHERE ${EQ({
email: 'dev@marpple.com',
password: '1234'
})}`;
IN
const users = await QUERY `SELECT * FROM users WHERE ${IN('id', [15, 19, 20, 40])}`;
NOT_IN
const users = await QUERY `SELECT * FROM users WHERE ${NOT_IN('id', [2, 4])} LIMIT 3 ORDER BY ID`;
VALUES
const post = { user_id: 10, body: 'hoho' };
await QUERY `
INSERT INTO posts ${post}
`;
await QUERY `
INSERT INTO coords ${VALUES([
{x: 20},
{y: 30},
{x: 10, y: 20}
])}`;
SET
await QUERY `
UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}
`;
Associations
기본
ASSOCIATE
는 connection pool을 이용합니다.
const { ASSOCIATE } = POOL;
const posts = await ASSOCIATE `
posts
- user
< comments
- user
`;
posts[0].body;
posts[0]._.user.name
posts[0]._.comments[0].body
posts[0]._.comments[0]._.user.name
Polymorphic
await ASSOCIATE `
posts
- user
p - photo
p < photos
< comments
p < photos
`;
Many to many
const books = await ASSOCIATE `
books
x authors
`;
books[0]._.authors[0].name;
const authors = await ASSOCIATE `
authors
x books ${{ xtable: 'books_authors' }}
`;
authors[0]._.books[0].name;
옵션
ASSOCIATE `
posts
- user
< comments
- user
p < likes
- user
p < likes
- user
x tags
`;
ASSOCIATE `
posts ${SQL `ORDER BY id DESC LIMIT ${10}`}
- user
< comments ${{
column: COLUMN('body', 'updated_at')
}}
- user
p < likes
- user
p < likes
- user
x tags
`;
const posts = await ASSOCIATE `
posts ${{
table: 'articles' // 데이터베이스 테이블 명이 다를 때
}}
- user ${{ // - 를 했으므로 하나를 객체로 가져옴
left_key: 'writer_id', // articles가 가진 members.member_id를 가리키는 컬럼
key: 'member_id', // members 테이블이 가진 키
table: 'members' // user의 테이블 명
}}
< comments ${{ // < 를 했으므로 배열로 여러개를 가져옴
key: 'article_id' // articles의 id를 가리키는 comments가 가진 컬럼
}}
- user ${{
left_key: 'writer_id', // articles가 가진 members.member_id를 가리키는 컬럼
key: 'member_id', // members 테이블이 가진 키
table: 'members' // user의 테이블 명
}}
p < likes ${{ // p < 를 이용해 하나의 likes 테이블을 통해 comments와 posts의 likes를 구현
poly_type: { parent_name: 'comments' },
key: 'parent_id'
}}
p < likes ${{ // p < 를 이용해 하나의 likes 테이블을 통해 comments와 posts의 likes를 구현
poly_type: { parent_name: 'articles' },
key: 'parent_id'
}}
x tags ${{ // x 를 통해 중간 테이블을 join 하여 다대다 관계 구현
left_key: 'id', // articles.id (articles.id = tags_articles.article_id)
left_xkey: 'article_id', // left_key와 매칭되는 tags_articles의 키 article_id
xtable: 'tags_articles', // 중간 테이블 이름
xkey: 'tag_name', // key와 매칭되는 tags_articles의 키 tag_name
key: 'name' // tags가 가진 키 (tags_articles.tag_name = tags.name)
}}
`;
위와 같이 데이터베이스의 테이블명과 사용하고자하는 이름이 다르거나, ASSOCIATE
가 자동생성하는 컬럼명 등과 실제 데이터베이스의 상태가 다를 경우 옵션을 이용하여 맞춰줄 수 있습니다. 그러나 대부분의 경우는 데이터베이스의 VIEW를 사용하는 것이 코드 관리에 좋습니다.
Transaction
const { CONNECT } = require('mql2');
const POOL = await CONNECT({
host: 'localhost',
user: 'username',
password: '1234',
database: 'dbname',
charset: 'utf8'
});
const { TRANSACTION } = POOL;
const { QUERY, COMMIT, ROLLBACK } = await TRANSACTION();
await QUERY `
INSERT INTO posts ${post}
`;
await QUERY `
UPDATE posts ${SET({ body: 'yo!', updated_at: new Date() })} WHERE id = ${post.id}
`;
await ROLLBACK();
TRANSACTION
을 통해 얻은 객체를 통해 얻은 QUERY
는 하나의 connection을 이용합니다. ROLLBACK
이나 COMMIT
후에는 위에서 만든 QUERY 함수는 더이상 사용할 수 없습니다.
DEBUG
MQL_DEBUG.LOG
를 true
로 설정한 후 QUERY
를 실행하면 콘솔에 DB로 보낸 쿼리들을 출력합니다.
const { MQL_DEBUG } = require('mql2');
MQL_DEBUG.LOG = true;
QUERY `SELECT ${"hi~"} as ho`;
Connection Pool 옵션
MQL은 내부적으로 node-postgres를 사용합니다. CONNECT
함수에 사용되는 옵션은 node-postgres와 동일합니다. 디비 연결이나 커넥션 풀과 관련된 자세한 옵션은 node-postgres 사이트에서 확인할 수 있습니다.