
fastify-fusion

Fastify API framework with best practices and plugins fused together to make it easy to build and maintain your API.
Features
- Fuse - Easily create a Fastify app with sensible defaults via
fuse().
- Start - Easy to start your Fastify app with sensible defaults via
start().
- Cache - High-performance layer 1/2 caching with the
cacheable library integrated as app.cache.
- OpenAPI - OpenAPI docs generated using
fastify-swagger and scalar with sensible defaults.
- Helmet - Security headers set using
@fastify/helmet with sensible defaults.
- CORS - CORS enabled using
@fastify/cors with sensible defaults for apis.
- Logging - Pino Configured using
pino-pretty to make it easy to read and access to a logger instance.
- Rate Limiting - Rate limiting using
@fastify/rate-limit with sensible defaults.
- Static Paths: Default
./public static path and easy to add / configure your own.
- Regularly updated: Updated regularly to keep up with the latest Fastify and TypeScript features.
Table of Contents
Installation
npm install fastify-fusion fastify
Usage
If you already have a Fastify app, you can use fuse to add the default options and plugins to your app.
import fastify from 'fastify';
import { fuse, FuseOptions } from 'fastify-fusion';
const app = fastify();
await fuse(app);
You can also pass in the FuseOptions to customize your fastify instance.
import fastify from 'fastify';
import { fuse, FuseOptions } from 'fastify-fusion';
const fuseOptions: FuseOptions = {
static: true,
log: true,
helmet: false,
rateLimit: false,
cache: true
};
const app = await fuse(app, fuseOptions);
You can also use the built in start() function to get up and running quickly. This function will create a Fastify app and start the server for you.
import fastify from 'fastify';
import { start, fuse } from 'fastify-fusion';
const app = fastify();
await fuse(app);
await start(app);
Fuse Options
You can customize the behavior of fastify-fusion by passing in options to the fuse function or when creating a new Fastify app with fastify().
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
helmet: {
contentSecurityPolicy: false,
},
static: {
path: '/static/',
dir: './static',
},
rateLimit: {
max: 200,
},
};
await fuse(app, options);
Here is the FuseOptions interface with all the available options:
export type FuseOptions = {
static?: boolean | StaticOptions;
log?: boolean | LoggerOptions;
helmet?: boolean | FastifyHelmetOptions;
rateLimit?: boolean | FastifyRateLimitOptions;
cors?: boolean | FastifyCorsOptions;
openApi?: boolean | OpenApiOptions;
cache?: boolean | CacheableOptions;
};
By default, all the options are set to true, which means that all of the default settings will be applied. You can learn about the default settings in each features's documentation below.
Fastify Start
You can start your Fastify app using the start function. This function will start the Fastify server and log the URL where the server is running. This will use the default configuration for the server, which includes a default port of 3000 and a host of 0.0.0.0 if process.env.PORT or process.env.HOST are not set.
import { fastify, start } from 'fastify-fusion';
const app = await fastify();
start(app);
You can customize the port and host by passing in a StartOptions object to the start function.
import { fastify, start, type StartOptions } from 'fastify-fusion';
const app = await fastify();
const options: StartOptions = {
port: 3001,
host: '127.0.0.1',
};
start(app, options);
If you want to also set the startup message when the server starts, you can pass in a message function to the StartOptions. This function will receive the host and port as arguments and should return a string that will be logged to the console.
import { fastify, start, type StartOptions } from 'fastify-fusion';
const app = await fastify();
const options: StartOptions = {
message: (host, port) => `🌏 started successfully at http://${host}:${port}`,
};
start(app, options);
Cache
fastify-fusion integrates the high-performance cacheable library as app.cache, providing layer 1/2 caching capabilities with advanced features like statistics, TTL management, and non-blocking operations.
Default Configuration
export const defaultCacheableOptions: CacheableOptions = {
ttl: "1h",
stats: true,
nonBlocking: true,
};
Basic Usage
Once fused, the cache is available on your Fastify instance as app.cache:
import fastify from 'fastify';
import { fuse } from 'fastify-fusion';
const app = fastify();
await fuse(app);
app.get('/user/:id', async (request, reply) => {
const userId = request.params.id;
const cacheKey = `user:${userId}`;
let user = await request.server.cache.get(cacheKey);
if (!user) {
user = await getUserFromDatabase(userId);
await request.server.cache.set(cacheKey, user, '10m');
}
return user;
});
Configuration Options
You can customize the cache behavior by passing options to the fuse function:
import { fuse, FuseOptions } from 'fastify-fusion';
import fastify from 'fastify';
const app = fastify();
const options: FuseOptions = {
cache: {
ttl: '30m',
stats: true,
nonBlocking: false,
},
};
await fuse(app, options);
You can also disable caching entirely:
const options: FuseOptions = {
cache: false,
};
Cache Operations
The cache instance provides all the standard caching operations:
await app.cache.set('key', 'value', '1h');
const value = await app.cache.get('key');
await app.cache.setMany([
{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2', ttl: '30m' }
]);
const values = await app.cache.getMany(['key1', 'key2']);
const exists = await app.cache.has('key');
await app.cache.delete('key');
await app.cache.clear();
const takenValue = await app.cache.take('key');
Advanced Features
Memoization with wrap()
Automatically cache function results:
const expensiveFunction = app.cache.wrap(async (input) => {
return await processData(input);
}, { ttl: '1h' });
const result1 = await expensiveFunction('test');
const result2 = await expensiveFunction('test');
GetOrSet Pattern
Fetch from cache or compute and store:
const posts = await app.cache.getOrSet('all-posts', async () => {
return await fetchPostsFromAPI();
}, { ttl: '5m' });
Cache Statistics
Monitor cache performance when stats are enabled:
app.get('/cache/stats', async (request, reply) => {
return {
hits: app.cache.stats.hits,
misses: app.cache.stats.misses,
gets: app.cache.stats.gets,
sets: app.cache.stats.sets,
count: app.cache.stats.count
};
});
TTL Formats
The cache supports both milliseconds and human-readable time formats:
await app.cache.set('key', 'value', 3600000);
await app.cache.set('key', 'value', '1h');
await app.cache.set('key', 'value', '30m');
await app.cache.set('key', 'value', '15s');
The cache automatically handles cleanup and provides graceful shutdown when your Fastify server closes.
Static Paths
By default fastify-fusion serves static files from the ./public directory. You can change this by passing in a StaticOptions object to the fuse function. The default configuration serves static files from the /public path. Here is an example of how to customize the static file serving:
const defaultStaticPath = [
{
dir: path.resolve('./public'),
path: '/',
},
];
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
static: {
dir: './static/',
path: '/static',
},
};
await fuse(app, options);
Logging
By default, fastify-fusion uses Pino for logging and configures it with sensible defaults. You can customize the logging behavior by passing in a LoggerOptions object to the fuse function. The default logging configuration uses pino-pretty and here are the default options:
export const defaultLoggingOptions = {
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: true,
ignore: 'pid,hostname',
singleLine: true,
},
},
};
Here is an example of how to customize the logging options:
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
log: {
level: 'info',
prettyPrint: true,
},
};
await fuse(app, options);
If you want to access the logger instance, you can use the logger function. This function will return a logger instance that you can use to log messages in your application. This will use the default logging options if none are provided. Here is an example of how to use the logger instance:
import { logger } from 'fastify-fusion';
const log = logger();
log.info('This is an info message');
Helmet
fastify-fusion uses fastify-helmet to set security headers by default. You can customize the behavior of fastify-helmet by passing in a FastifyHelmetOptions object to the fuse function. The default configuration sets the following headers:
export const defaultFastifyHelmetOptions: FastifyHelmetOptions = {
contentSecurityPolicy: false,
hidePoweredBy: true,
frameguard: {action: 'deny'},
dnsPrefetchControl: {allow: false},
hsts: {
maxAge: 31_536_000,
includeSubDomains: true,
preload: true,
},
noSniff: true,
xssFilter: true,
referrerPolicy: {policy: 'no-referrer'},
crossOriginResourcePolicy: {policy: 'same-origin'},
crossOriginEmbedderPolicy: false,
enableCSPNonces: false,
};
You can customize the security headers by passing in a FastifyHelmetOptions object to the fuse function. The default configuration sets the following:
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
helmet: {
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
},
};
await fuse(app, options);
Rate Limiting
fastify-fusion uses @fastify/rate-limit to limit the number of requests to your API. By default, it allows 100 requests per minute per IP address. You can customize the rate limiting behavior by passing in a RateLimitOptions object to the fuse function. Here is an example of how to customize the rate limiting options:
export const defaultFastifyRateLimitOptions: FastifyRateLimitOptions = {
global: true,
max: 500,
timeWindow: 60_000,
allowList: ['127.0.0.1', '0.0.0.0'],
};
You can customize the rate limiting options by passing in a RateLimitOptions object to the fuse function. Here is an example of how to customize the rate limiting options:
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
rateLimit: {
max: 200,
},
};
await fuse(app, options);
CORS
fastify-fusion uses @fastify/cors to enable CORS for your API. By default, it allows all origins and methods. You can customize the CORS behavior by passing in a FastifyCorsOptions object to the fuse function. Here is an example of how to customize the CORS options:
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
cors: {
origin: [
'https://app.yourdomain.com',
'https://staging.yourdomain.com'
],
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Bearer'],
exposedHeaders: ['Content-Length', 'X-Requested-With'],
credentials: true,
},
};
await fuse(app, options);
Here are the default CORS options:
export const defaultFastifyCorsOptions: FastifyCorsOptions = {
origin: true,
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Bearer'],
exposedHeaders: ['Content-Length', 'X-Requested-With'],
credentials: true,
};
Open API and Docs UX
fastify-fusion integrates with fastify-swagger and scalar to provide OpenAPI documentation and a user-friendly interface for exploring your API. By default, it serves the OpenAPI documentation at /docs/json and the Scalar UX at /. You can customize the route prefixes and the static path for the docs UX.
You can customize the OpenAPI options by passing in an OpenApiOptions object to the fuse function. Here is an example of how to customize the OpenAPI options:
export type OpenApiOptions = {
title?: string;
description?: string;
version?: string;
openApiRoutePrefix?: string;
docsRoutePath?: string;
};
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
openApi: {
openApiRoutePrefix: '/api-docs',
docsRoutePath: '/docs',
},
};
await fuse(app, options);
You can also set it to false to disable the OpenAPI documentation and Scalar UX:
import { fuse, FuseOptions } from 'fastify-fusion';
import Fastify from 'fastify';
const app = Fastify();
const options: FuseOptions = {
openApi: false,
};
await fuse(app, options);
How to Contribute
If you want to contribute to this project, please read the Contributing Guide for more information on how to get started.
Licensing and Copyright
This project is licensed under the MIT License. Copyright (c) Jared Wray.