Skip to content
Open
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
111 changes: 86 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ For Valkey and Redis DB compatibility look [here](https://github.com/valkey-io/v

Add it to your project with `register` and you are done!

### Upgrade notes

- Mixed mode registrations are rejected within the same Fastify instance tree. You cannot mix one default instance and namespaced instances under the same root Fastify instance.
- `clientMode` is validated when provided. Only `'standalone'` and `'cluster'` are accepted; with an existing `client`, the option is validated but does not change the supplied client's mode.
- If `namespace` is provided, it must be a non-empty string.
- TypeScript: `fastify.valkey` is typed as a union (`ValkeyClient | FastifyValkeyNamespacedInstance`). Depending on your usage, you may need to narrow or cast before calling root client methods (for example, `.get`) or namespace properties.

### Create a new Valkey Client

The `options` that you pass to `register` will be passed to the Valkey client.
Expand All @@ -42,13 +49,20 @@ fastify.register(fastifyValkey, {
credentials: {username: "user1", password: "password"},
useTLS: true
})

// OR create a managed cluster client
fastify.register(fastifyValkey, {
clientMode: 'cluster',
addresses: [{ host: '127.0.0.1', port: 6379 }],
periodicChecks: 'enabledDefaultConfigs'
})
```

### Accessing the Valkey Client

Once you have registered your plugin, you can access the Valkey client via `fastify.valkey`.

The client is automatically closed when the fastify instance is closed.
Clients created by this plugin are automatically closed when the fastify instance is closed.

```js
import Fastify from 'fastify'
Expand All @@ -60,16 +74,14 @@ fastify.register(fastifyValkey, {
addresses: [{ host: '127.0.0.1', port: 6379 }],
})

fastify.post('/foo', (request, reply) => {
fastify.valkey.set(request.body.key, request.body.value, (err) => {
reply.send(err || { status: 'ok' })
})
fastify.post('/foo', async (request, reply) => {
await fastify.valkey.set(request.body.key, request.body.value)
reply.send({ status: 'ok' })
})

fastify.get('/foo', (request, reply) => {
fastify.valkey.get(request.query.key, (err, val) => {
reply.send(err || val)
})
fastify.get('/foo', async (request, reply) => {
const val = await fastify.valkey.get(request.query.key)
reply.send(val)
})

try {
Expand Down Expand Up @@ -158,29 +170,25 @@ fastify
})

// Here we will use the `hello` named instance
fastify.post('/hello', (request, reply) => {
fastify.valkey['hello'].set(request.body.key, request.body.value, (err) => {
reply.send(err || { status: 'ok' })
})
fastify.post('/hello', async (request, reply) => {
await fastify.valkey['hello'].set(request.body.key, request.body.value)
reply.send({ status: 'ok' })
})

fastify.get('/hello', (request, reply) => {
fastify.valkey.hello.get(request.query.key, (err, val) => {
reply.send(err || val)
})
fastify.get('/hello', async (request, reply) => {
const val = await fastify.valkey.hello.get(request.query.key)
reply.send(val)
})

// Here we will use the `world` named instance
fastify.post('/world', (request, reply) => {
fastify.valkey.world.set(request.body.key, request.body.value, (err) => {
reply.send(err || { status: 'ok' })
})
fastify.post('/world', async (request, reply) => {
await fastify.valkey.world.set(request.body.key, request.body.value)
reply.send({ status: 'ok' })
})

fastify.get('/world', (request, reply) => {
fastify.valkey['world'].get(request.query.key, (err, val) => {
reply.send(err || val)
})
fastify.get('/world', async (request, reply) => {
const val = await fastify.valkey['world'].get(request.query.key)
reply.send(val)
})

try {
Expand All @@ -191,6 +199,59 @@ try {
}
```

### Comprehensive standalone configuration example

```js
import Fastify from 'fastify'
import fastifyValkey from '@fastify/valkey-glide'
import {
Decoder,
GlideClientConfiguration,
ProtocolVersion
} from '@valkey/valkey-glide'

const fastify = Fastify()

fastify.register(fastifyValkey, {
addresses: [
{ host: '127.0.0.1', port: 6379 },
{ host: '127.0.0.2', port: 6379 }
],
databaseId: 1,
useTLS: true,
credentials: { username: 'user1', password: 'password' },
requestTimeout: 5000,
protocol: ProtocolVersion.RESP3,
clientName: 'fastify-valkey-main',
readFrom: 'preferReplica',
clientAz: 'us-east-1a',
defaultDecoder: Decoder.String,
inflightRequestsLimit: 1000,
lazyConnect: false,
connectionBackoff: {
numberOfRetries: 5,
factor: 500,
exponentBase: 2,
jitterPercent: 20
},
advancedConfiguration: {
connectionTimeout: 1000,
tlsAdvancedConfiguration: {
insecure: false
}
},
pubsubSubscriptions: {
channelsAndPatterns: {
[GlideClientConfiguration.PubSubChannelModes.Exact]: new Set(['updates'])
},
callback: (message, context) => {
console.log('pubsub message', message, context)
},
context: { source: 'fastify' }
}
})
```

## License

Licensed under [MIT](./LICENSE).
28 changes: 24 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
'use strict'

const fp = require('fastify-plugin')
const { GlideClient } = require('@valkey/valkey-glide')
const { GlideClient, GlideClusterClient } = require('@valkey/valkey-glide')

const NAMESPACE_CONTAINER_MARKER = Symbol('fastify.valkey.namespace.container')

async function fastifyValkey (fastify, options) {
const { namespace, closeClient = false, ...valkeyOptions } = options

if (namespace !== undefined && (typeof namespace !== 'string' || namespace.length === 0)) {
throw new Error('Invalid namespace. Expected a non-empty string when namespace is provided')
}

if (valkeyOptions.clientMode !== undefined && valkeyOptions.clientMode !== 'standalone' && valkeyOptions.clientMode !== 'cluster') {
throw new Error("Invalid clientMode. Expected 'standalone' or 'cluster'")
}

let client = options.client || null

if (namespace) {
if (!fastify.valkey) {
fastify.decorate('valkey', Object.create(null))
const namespaceContainer = Object.create(null)
namespaceContainer[NAMESPACE_CONTAINER_MARKER] = true
fastify.decorate('valkey', namespaceContainer)
} else if (!fastify.valkey[NAMESPACE_CONTAINER_MARKER]) {
throw new Error('@fastify/valkey-glide has already been registered')
}
Comment thread
janisto marked this conversation as resolved.
if (fastify.valkey[namespace]) {
throw new Error(`Valkey '${namespace}' instance namespace has already been registered`)
Expand All @@ -23,7 +37,7 @@ async function fastifyValkey (fastify, options) {
fastify.valkey[namespace] = client
} else {
if (fastify.valkey) {
throw new Error('@fastify/valkey has already been registered')
throw new Error('@fastify/valkey-glide has already been registered')
}

const close = (fastify) => { fastify.valkey.close() }
Expand All @@ -40,7 +54,13 @@ async function setupClient (fastify, client, closeClient, valkeyOptions, closeIn
fastify.addHook('onClose', closeInstance)
}
} else {
client = await GlideClient.createClient(valkeyOptions)
const { clientMode = 'standalone', ...options } = valkeyOptions

if (clientMode === 'cluster') {
client = await GlideClusterClient.createClient(options)
} else {
client = await GlideClient.createClient(options)
}

fastify.addHook('onClose', closeInstance)
}
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix",
"redis": "docker run -p 6379:6379 --rm redis:7.2",
"valkey": "docker run -p 6379:6379 --rm valkey/valkey:latest",
"redis": "docker run -p 6379:6379 --name valkey-glide-redis -d --rm redis:latest",
"valkey": "docker run -p 6380:6379 --name valkey-glide-valkey -d --rm valkey/valkey:latest",
"test": "npm run unit && npm run typescript",
"typescript": "tstyche",
"unit": "c8 --100 node --test"
Expand Down Expand Up @@ -58,7 +58,7 @@
},
"dependencies": {
"fastify-plugin": "^5.0.0",
"@valkey/valkey-glide": "^2.0.1"
"@valkey/valkey-glide": "^2.0.0"
},
"publishConfig": {
"access": "public"
Expand Down
Loading