Skip to content

Commit

Permalink
feat: Add Fastify v4 compatibility
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Fastify is now a peer-dependency.
BREAKING CHANGE: Removed deprecated options
  • Loading branch information
franky47 committed Oct 27, 2022
1 parent 4d40d6b commit c4cd92b
Show file tree
Hide file tree
Showing 14 changed files with 1,700 additions and 1,680 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,11 +491,6 @@ createServer({
If for some reason you wish to disable service health monitoring, you can set
the `FASTIFY_MICRO_DISABLE_SERVICE_HEALTH_MONITORING` environment variable to `true`.

## Deprecated APIs

- `configure` _(will be removed in v4.x)_: Use `plugins` with full `fastify-autoload` options.
- `routesDir` _(will be removed in v4.x)_: Use `routes` with full `fastify-autoload` options.

## License

[MIT](https://github.com/47ng/fastify-micro/blob/master/LICENSE) - Made with ❤️ by [François Best](https://francoisbest.com)
Expand Down
66 changes: 40 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,48 +24,62 @@
"scripts": {
"test": "jest --coverage --runInBand",
"test:watch": "jest --watch --runInBand",
"build:clean": "rm -rf ./dist",
"prebuild": "rm -rf ./dist && mkdir -p ./dist",
"build": "run-p build:*",
"build:ts": "tsc",
"build": "run-s build:clean build:ts",
"build:copy-worker": "cp -f ./src/logRedactionWorker.mjs ./dist/",
"ci": "run-s build test",
"test:integration": "NODE_ENV=production ts-node ./tests/integration/main.ts",
"prepare": "husky install"
},
"peerDependencies": {
"fastify": "^4"
},
"dependencies": {
"@47ng/check-env": "^2.1.0",
"@sentry/node": "^6.18.1",
"fastify": "^3.27.2",
"fastify-autoload": "^3.11.0",
"fastify-plugin": "^3.0.1",
"fastify-sensible": "^3.1.2",
"@47ng/check-env": "^3.0.0",
"@fastify/autoload": "^5.4.1",
"@fastify/sensible": "^5.1.1",
"@fastify/under-pressure": "^8.1.0",
"@sentry/node": "^7.16.0",
"fastify-plugin": "^4.3.0",
"get-port": "^6.1.2",
"nanoid": "^3.3.1",
"redact-env": "^0.3.1",
"sonic-boom": "^2.6.0",
"under-pressure": "^5.8.0"
"pino-abstract-transport": "^1.0.0",
"redact-env": "^1.0.0",
"sonic-boom": "^3.2.0"
},
"devDependencies": {
"@commitlint/config-conventional": "^16.2.1",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.21",
"@commitlint/config-conventional": "^17.1.0",
"@swc/cli": "^0.1.57",
"@swc/core": "^1.3.11",
"@swc/helpers": "^0.4.12",
"@swc/jest": "^0.2.23",
"@types/jest": "^29.2.0",
"@types/node": "^18.11.5",
"@types/pino": "7.0.5",
"@types/sonic-boom": "^2.1.1",
"axios": "^0.26.0",
"commitlint": "^16.2.1",
"husky": "^7.0.4",
"jest": "^27.5.1",
"axios": "^1.1.3",
"commitlint": "^17.1.2",
"fastify": "^4",
"husky": "^8.0.1",
"jest": "^29.2.2",
"npm-run-all": "^4.1.5",
"regenerator-runtime": "^0.13.9",
"sentry-testkit": "^3.3.7",
"ts-jest": "^27.1.3",
"ts-node": "^10.6.0",
"typescript": "^4.6.2",
"regenerator-runtime": "^0.13.10",
"sentry-testkit": "^5.0.3",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"typescript": "^4.8.4",
"wait-for-expect": "^3.0.2"
},
"jest": {
"verbose": true,
"preset": "ts-jest/presets/js-with-ts",
"transform": {
"^.+\\.(t|j)sx?$": [
"@swc/jest"
]
},
"testEnvironment": "node",
"testMatch": [
"<rootDir>/tests/**/*.test.ts"
],
"testPathIgnorePatterns": [
"/node_modules/",
"<rootDir>/tests/integration/"
Expand Down
4 changes: 2 additions & 2 deletions src/graceful-shutdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// - await async onClose hooks
// - add some options

import { FastifyPluginAsync } from 'fastify'
import type { FastifyPluginAsync } from 'fastify'
import fp from 'fastify-plugin'
import { performance } from 'node:perf_hooks'

Expand Down Expand Up @@ -85,6 +85,6 @@ const gracefulShutdownPlugin: FastifyPluginAsync<GracefulShutdownOptions> =
}

export default fp(gracefulShutdownPlugin, {
fastify: '3.x',
fastify: '4.x',
name: 'fastify-micro:graceful-shutdown'
})
57 changes: 5 additions & 52 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import checkEnv from '@47ng/check-env'
import { checkEnv } from '@47ng/check-env'
import { AutoloadPluginOptions, fastifyAutoload } from '@fastify/autoload'
import sensible from '@fastify/sensible'
import underPressurePlugin from '@fastify/under-pressure'
import Fastify, { FastifyInstance, FastifyServerOptions } from 'fastify'
import { AutoloadPluginOptions, fastifyAutoload } from 'fastify-autoload'
import 'fastify-sensible'
import sensible from 'fastify-sensible'
import underPressurePlugin from 'under-pressure'
import gracefulShutdown, { GracefulShutdownOptions } from './graceful-shutdown'
import { getLoggerOptions, makeReqIdGenerator } from './logger'
import sentry, { SentryOptions } from './sentry'
Expand Down Expand Up @@ -44,19 +43,6 @@ export type Options = FastifyServerOptions & {
*/
redactLogPaths?: string[]

/**
* @deprecated - Use `plugins` instead to load plugins from the filesystem.
*
* Add your own plugins in this callback.
*
* It's called after most built-in plugins have run,
* but before loading your routes (if enabled with routesDir).
*
* This is where we recommend registering interfaces
* to your service's data stores.
*/
configure?: (server: FastifyInstance) => void

/**
* Add custom options for under-pressure
*/
Expand Down Expand Up @@ -89,19 +75,6 @@ export type Options = FastifyServerOptions & {
*/
routes?: AutoloadPluginOptions

/**
* @deprecated - Use `routes` instead, with full `fastify-autoload` options.
*
* Path to a directory where to load routes.
*
* This directory will be walked recursively and any file encountered
* will be registered as a fastify plugin.
* Routes are loaded after `configure` has run (if specified).
*
* Pass `false` to disable (it is disabled by default).
*/
routesDir?: string | false

/**
* Run cleanup tasks before exiting.
*
Expand All @@ -125,12 +98,10 @@ export type Options = FastifyServerOptions & {

export function createServer(
options: Options = {
printRoutes: 'auto',
routesDir: false
printRoutes: 'auto'
}
) {
checkEnv({ required: ['NODE_ENV'] })

const server = Fastify({
logger: getLoggerOptions(options),
genReqId: makeReqIdGenerator(),
Expand All @@ -148,14 +119,6 @@ export function createServer(
if (options.plugins) {
server.register(fastifyAutoload, options.plugins)
}
if (options.configure) {
if (process.env.NODE_ENV === 'development') {
console.warn(
'[fastify-micro] Option `configure` is deprecated. Use `plugins` instead with full fastify-autoload options.'
)
}
options.configure(server)
}

const afterPlugins = server.after(error => {
if (error) {
Expand Down Expand Up @@ -196,16 +159,6 @@ export function createServer(
if (options.routes) {
server.register(fastifyAutoload, options.routes)
}
if (options.routesDir) {
if (process.env.NODE_ENV === 'development') {
console.warn(
'[fastify-micro] Option `routesDir` is deprecated. Use `routes` instead with full fastify-autoload options.'
)
}
server.register(fastifyAutoload, {
dir: options.routesDir
})
}

if (options.cleanupOnExit) {
server.addHook('onClose', options.cleanupOnExit)
Expand Down
17 changes: 17 additions & 0 deletions src/logRedactionWorker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import build from 'pino-abstract-transport'
import * as redactEnv from 'redact-env'

export default async function redactEnvInLogsWorker(options) {
// todo: Build the regexp on the main thread?
const regexp = redactEnv.build(options.redactEnv)
return build(
async function redactEnvInLogs(source) {
for await (let line of source) {
console.log('• ' + redactEnv.redact(line, regexp))
}
},
{
parse: 'lines'
}
)
}
30 changes: 13 additions & 17 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { FastifyLoggerOptions, FastifyRequest } from 'fastify'
import { nanoid } from 'nanoid'
import type { FastifyReply, FastifyRequest } from 'fastify'
import crypto from 'node:crypto'
import pino from 'pino'
import redactEnv from 'redact-env'
import SonicBoom from 'sonic-boom'
import type { Options } from './index'
import { randomID } from './randomID'

function createRedactedStream(
pipeTo: SonicBoom,
Expand All @@ -23,10 +23,11 @@ export function getLoggerOptions({
name,
redactEnv = [],
redactLogPaths = []
}: Options): FastifyLoggerOptions & pino.LoggerOptions & { stream: any } {
}: Options) {
// todo: Move env redaction to a Pino v7+ Transport
return {
level:
process.env.LOG_LEVEL ||
process.env.LOG_LEVEL ??
(process.env.DEBUG === 'true' ? 'debug' : 'info'),
redact: [
// Security redactions
Expand All @@ -47,14 +48,14 @@ export function getLoggerOptions({
commit: process.env.COMMIT_ID?.slice(0, 8)
},
serializers: {
req(req) {
req(req: FastifyRequest) {
return {
method: req.method,
url: req.url,
headers: req.headers
}
},
res(res) {
res(res: FastifyReply) {
// Response has already be sent at time of logging,
// so we need to parse the headers to log them.
// Trying to collect them earlier to show them here
Expand Down Expand Up @@ -91,7 +92,7 @@ export function getLoggerOptions({
}
}

export const makeReqIdGenerator = (defaultSalt: string = nanoid()) =>
export const makeReqIdGenerator = (defaultSalt: string = randomID()) =>
function genReqId(req: FastifyRequest): string {
let ipAddress: string = ''
const xForwardedFor = req.headers['x-forwarded-for']
Expand All @@ -101,17 +102,12 @@ export const makeReqIdGenerator = (defaultSalt: string = nanoid()) =>
? xForwardedFor.split(',')[0]
: xForwardedFor[0].split(',')[0]
} else {
ipAddress = req.socket?.remoteAddress || ''
ipAddress = req.socket?.remoteAddress ?? ''
}
const hash = crypto.createHash('sha256')
hash.update(ipAddress)
hash.update(req.headers['user-agent'] || '')
hash.update(process.env.LOG_FINGERPRINT_SALT || defaultSalt)
const fingerprint = hash
.digest('base64')
.slice(0, 16)
.replace(/\+/g, '-')
.replace(/\//g, '_')

return [fingerprint, nanoid(16)].join('.')
hash.update(req.headers['user-agent'] ?? '')
hash.update(process.env.LOG_FINGERPRINT_SALT ?? defaultSalt)
const fingerprint = hash.digest('base64url').slice(0, 16)
return [fingerprint, randomID(12)].join('.')
}
5 changes: 5 additions & 0 deletions src/randomID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import crypto from 'node:crypto'

export function randomID(bytes: number = 12) {
return crypto.randomBytes(bytes).toString('base64url')
}
2 changes: 1 addition & 1 deletion src/sentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,6 @@ function sentryPlugin(
}

export default fp(sentryPlugin as FastifyPluginCallback<SentryOptions>, {
fastify: '3.x',
fastify: '4.x',
name: 'fastify-micro:sentry'
})
8 changes: 4 additions & 4 deletions tests/basics.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios'
import { nanoid } from 'nanoid'
import { createServer, startServer } from '../src'
import { randomID } from '../src/randomID'

describe('Basics', () => {
beforeEach(() => {
Expand All @@ -15,7 +15,7 @@ describe('Basics', () => {
})

test('Default port should be 3000', async () => {
const key = nanoid()
const key = randomID()
const server = createServer()
server.get('/', (_, res) => {
res.send({ key })
Expand All @@ -28,7 +28,7 @@ describe('Basics', () => {

test('Port should be configurable via the environment', async () => {
process.env.PORT = '3001'
const key = nanoid()
const key = randomID()
const server = createServer()
server.get('/', (_, res) => {
res.send({ key })
Expand All @@ -42,7 +42,7 @@ describe('Basics', () => {

test('Port can be passed as a second argument to `startServer`', async () => {
const server = createServer()
const key = nanoid()
const key = randomID()
server.get('/', (_, res) => {
res.send({ key })
})
Expand Down
Loading

0 comments on commit c4cd92b

Please sign in to comment.