From b12dc1f7a9dcdfb71dad15158aa477e12d84fce8 Mon Sep 17 00:00:00 2001 From: KaKa Date: Mon, 18 Jan 2021 19:02:37 +0800 Subject: [PATCH 1/6] feat: typings - add typescript typing --- README.md | 34 +++++++++++++++++++++++++++ index.d.ts | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ index.test-d.ts | 16 +++++++++++++ package.json | 8 +++++-- 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 index.d.ts create mode 100644 index.test-d.ts diff --git a/README.md b/README.md index 6daff27..4dafba5 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,40 @@ fastify.listen(3000, err => { }) ``` +## TypeScript +As ```mysql2``` expose four different type of client, we do not specify the typing for you. You need to specify the type yourself follow by the example below. +```ts +import { MySQLConnection, MySQLPool, MySQLPromiseConnection, MySQLPromisePool } from 'fastify-mysql' + +// if you only pass connectionString +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPool + } +} + +// if you passed type = 'connection' +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLConnection + } +} + +// if you passed promise = true +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPromisePool + } +} + +// if you passed promise = true, type = 'connection' +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPromiseConnection + } +} +``` + ## Acknowledgements This project is kindly sponsored by: diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..a2d6d6b --- /dev/null +++ b/index.d.ts @@ -0,0 +1,61 @@ +import { FastifyPlugin } from "fastify"; +import { + Connection, + ConnectionOptions, + escape, + format, + Pool, + PoolOptions, +} from "mysql2"; +import { + Connection as PromiseConnection, + Pool as PromisePool, +} from "mysql2/promise"; + +export type MySQLConnection = Pick & { + connection: Connection; + format: typeof format; + escape: typeof escape; +}; + +export type MySQLPool = Pick & { + pool: Pool; + format: typeof format; + escape: typeof escape; +}; + +export type MySQLPromiseConnection = Pick< + PromiseConnection, + "query" | "execute" +> & { + connection: PromiseConnection; + format: typeof format; + escape: typeof escape; +}; + +export type MySQLPromisePool = Pick< + PromisePool, + "query" | "execute" | "getConnection" +> & { + pool: PromisePool; + format: typeof format; + escape: typeof escape; +}; + +declare module "fastify" { + interface FastifyInstance { + mysql: {}; + } +} + +export type ConnectionType = "connection" | "pool"; + +export interface MySQLOptions extends PoolOptions, ConnectionOptions { + type?: ConnectionType; + name?: string; + promise?: boolean; + connectionString?: string; +} + +export const fastifyMySQL: FastifyPlugin; +export default fastifyMySQL; diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..850ff11 --- /dev/null +++ b/index.test-d.ts @@ -0,0 +1,16 @@ +import fastify from "fastify"; +import fastifyMysql, { MySQLPool } from "."; + +const app = fastify(); +app + .register(fastifyMysql, { + connectionString: "mysql://root@localhost/mysql", + }) + .after(function (err) { + const mysql = app.mysql as MySQLPool; + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.getConnection(function (err, con) { + con.release(); + }); + }); diff --git a/package.json b/package.json index 5978bcf..a3b7354 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,10 @@ "version": "2.0.1", "description": "Fastify Mysql connection plugin", "main": "index.js", + "types": "index.d.ts", "scripts": { - "test": "standard && tap test/*.test.js", + "test": "standard && tap test/*.test.js && npm run typescript", + "typescript": "tsd", "mariadb": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mariadb:10.1", "mysql": "npm run mysql:8.0", "mysql:5.7": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:5.7", @@ -32,8 +34,10 @@ "mysql2": "^2.0.0" }, "devDependencies": { + "@types/node": "^14.14.21", "fastify": "^3.0.0", "standard": "^16.0.0", - "tap": "^14.10.7" + "tap": "^14.10.7", + "tsd": "^0.14.0" } } From 9548e3dc8656ad11cc5f847c540bb3116c32da7c Mon Sep 17 00:00:00 2001 From: KaKa Date: Mon, 18 Jan 2021 23:42:26 +0800 Subject: [PATCH 2/6] chore: fix quote Co-authored-by: Matteo Collina --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dafba5..56c72f0 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ fastify.listen(3000, err => { ``` ## TypeScript -As ```mysql2``` expose four different type of client, we do not specify the typing for you. You need to specify the type yourself follow by the example below. +As `mysql2` expose four different type of client, we do not specify the typing for you. You need to specify the type yourself follow by the example below. ```ts import { MySQLConnection, MySQLPool, MySQLPromiseConnection, MySQLPromisePool } from 'fastify-mysql' From fd4e74ceb307afa0c0ad9ef7c01dd5361e7ac007 Mon Sep 17 00:00:00 2001 From: KaKa Date: Tue, 19 Jan 2021 00:17:23 +0800 Subject: [PATCH 3/6] chore: remove deprecate type --- index.d.ts | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/index.d.ts b/index.d.ts index a2d6d6b..cebeaa3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,4 @@ -import { FastifyPlugin } from "fastify"; +import { FastifyPluginCallback } from "fastify"; import { Connection, ConnectionOptions, @@ -12,35 +12,36 @@ import { Pool as PromisePool, } from "mysql2/promise"; -export type MySQLConnection = Pick & { - connection: Connection; +// upstream package missed type +type escapeId = (val: any, forbidQualified?: boolean) => string; + +interface BaseClient { format: typeof format; escape: typeof escape; -}; + escapeId: escapeId; +} + +export type MySQLConnection = Pick & { + connection: Connection; +} & BaseClient; export type MySQLPool = Pick & { pool: Pool; - format: typeof format; - escape: typeof escape; -}; +} & BaseClient; export type MySQLPromiseConnection = Pick< PromiseConnection, "query" | "execute" > & { connection: PromiseConnection; - format: typeof format; - escape: typeof escape; -}; +} & BaseClient; export type MySQLPromisePool = Pick< PromisePool, "query" | "execute" | "getConnection" > & { pool: PromisePool; - format: typeof format; - escape: typeof escape; -}; +} & BaseClient; declare module "fastify" { interface FastifyInstance { @@ -57,5 +58,5 @@ export interface MySQLOptions extends PoolOptions, ConnectionOptions { connectionString?: string; } -export const fastifyMySQL: FastifyPlugin; +export const fastifyMySQL: FastifyPluginCallback; export default fastifyMySQL; From c3a803440107760b1ab7ad57b571ecd9706ed8b6 Mon Sep 17 00:00:00 2001 From: KaKa Date: Tue, 19 Jan 2021 00:17:30 +0800 Subject: [PATCH 4/6] test: add more test case --- index.test-d.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/index.test-d.ts b/index.test-d.ts index 850ff11..cda9c43 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,10 @@ import fastify from "fastify"; -import fastifyMysql, { MySQLPool } from "."; +import fastifyMysql, { + MySQLConnection, + MySQLPool, + MySQLPromiseConnection, + MySQLPromisePool, +} from "."; const app = fastify(); app @@ -8,9 +13,61 @@ app }) .after(function (err) { const mysql = app.mysql as MySQLPool; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); mysql.query("SELECT NOW()", function () {}); mysql.execute("SELECT NOW()", function () {}); mysql.getConnection(function (err, con) { con.release(); }); + mysql.pool.end(); + }); + +app + .register(fastifyMysql, { + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLPromisePool; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + const con = await mysql.getConnection(); + con.release(); + mysql.pool.end(); + }); + +app + .register(fastifyMysql, { + type: "connection", + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLConnection; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.connection.end(); + }); + +app + .register(fastifyMysql, { + type: "connection", + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLPromiseConnection; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + mysql.connection.end(); }); From 1575385e4f882ab0648f051830bffa81ad30602f Mon Sep 17 00:00:00 2001 From: KaKa Date: Tue, 19 Jan 2021 02:20:36 +0800 Subject: [PATCH 5/6] test: update the test for type augmentation --- index.d.ts | 6 -- index.test-d.ts | 73 ------------------- .../connection-promise.test.ts | 25 +++++++ test/types/connection-promise/tsconfig.json | 3 + test/types/connection/connection.test.ts | 24 ++++++ test/types/connection/tsconfig.json | 3 + test/types/pool-promise/pool-promise.test.ts | 26 +++++++ test/types/pool-promise/tsconfig.json | 3 + test/types/pool/pool.test.ts | 26 +++++++ test/types/pool/tsconfig.json | 3 + 10 files changed, 113 insertions(+), 79 deletions(-) delete mode 100644 index.test-d.ts create mode 100644 test/types/connection-promise/connection-promise.test.ts create mode 100644 test/types/connection-promise/tsconfig.json create mode 100644 test/types/connection/connection.test.ts create mode 100644 test/types/connection/tsconfig.json create mode 100644 test/types/pool-promise/pool-promise.test.ts create mode 100644 test/types/pool-promise/tsconfig.json create mode 100644 test/types/pool/pool.test.ts create mode 100644 test/types/pool/tsconfig.json diff --git a/index.d.ts b/index.d.ts index cebeaa3..c9bf1fd 100644 --- a/index.d.ts +++ b/index.d.ts @@ -43,12 +43,6 @@ export type MySQLPromisePool = Pick< pool: PromisePool; } & BaseClient; -declare module "fastify" { - interface FastifyInstance { - mysql: {}; - } -} - export type ConnectionType = "connection" | "pool"; export interface MySQLOptions extends PoolOptions, ConnectionOptions { diff --git a/index.test-d.ts b/index.test-d.ts deleted file mode 100644 index cda9c43..0000000 --- a/index.test-d.ts +++ /dev/null @@ -1,73 +0,0 @@ -import fastify from "fastify"; -import fastifyMysql, { - MySQLConnection, - MySQLPool, - MySQLPromiseConnection, - MySQLPromisePool, -} from "."; - -const app = fastify(); -app - .register(fastifyMysql, { - connectionString: "mysql://root@localhost/mysql", - }) - .after(function (err) { - const mysql = app.mysql as MySQLPool; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - mysql.query("SELECT NOW()", function () {}); - mysql.execute("SELECT NOW()", function () {}); - mysql.getConnection(function (err, con) { - con.release(); - }); - mysql.pool.end(); - }); - -app - .register(fastifyMysql, { - promise: true, - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql as MySQLPromisePool; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - await mysql.query("SELECT NOW()"); - await mysql.execute("SELECT NOW()"); - const con = await mysql.getConnection(); - con.release(); - mysql.pool.end(); - }); - -app - .register(fastifyMysql, { - type: "connection", - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql as MySQLConnection; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - mysql.query("SELECT NOW()", function () {}); - mysql.execute("SELECT NOW()", function () {}); - mysql.connection.end(); - }); - -app - .register(fastifyMysql, { - type: "connection", - promise: true, - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql as MySQLPromiseConnection; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - await mysql.query("SELECT NOW()"); - await mysql.execute("SELECT NOW()"); - mysql.connection.end(); - }); diff --git a/test/types/connection-promise/connection-promise.test.ts b/test/types/connection-promise/connection-promise.test.ts new file mode 100644 index 0000000..2ba9183 --- /dev/null +++ b/test/types/connection-promise/connection-promise.test.ts @@ -0,0 +1,25 @@ +import fastify from "fastify"; +import fastifyMysql, { MySQLPromiseConnection } from "../../.."; + +declare module "fastify" { + interface FastifyInstance { + mysql: MySQLPromiseConnection; + } +} + +const app = fastify(); +app + .register(fastifyMysql, { + type: "connection", + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + mysql.connection.end(); + }); diff --git a/test/types/connection-promise/tsconfig.json b/test/types/connection-promise/tsconfig.json new file mode 100644 index 0000000..1d33786 --- /dev/null +++ b/test/types/connection-promise/tsconfig.json @@ -0,0 +1,3 @@ +{ + "rootDir": "." +} \ No newline at end of file diff --git a/test/types/connection/connection.test.ts b/test/types/connection/connection.test.ts new file mode 100644 index 0000000..d3e9035 --- /dev/null +++ b/test/types/connection/connection.test.ts @@ -0,0 +1,24 @@ +import fastify from "fastify"; +import fastifyMysql, { MySQLConnection } from "../../.."; + +declare module "fastify" { + interface FastifyInstance { + mysql: MySQLConnection; + } +} + +const app = fastify(); +app + .register(fastifyMysql, { + type: "connection", + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.connection.end(); + }); diff --git a/test/types/connection/tsconfig.json b/test/types/connection/tsconfig.json new file mode 100644 index 0000000..1d33786 --- /dev/null +++ b/test/types/connection/tsconfig.json @@ -0,0 +1,3 @@ +{ + "rootDir": "." +} \ No newline at end of file diff --git a/test/types/pool-promise/pool-promise.test.ts b/test/types/pool-promise/pool-promise.test.ts new file mode 100644 index 0000000..3a2956a --- /dev/null +++ b/test/types/pool-promise/pool-promise.test.ts @@ -0,0 +1,26 @@ +import fastify from "fastify"; +import fastifyMysql, { MySQLPromisePool } from "../../.."; + +declare module "fastify" { + interface FastifyInstance { + mysql: MySQLPromisePool; + } +} + +const app = fastify(); +app + .register(fastifyMysql, { + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + const con = await mysql.getConnection(); + con.release(); + mysql.pool.end(); + }); diff --git a/test/types/pool-promise/tsconfig.json b/test/types/pool-promise/tsconfig.json new file mode 100644 index 0000000..1d33786 --- /dev/null +++ b/test/types/pool-promise/tsconfig.json @@ -0,0 +1,3 @@ +{ + "rootDir": "." +} \ No newline at end of file diff --git a/test/types/pool/pool.test.ts b/test/types/pool/pool.test.ts new file mode 100644 index 0000000..6eddbef --- /dev/null +++ b/test/types/pool/pool.test.ts @@ -0,0 +1,26 @@ +import fastify from "fastify"; +import fastifyMysql, { MySQLPool } from "../../.."; + +declare module "fastify" { + interface FastifyInstance { + mysql: MySQLPool; + } +} + +const app = fastify(); +app + .register(fastifyMysql, { + connectionString: "mysql://root@localhost/mysql", + }) + .after(function (err) { + const mysql = app.mysql; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.getConnection(function (err, con) { + con.release(); + }); + mysql.pool.end(); + }); diff --git a/test/types/pool/tsconfig.json b/test/types/pool/tsconfig.json new file mode 100644 index 0000000..1d33786 --- /dev/null +++ b/test/types/pool/tsconfig.json @@ -0,0 +1,3 @@ +{ + "rootDir": "." +} \ No newline at end of file From 10bd6a6b84c6d2bea07a64c5b1ef83edd601c18b Mon Sep 17 00:00:00 2001 From: KaKa Date: Tue, 19 Jan 2021 02:38:20 +0800 Subject: [PATCH 6/6] test: fix failing typescript testing --- README.md | 292 +++++++++--------- index.d.ts | 112 +++---- index.test-d.ts | 83 +++++ package.json | 86 +++--- .../connection-promise.test.ts | 25 -- test/types/connection-promise/tsconfig.json | 3 - test/types/connection/connection.test.ts | 24 -- test/types/connection/tsconfig.json | 3 - test/types/pool-promise/pool-promise.test.ts | 26 -- test/types/pool-promise/tsconfig.json | 3 - test/types/pool/pool.test.ts | 26 -- test/types/pool/tsconfig.json | 3 - 12 files changed, 328 insertions(+), 358 deletions(-) create mode 100644 index.test-d.ts delete mode 100644 test/types/connection-promise/connection-promise.test.ts delete mode 100644 test/types/connection-promise/tsconfig.json delete mode 100644 test/types/connection/connection.test.ts delete mode 100644 test/types/connection/tsconfig.json delete mode 100644 test/types/pool-promise/pool-promise.test.ts delete mode 100644 test/types/pool-promise/tsconfig.json delete mode 100644 test/types/pool/pool.test.ts delete mode 100644 test/types/pool/tsconfig.json diff --git a/README.md b/README.md index 56c72f0..6ab019b 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,146 @@ -# fastify-mysql - -![CI workflow](https://github.com/fastify/fastify-mysql/workflows/CI%20workflow/badge.svg) -[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) - -Fastify MySQL connection plugin, with this you can share the same MySQL connection pool in every part of your server. -Under the hood the [mysql2](https://github.com/sidorares/node-mysql2) is used, the options that you pass to `register` will be passed to the MySQL pool builder. - -## Install -``` -npm i fastify-mysql --save -``` -## Usage -Add it to you project with `register` and you are done! -This plugin will add the `mysql` namespace in your Fastify instance, with the following properties: -``` -pool: the pool instance -query: an utility to perform a query without a transaction -execute: an utility to perform a prepared statement without a transaction -getConnection: get a connection from the pool -format: an utility to generate SQL string -escape: an utility to escape query values -escapeId: an utility to escape query identifiers -``` - -Example: -```js -const fastify = require('fastify')() - -fastify.register(require('fastify-mysql'), { - connectionString: 'mysql://root@localhost/mysql' -}) - -fastify.get('/user/:id', (req, reply) => { - fastify.mysql.getConnection(onConnect) - - function onConnect (err, client) { - if (err) return reply.send(err) - - client.query( - 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], - function onResult (err, result) { - client.release() - reply.send(err || result) - } - ) - } -}) - -fastify.listen(3000, err => { - if (err) throw err - console.log(`server listening on ${fastify.server.address().port}`) -}) -``` - -Use of `mysql.query` -```js -const fastify = require('fastify')() - -fastify.register(require('fastify-mysql'), { - connectionString: 'mysql://root@localhost/mysql' -}) - -fastify.get('/user/:id', (req, reply) => { - fastify.mysql.query( - 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], - function onResult (err, result) { - reply.send(err || result) - } - ) -}) - -fastify.listen(3000, err => { - if (err) throw err - console.log(`server listening on ${fastify.server.address().port}`) -}) -``` -As you can see there is no need to close the client, since is done internally. - -Async/await is supported, when register `promise` option is `true`: -```js -const fastify = require('fastify')() - -fastify.register(require('fastify-mysql'), { - promise: true, - connectionString: 'mysql://root@localhost/mysql' -}) - -fastify.get('/user/:id', async (req, reply) => { - const connection = await fastify.mysql.getConnection() - const [rows, fields] = await connection.query( - 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], - ) - connection.release() - return rows[0] -}) - -fastify.listen(3000, err => { - if (err) throw err - console.log(`server listening on ${fastify.server.address().port}`) -}) -``` - -## TypeScript -As `mysql2` expose four different type of client, we do not specify the typing for you. You need to specify the type yourself follow by the example below. -```ts -import { MySQLConnection, MySQLPool, MySQLPromiseConnection, MySQLPromisePool } from 'fastify-mysql' - -// if you only pass connectionString -declare module 'fastify' { - interface FastifyInstance { - mysql: MySQLPool - } -} - -// if you passed type = 'connection' -declare module 'fastify' { - interface FastifyInstance { - mysql: MySQLConnection - } -} - -// if you passed promise = true -declare module 'fastify' { - interface FastifyInstance { - mysql: MySQLPromisePool - } -} - -// if you passed promise = true, type = 'connection' -declare module 'fastify' { - interface FastifyInstance { - mysql: MySQLPromiseConnection - } -} -``` - -## Acknowledgements - -This project is kindly sponsored by: -- [nearForm](http://nearform.com) -- [LetzDoIt](http://www.letzdoitapp.com/) - -## License - -Licensed under [MIT](./LICENSE). +# fastify-mysql + +![CI workflow](https://github.com/fastify/fastify-mysql/workflows/CI%20workflow/badge.svg) +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) + +Fastify MySQL connection plugin, with this you can share the same MySQL connection pool in every part of your server. +Under the hood the [mysql2](https://github.com/sidorares/node-mysql2) is used, the options that you pass to `register` will be passed to the MySQL pool builder. + +## Install +``` +npm i fastify-mysql --save +``` +## Usage +Add it to you project with `register` and you are done! +This plugin will add the `mysql` namespace in your Fastify instance, with the following properties: +``` +pool: the pool instance +query: an utility to perform a query without a transaction +execute: an utility to perform a prepared statement without a transaction +getConnection: get a connection from the pool +format: an utility to generate SQL string +escape: an utility to escape query values +escapeId: an utility to escape query identifiers +``` + +Example: +```js +const fastify = require('fastify')() + +fastify.register(require('fastify-mysql'), { + connectionString: 'mysql://root@localhost/mysql' +}) + +fastify.get('/user/:id', (req, reply) => { + fastify.mysql.getConnection(onConnect) + + function onConnect (err, client) { + if (err) return reply.send(err) + + client.query( + 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], + function onResult (err, result) { + client.release() + reply.send(err || result) + } + ) + } +}) + +fastify.listen(3000, err => { + if (err) throw err + console.log(`server listening on ${fastify.server.address().port}`) +}) +``` + +Use of `mysql.query` +```js +const fastify = require('fastify')() + +fastify.register(require('fastify-mysql'), { + connectionString: 'mysql://root@localhost/mysql' +}) + +fastify.get('/user/:id', (req, reply) => { + fastify.mysql.query( + 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], + function onResult (err, result) { + reply.send(err || result) + } + ) +}) + +fastify.listen(3000, err => { + if (err) throw err + console.log(`server listening on ${fastify.server.address().port}`) +}) +``` +As you can see there is no need to close the client, since is done internally. + +Async/await is supported, when register `promise` option is `true`: +```js +const fastify = require('fastify')() + +fastify.register(require('fastify-mysql'), { + promise: true, + connectionString: 'mysql://root@localhost/mysql' +}) + +fastify.get('/user/:id', async (req, reply) => { + const connection = await fastify.mysql.getConnection() + const [rows, fields] = await connection.query( + 'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id], + ) + connection.release() + return rows[0] +}) + +fastify.listen(3000, err => { + if (err) throw err + console.log(`server listening on ${fastify.server.address().port}`) +}) +``` + +## TypeScript +As `mysql2` expose four different type of client, we do not specify the typing for you. You need to specify the type yourself follow by the example below. +```ts +import { MySQLConnection, MySQLPool, MySQLPromiseConnection, MySQLPromisePool } from 'fastify-mysql' + +// if you only pass connectionString +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPool + } +} + +// if you passed type = 'connection' +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLConnection + } +} + +// if you passed promise = true +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPromisePool + } +} + +// if you passed promise = true, type = 'connection' +declare module 'fastify' { + interface FastifyInstance { + mysql: MySQLPromiseConnection + } +} +``` + +## Acknowledgements + +This project is kindly sponsored by: +- [nearForm](http://nearform.com) +- [LetzDoIt](http://www.letzdoitapp.com/) + +## License + +Licensed under [MIT](./LICENSE). diff --git a/index.d.ts b/index.d.ts index c9bf1fd..3b2151d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,56 +1,56 @@ -import { FastifyPluginCallback } from "fastify"; -import { - Connection, - ConnectionOptions, - escape, - format, - Pool, - PoolOptions, -} from "mysql2"; -import { - Connection as PromiseConnection, - Pool as PromisePool, -} from "mysql2/promise"; - -// upstream package missed type -type escapeId = (val: any, forbidQualified?: boolean) => string; - -interface BaseClient { - format: typeof format; - escape: typeof escape; - escapeId: escapeId; -} - -export type MySQLConnection = Pick & { - connection: Connection; -} & BaseClient; - -export type MySQLPool = Pick & { - pool: Pool; -} & BaseClient; - -export type MySQLPromiseConnection = Pick< - PromiseConnection, - "query" | "execute" -> & { - connection: PromiseConnection; -} & BaseClient; - -export type MySQLPromisePool = Pick< - PromisePool, - "query" | "execute" | "getConnection" -> & { - pool: PromisePool; -} & BaseClient; - -export type ConnectionType = "connection" | "pool"; - -export interface MySQLOptions extends PoolOptions, ConnectionOptions { - type?: ConnectionType; - name?: string; - promise?: boolean; - connectionString?: string; -} - -export const fastifyMySQL: FastifyPluginCallback; -export default fastifyMySQL; +import { FastifyPluginCallback } from "fastify"; +import { + Connection, + ConnectionOptions, + escape, + format, + Pool, + PoolOptions, +} from "mysql2"; +import { + Connection as PromiseConnection, + Pool as PromisePool, +} from "mysql2/promise"; + +// upstream package missed type +type escapeId = (val: any, forbidQualified?: boolean) => string; + +interface BaseClient { + format: typeof format; + escape: typeof escape; + escapeId: escapeId; +} + +export type MySQLConnection = Pick & { + connection: Connection; +} & BaseClient; + +export type MySQLPool = Pick & { + pool: Pool; +} & BaseClient; + +export type MySQLPromiseConnection = Pick< + PromiseConnection, + "query" | "execute" +> & { + connection: PromiseConnection; +} & BaseClient; + +export type MySQLPromisePool = Pick< + PromisePool, + "query" | "execute" | "getConnection" +> & { + pool: PromisePool; +} & BaseClient; + +export type ConnectionType = "connection" | "pool"; + +export interface MySQLOptions extends PoolOptions, ConnectionOptions { + type?: ConnectionType; + name?: string; + promise?: boolean; + connectionString?: string; +} + +export const fastifyMySQL: FastifyPluginCallback; +export default fastifyMySQL; diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..6872830 --- /dev/null +++ b/index.test-d.ts @@ -0,0 +1,83 @@ +import fastify from "fastify"; +import fastifyMysql, { + MySQLConnection, + MySQLPool, + MySQLPromiseConnection, + MySQLPromisePool, +} from "."; + +declare module "fastify" { + interface FastifyInstance { + mysql: + | MySQLPool + | MySQLConnection + | MySQLPromisePool + | MySQLPromiseConnection; + } +} + +const app = fastify(); +app + .register(fastifyMysql, { + connectionString: "mysql://root@localhost/mysql", + }) + .after(function (err) { + const mysql = app.mysql as MySQLPool; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.getConnection(function (err, con) { + con.release(); + }); + mysql.pool.end(); + }); + +app + .register(fastifyMysql, { + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLPromisePool; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + const con = await mysql.getConnection(); + con.release(); + mysql.pool.end(); + }); + +app + .register(fastifyMysql, { + type: "connection", + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLConnection; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + mysql.query("SELECT NOW()", function () {}); + mysql.execute("SELECT NOW()", function () {}); + mysql.connection.end(); + }); + +app + .register(fastifyMysql, { + type: "connection", + promise: true, + connectionString: "mysql://root@localhost/mysql", + }) + .after(async function (err) { + const mysql = app.mysql as MySQLPromiseConnection; + mysql.escapeId("foo"); + mysql.escape("bar"); + mysql.format("baz"); + await mysql.query("SELECT NOW()"); + await mysql.execute("SELECT NOW()"); + mysql.connection.end(); + }); diff --git a/package.json b/package.json index a3b7354..b25aab9 100644 --- a/package.json +++ b/package.json @@ -1,43 +1,43 @@ -{ - "name": "fastify-mysql", - "version": "2.0.1", - "description": "Fastify Mysql connection plugin", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "test": "standard && tap test/*.test.js && npm run typescript", - "typescript": "tsd", - "mariadb": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mariadb:10.1", - "mysql": "npm run mysql:8.0", - "mysql:5.7": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:5.7", - "mysql:5.5": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:5.5", - "mysql:8.0": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:8.0" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/fastify/fastify-mysql.git" - }, - "keywords": [ - "fastify", - "mysql", - "database", - "connection", - "sql" - ], - "license": "MIT", - "bugs": { - "url": "https://github.com/fastify/fastify-mysql/issues" - }, - "homepage": "https://github.com/fastify/fastify-mysql#readme", - "dependencies": { - "fastify-plugin": "^3.0.0", - "mysql2": "^2.0.0" - }, - "devDependencies": { - "@types/node": "^14.14.21", - "fastify": "^3.0.0", - "standard": "^16.0.0", - "tap": "^14.10.7", - "tsd": "^0.14.0" - } -} +{ + "name": "fastify-mysql", + "version": "2.0.1", + "description": "Fastify Mysql connection plugin", + "main": "index.js", + "types": "index.d.ts", + "scripts": { + "test": "standard && tap test/*.test.js && npm run typescript", + "typescript": "tsd", + "mariadb": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mariadb:10.1", + "mysql": "npm run mysql:8.0", + "mysql:5.7": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:5.7", + "mysql:5.5": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:5.5", + "mysql:8.0": "docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes --rm mysql:8.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/fastify/fastify-mysql.git" + }, + "keywords": [ + "fastify", + "mysql", + "database", + "connection", + "sql" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/fastify/fastify-mysql/issues" + }, + "homepage": "https://github.com/fastify/fastify-mysql#readme", + "dependencies": { + "fastify-plugin": "^3.0.0", + "mysql2": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^14.14.21", + "fastify": "^3.0.0", + "standard": "^16.0.0", + "tap": "^14.10.7", + "tsd": "^0.14.0" + } +} diff --git a/test/types/connection-promise/connection-promise.test.ts b/test/types/connection-promise/connection-promise.test.ts deleted file mode 100644 index 2ba9183..0000000 --- a/test/types/connection-promise/connection-promise.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import fastify from "fastify"; -import fastifyMysql, { MySQLPromiseConnection } from "../../.."; - -declare module "fastify" { - interface FastifyInstance { - mysql: MySQLPromiseConnection; - } -} - -const app = fastify(); -app - .register(fastifyMysql, { - type: "connection", - promise: true, - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - await mysql.query("SELECT NOW()"); - await mysql.execute("SELECT NOW()"); - mysql.connection.end(); - }); diff --git a/test/types/connection-promise/tsconfig.json b/test/types/connection-promise/tsconfig.json deleted file mode 100644 index 1d33786..0000000 --- a/test/types/connection-promise/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "." -} \ No newline at end of file diff --git a/test/types/connection/connection.test.ts b/test/types/connection/connection.test.ts deleted file mode 100644 index d3e9035..0000000 --- a/test/types/connection/connection.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import fastify from "fastify"; -import fastifyMysql, { MySQLConnection } from "../../.."; - -declare module "fastify" { - interface FastifyInstance { - mysql: MySQLConnection; - } -} - -const app = fastify(); -app - .register(fastifyMysql, { - type: "connection", - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - mysql.query("SELECT NOW()", function () {}); - mysql.execute("SELECT NOW()", function () {}); - mysql.connection.end(); - }); diff --git a/test/types/connection/tsconfig.json b/test/types/connection/tsconfig.json deleted file mode 100644 index 1d33786..0000000 --- a/test/types/connection/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "." -} \ No newline at end of file diff --git a/test/types/pool-promise/pool-promise.test.ts b/test/types/pool-promise/pool-promise.test.ts deleted file mode 100644 index 3a2956a..0000000 --- a/test/types/pool-promise/pool-promise.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import fastify from "fastify"; -import fastifyMysql, { MySQLPromisePool } from "../../.."; - -declare module "fastify" { - interface FastifyInstance { - mysql: MySQLPromisePool; - } -} - -const app = fastify(); -app - .register(fastifyMysql, { - promise: true, - connectionString: "mysql://root@localhost/mysql", - }) - .after(async function (err) { - const mysql = app.mysql; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - await mysql.query("SELECT NOW()"); - await mysql.execute("SELECT NOW()"); - const con = await mysql.getConnection(); - con.release(); - mysql.pool.end(); - }); diff --git a/test/types/pool-promise/tsconfig.json b/test/types/pool-promise/tsconfig.json deleted file mode 100644 index 1d33786..0000000 --- a/test/types/pool-promise/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "." -} \ No newline at end of file diff --git a/test/types/pool/pool.test.ts b/test/types/pool/pool.test.ts deleted file mode 100644 index 6eddbef..0000000 --- a/test/types/pool/pool.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import fastify from "fastify"; -import fastifyMysql, { MySQLPool } from "../../.."; - -declare module "fastify" { - interface FastifyInstance { - mysql: MySQLPool; - } -} - -const app = fastify(); -app - .register(fastifyMysql, { - connectionString: "mysql://root@localhost/mysql", - }) - .after(function (err) { - const mysql = app.mysql; - mysql.escapeId("foo"); - mysql.escape("bar"); - mysql.format("baz"); - mysql.query("SELECT NOW()", function () {}); - mysql.execute("SELECT NOW()", function () {}); - mysql.getConnection(function (err, con) { - con.release(); - }); - mysql.pool.end(); - }); diff --git a/test/types/pool/tsconfig.json b/test/types/pool/tsconfig.json deleted file mode 100644 index 1d33786..0000000 --- a/test/types/pool/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "." -} \ No newline at end of file