Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 146 additions & 112 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,112 +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}`)
})
```

## 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).
56 changes: 56 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +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, "query" | "execute"> & {
connection: Connection;
} & BaseClient;

export type MySQLPool = Pick<Pool, "query" | "execute" | "getConnection"> & {
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<MySQLOptions>;
export default fastifyMySQL;
83 changes: 83 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -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();
});
Loading