Mysql을 쓰다 보니 리턴값의 타입이
RowDataPacket[], ResultSetHeader 이라는 것을 알게 되었다.
ResultSetHeader을 이용해 insert한 행의 id를 가져오고 싶었는데
Property 'insertId' does not exist on type 'RowDataPacket[] | ResultSetHeader'.
Property 'insertId' does not exist on type 'RowDataPacket[]'
에러가 발생해서 무작정 따라 썼던 db연결 방법을 다시 공부해보기로 했다.
https://sidorares.github.io/node-mysql2/docs/documentation/typescript-examples
[ 원래 코드 ]
import {
createPool,
Pool,
PoolConnection,
ResultSetHeader,
RowDataPacket,
} from 'mysql2'
// DB 연결 및 풀 생성
const pool: Pool = createPool({
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_DATABASE || '',
port: 3306,
})
// executeQuery 함수
export default function executeQuery(
query: string,
arrParams: (string | '')[],
): Promise<RowDataPacket[]> {
return new Promise((resolve, reject) => {
pool.getConnection((err: Error, conn: PoolConnection) => {
if (err) {
console.error('Error connecting to db:', err.message)
reject(err)
} else {
conn.query(
query,
arrParams,
(
err: Error | null,
data: RowDataPacket[] | ResultSetHeader,
) => {
conn.release()
if (err) {
console.error(
'Error in executing the query:',
err.message,
)
reject(err)
} else {
console.log('Query executed successfully')
resolve(data as RowDataPacket[])
}
},
)
}
})
})
}
connection Pool : 데이터베이스와 연결된 Connection을 미리 만들어서 pool 속에 저장해 두고 있다가 필요할 때 Connection을 Pool에서 쓰고 다시 Pool에 반환하는 기법
//DB pool options
const access: PoolOptions = {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_DATABASE || '',
port: 3306,
}
// DB pool create
const pool: Pool = createPool(access)
pool.getConnection((err: Error, conn: PoolConnection) => {
if (err) {
console.error('Error connecting to db:', err.message)
} else {
const res = conn.query('select foo from bar')
conn.release()
return res
}
})
select, show 쿼리 결과 리턴 값 타입
// SELECT
conn.query<RowDataPacket[]>('SELECT 1 + 1 AS `test`;', (_err, rows) => {
console.log(rows);
/**
* @rows: [ { test: 2 } ]
*/
});
// SHOW
conn.query<RowDataPacket[]>('SHOW TABLES FROM `test`;', (_err, rows) => {
console.log(rows);
/**
* @rows: [ { Tables_in_test: 'test' } ]
*/
});
Using multipleStatements option as true with multiple queries
다중 쿼리(show, select)에 대한 다중리턴값 타입
const sql = `
SELECT 1 + 1 AS test;
SELECT 2 + 2 AS test;
`;
conn.query<RowDataPacket[][]>(sql, (_err, rows) => {
console.log(rows);
/**
* @rows: [ [ { test: 2 } ], [ { test: 4 } ] ]
*/
});
INSERT, UPDATE, DELETE, TRUNCATE
const sql = `
SET @1 = 1;
`;
conn.query<ResultSetHeader>(sql, (_err, result) => {
console.log(result);
/**
* @result: ResultSetHeader {
fieldCount: 0,
affectedRows: 0,
insertId: 0,
info: '',
serverStatus: 2,
warningStatus: 0,
changedRows: 0
}
*/
ResultSetHeader[] : 다중 쿼리(INSERT, UPDATE, DELETE, TRUNCATE)에 대한 다중리턴값 타입
/** ProcedureCallPacket */
const sql = 'CALL myProcedure()';
conn.query<ProcedureCallPacket<ResultSetHeader>>(sql, (_err, result) => {
console.log(result);
/**
* @result: ResultSetHeader {
fieldCount: 0,
affectedRows: 0,
insertId: 0,
info: '',
serverStatus: 2,
warningStatus: 0,
changedRows: 0
}
*/
});
OkPacket is deprecated and might be removed in the future major release.
Please use ResultSetHeader instead.
const sql =
'INSERT INTO bridge.post (title, content, num, location, startDate, atTime, duration, endDate, author_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
try {
const res: ResultSetHeader = await executeQuery(sql, [...values])
console.log('res', res.insertId)
} catch (error) {
throw new Error('error')
}
여기서 res에 에러가 발생했다.
Type 'ResultSetHeader | RowDataPacket[]' is not assignable to type 'ResultSetHeader'.
Type 'RowDataPacket[]' is missing the following properties from type 'ResultSetHeader': affectedRows, fieldCount, info, insertId, and 3 more
res는 executeQuery에 의해 반환 값이 ResultSetHeader | RowDataPacket[]는데 ResultSetHeader을 지정했다는 오류다.
⭐해결법: 제네릭으로 선언 시점이 아닌, 사용 시점에 타입을 선언하자
[최종 수정 코드]
export default function executeQuery<T>(
query: string,
arrParams: (string | number | '' | null)[],
): Promise<T> {
return new Promise<T>((resolve, reject) => {
pool.getConnection((err: Error, conn: PoolConnection) => {
if (err) {
console.error('Error connecting to db:', err.message)
} else {
conn.query(query, arrParams, (err, rows) => {
conn.release()
if (err) {
console.error(
'Error in executing the query:',
err.message,
)
reject(err)
} else {
console.log('Query executed successfully')
resolve(rows as T)
}
})
}
})
})
}