Package Development
Formidable provides an easy way of letting developers extend the framework. This is a great way of building in features that aren't bundled with the framework. For example, the Pretty Errors package adds a new view that gets displayed when there's an error. This view only gets triggered if the application is in debug
mode.
Package Registration
Service Resolver
We can create a service resolver in our package that adds a route that returns a random quote:
- Imba
- TypeScript
import { Route } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
export class QuotesServiceResolver < ServiceResolver
def boot
Route.get 'quote', do
const key = Math.floor(Math.random! * (2 - 1 + 1))
self.quotes[key] ?? 'Could not find a quote'
get quotes
[
'Computers are good at following instructions, but not at reading your mind.'
'UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity.'
]
import { Route } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
export class QuotesServiceResolver extends ServiceResolver {
boot(): void {
Route.get('quote', () => {
const key = Math.floor(Math.random() * (2 - 1 + 1))
this.quotes[key] ?? 'Could not find a quote'
})
}
get quotes(): Array<string> {
return [
'Computers are good at following instructions, but not at reading your mind.'
'UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity.'
]
}
We can then register the resolver in a formidable application in the bootstrap/resolvers.imba
or bootstrap/resolvers.ts
config file:
- Imba
- TypeScript
import { QuotesServiceResolver } from '<custom-package>'
...
export default [
...
QuotesServiceResolver
import { QuotesServiceResolver } from '<custom-package>'
...
export default [
...
QuotesServiceResolver,
Once the resolver has been registered, you can access /quote
in the browser, and you will see any of the 2 quotes you added above.
Publisher
Before a package can be used by Formidable, it needs to be registered. This means a package can be easily turned off and removed from the application boot cycle.
You can ship framework files such as a config
file, along with your package. This can be done by including a Package.js
file in your package:
exports.Package = class Package {
publish(language = 'imba') {
const ext = language.toLowerCase() == 'imba'
? 'imba' : (
language.toLowerCase() == 'typescript' ? 'ts' : 'imba'
)
const configKey = `config/bugsnag.${ext}`;
const configValue = `./formidable/config/bugsnag.${ext}`;
return {
config: {
paths: {
[configKey]: configValue
}
}
}
}
}
For formidable to be aware of the Package.js
file, you must include it in the package.json
npm file with the key publisher
:
{
"name": "my-package",
"version": "1.0.0",
"publisher": "formidable/Package.js",
...
The Package.js
file contains a publish
function which should always return an object. In this object, you can specify the files that must be copied from the package to the framework.
e.g. The package above will copy the formidable/config/bugsnag.imba
or formidable/config/bugsnag.ts
file from the package to config
folder in a formidable application when running the package:publish
Craftsman command:
node craftsman package:publish --package=<package-name> --tag=config
flag | description |
---|---|
--package | npm package that should be published. |
--tag | tags that should be published - paths returned by the publish function |
Server
Hooks
Formidable supports Fastify hooks, in the example below, we will look at how you can register a hook in your service resolver:
- Imba
- TypeScript
import { FastifyRequest } from '@formidablejs/framework'
import { Log } from '@formidablejs/logger'
import { ServiceResolver } from '@formidablejs/framework'
export class HttpLoggerServiceResolver < ServiceResolver
def boot
self.app.addHook 'onRequest', do(request\FastifyRequest)
Log.info "{request.method}: {request.url}"
import { Log } from '@formidablejs/logger'
import { FastifyRequest } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
export class HttpLoggerServiceResolver extends ServiceResolver {
boot(): void {
this.app.addHook('onRequest', (request: FastifyRequest) => {
Log.info("{request.method}: {request.url}")
})
}
This hook logs all requests made to our application.
For a list of fastify hooks you can use: visit Fastify Hooks.
Plugins
We can also register Fastify plugins:
Install under-pressure
:
npm i under-pressure --save
Update your Service Resolver:
- Imba
- TypeScript
import { ServiceResolver } from '@formidablejs/framework'
export class UnderPresureServiceResolver < ServiceResolver
get config
{
maxEventLoopDelay: 1000
maxHeapUsedBytes: 100000000
maxRssBytes: 100000000
maxEventLoopUtilization: 0.98
}
def boot
self.app.register require('under-pressure'), self.config
import { ServiceResolver } from '@formidablejs/framework'
export class UnderPresureServiceResolver extends ServiceResolver {
get config(): object {
return {
maxEventLoopDelay: 1000,
maxHeapUsedBytes: 100000000,
maxRssBytes: 100000000,
maxEventLoopUtilization: 0.98
}
}
boot(): void {
this.app.register(require('under-pressure'), this.config)
}
}
If you want to tinker with the updated Fastify server instance:
- Imba
- TypeScript
import { ServiceResolver } from '@formidablejs/framework'
import { FastifyInstance } from '@formidablejs/framework'
export class UnderPresureServiceResolver < ServiceResolver
get config
{
maxEventLoopDelay: 1000
maxHeapUsedBytes: 100000000
maxRssBytes: 100000000
maxEventLoopUtilization: 0.98
}
def boot
self.app.register require('under-pressure'), self.config, do(error, instance\FastifyInstance)
if error then throw error
# do something with the instance
import { ServiceResolver } from '@formidablejs/framework'
import { FastifyInstance } from '@formidablejs/framework'
export class UnderPresureServiceResolver extends ServiceResolver {
get config(): object {
return {
maxEventLoopDelay: 1000,
maxHeapUsedBytes: 100000000,
maxRssBytes: 100000000,
maxEventLoopUtilization: 0.98
}
}
boot(): void {
this.app.register(require('under-pressure'), this.config, (error, instance: FastifyInstance) => {
if (error) {
throw error
}
// do something with the instance
}
}
}
For a list of fastify plugins you can use: visit Fastify Ecosystem.
Routes
To register a new route, just use the Route
class:
- Imba
- TypeScript
import { ServiceResolver } from '@formidablejs/framework'
import { Route } from '@formidablejs/framework'
import { view } from '@formidablejs/framework'
import { Dashboard } from '../views/Dashboard'
export class DashboardServiceResolver < ServiceResolver
def boot
Route.get 'dashboard', do view(Dashboard)
import { ServiceResolver } from '@formidablejs/framework'
import { Route } from '@formidablejs/framework'
import { view } from '@formidablejs/framework'
import { Dashboard } from '../views/Dashboard'
export class DashboardServiceResolver extends ServiceResolver {
boot(): void {
Route.get('dashboard', () => view(Dashboard))
}
}
In the example above, the Service Resolver adds a new dashboard GET
route.
See Routing for more information.
Commands
You can register a package command using the registerCommand
app function:
- Imba
- TypeScript
import { ServiceResolver } from '@formidablejs/framework'
import { MakeRoleCommand } from '../commands/MakeRoleCommand'
export class DashboardServiceResolver < ServiceResolver
def boot
self.app.registerCommand MakeRoleCommand
import { ServiceResolver } from '@formidablejs/framework'
import { MakeRoleCommand } from '../commands/MakeRoleCommand'
export class DashboardServiceResolver extends ServiceResolver {
boot(): void {
this.app.registerCommand(MakeRoleCommand)
}
}
See Commands for more information.