Chassis for Fastify NodeJS applications
Outdated documentation. We are working to update it.
An ESM/CommonJS toolkit to help you to do common operations in your back-end applications with Fastify and NodeJS.
Features
This library was a requirement for some internal projects on our company. But it may work with another projects designed following Oriented-Object Programming pattern.
- Encapsulates Fastify into a manageable class;
- Allow to use common functions for common behavious;
- Manages JWT tokens, environment and logging.
Usage
All classes, methods and functions are well-documented with JSDoc and implementing a flexible types for TypeScript.
API Server / HTTP Server
The HttpInsecureServer
, HttpSecureServer
, Http2InsecureServer
or Http2SecureServer
classes create and manage the fastify
instance, bootstrap it and apply all plugins, routes, hooks attached to options. After bootstraping, it returns the HTTPServer
instance to start, stop and restart the fastify
instance. Below you can see a full implementation example:
You may see a complete example on samples folder.
See the caiquearaujo/fastify-chassis-benchmarks repository to see some benchmarks and advices.
import { FastifyReply, FastifyRequest, RouteGenericInterface } from 'fastify';
import { Server } from 'http';
import path from 'path';
import fastifyRateLimit from '@fastify/rate-limit';
import {
BaseController
ApiServerOptions,
DefaultEnvironment,
FastifyModifierCallable,
FastifyModifiers,
HttpInsecureServer
RequestNotFoundError,
RequestServerError,
AuditRequestLogger,
SyncErrorOnDiskHandler,
} from '@piggly/fastify-chassis';
type MyCurrentServer = Server;
type MyCurrentEnvironment = DefaultEnvironment;
type Request = FastifyRequest<RouteGenericInterface, MyCurrentServer>;
type Reply = FastifyReply<MyCurrentServer>;
const env: MyCurrentEnvironment = {
environment: 'development',
name: 'http-insecure',
port: 3005,
host: '0.0.0.0',
debug: true,
timezone: 'UTC',
log_path: path.resolve(__dirname, 'logs'),
};
class PublicApiController extends BaseController<
MyCurrentServer,
MyCurrentEnvironment,
any
> {
public async helloWorld(request: Request, reply: Reply): Promise<void> {
return reply.send({
message: 'Hello world!',
application: this._env.name,
});
}
}
const PublicApiRoutes: FastifyModifierCallable<MyCurrentServer> = (
app: FastifyInstance<MyCurrentServer>
): Promise<void> => {
const controller = new PublicApiController(env);
app.get('/hello-world', controller.helloWorld.bind(controller));
return Promise.resolve();
};
const rateLimitPlugin: FastifyModifierCallable<
MyCurrentServer,
MyCurrentEnvironment
> = async app => {
await app.register(fastifyRateLimit, {
max: 30,
timeWindow: '1 minute',
});
};
const plugins = new FastifyModifiers<MyCurrentServer, MyCurrentEnvironment>(
rateLimitPlugin
);
import fastifyRateLimit from '@fastify/rate-limit';
const beforeInit: FastifyModifierCallable<
MyCurrentServer,
MyCurrentEnvironment
> = async app => {
console.log('Do something before fastify init.');
AuditRequestLogger(
app,
env.log_path,
env.environment,
env.debug ? 'debug' : 'info'
);
};
const afterInit: FastifyModifierCallable<
MyCurrentServer,
MyCurrentEnvironment
> = async () => {
console.log(
'Do something after fastify init.'
);
app.addHook('onClose', async () => {
await database.quit();
});
};
const options: ApiServerOptions<MyCurrentServer, MyCurrentEnvironment> = {
routes: new FastifyModifiers<MyCurrentServer, MyCurrentEnvironment>(
PublicApiRoutes
),
plugins,
env,
hooks: { beforeInit, afterInit },
errors: {
notFound: new RequestNotFoundError(),
unknown: new RequestServerError(),
handler: SyncErrorOnDiskHandler(env.log_path),
},
};
new HttpInsecureServer(options)
.bootstrap()
.then(server => {
server
.start()
.then(() =>
console.log(
`⚡️ Server started ${environment.host}:${environment.port}.`
)
)
.catch((err: any) => {
console.error('❌ Server failed.');
console.error(err);
process.exit(1);
});
})
.catch((err: any) => {
console.error('❌ Server failed.');
console.error(err);
process.exit(1);
});
JWT Service
This library also help to handleing with access token based in JWT. The JWTAccessTokenService
can issue, get, unlock request and return a middleware to be used at some route. The abstract JWTService
does the implementation to issue/verify JWT token. By default, the implementation of EdDSA
token is JWTEdDSAService
.
const jwt_options = {
issuer: 'string',
audience: ['string'],
accept_issuer: 'string',
accept_audience: 'string',
ed25519: {
public_key: string;
private_key: string;
},
ttl: 300,
required_claims: ['scopes','role'],
}
const jwt_service = new JWTEdDSAService(jwt_options);
const access_token_options = {
unlock_by: {
role: true,
scope: true,
origin: true,
ip: true,
}
};
const access_token_errors = {
forbidden: () => new ForbiddenError();
unauthorized: () => new UnauthorizedError();
missing_header: () => new MissingAuthorizationHeaderError();
invalid_token_type: () => new InvalidAuthorizationHeaderError();
}
const access_token_service = new JWTAccessTokenService(jwt_service, access_token_options, access_token_errors);
fastify.register(
(fastify, options, done) => {
fastify.addHook(
'preHandler',
this._services.AccessTokenService.middleware(
'payment.read',
'customer'
)
);
fastify.get('/payments', this.collection.bind(this));
done();
},
{
prefix: '/public',
}
);
[TIP] Pub/priv key for JWTEdDSAService
The JWTEdDSAService
is expecting the public/private key of type ed25519
to sign/verify JWT tokens. You must issue it following the procedures below:
openssl genpkey -algorithm ed25519 -outform PEM -out private.pem
openssl pkey -in private.pem -pubout >> public.pem
We recommend you to keep 600
or 400
cmod permissions for these files.
Other tools
You may see another tools on this library, such as:
- Predefined errors;
- Singleton to get environment and logger;
- Hook for audit request to include access token data (if available) on logs;
- Date parser, pagination meta handler, and some individual functions.
Feel free to explore.
Installation
This library is ready for ES module or CommonJs module. You must add it by using Node.Js:
npm i --save @piggly/fastify-chassis
Changelog
See the CHANGELOG file for information about all code changes.
Testing the code
This library uses the Jest. We carry out tests of all the main features of this application.
npm run test:once
Contributions
See the CONTRIBUTING file for information before submitting your contribution.
Credits
License
MIT License (MIT). See LICENSE.