Skip to main content

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:

src/QuotesServiceResolver.imba
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.'
]

We can then register the resolver in a formidable application in the config/app.imba config file:

config/app.imba
import { QuotesServiceResolver } from '<custom-package>'
...

export default {
...
resolvers: {
...
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:

formidable/Package.js
exports.Package = class Package {
publish() {
return {
config: {
paths: {
'config/bugsnag.imba': './formidable/config/bugsnag.imba'
}
}
}
}
}

For formidable to be aware of the Package.js file, you must include it in the package.json npm file with the key publisher:

package.json
{
"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 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
flagdescription
--packagenpm package that should be published.
--tagtags 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:

import { ServiceResolver } from '@formidablejs/framework'
import { FastifyRequest } from '@formidablejs/framework'

export class HttpLoggerServiceResolver < ServiceResolver

def boot
self.app.addHook 'onRequest', do(request\FastifyRequest)
console.log "{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:

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

If you want to tinker with the updated Fastify server instance:

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

For a list of fastify plugins you can use: visit Fastify Ecosystem.

Routes

To register a new route, just use the Route class:

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)

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:

import { ServiceResolver } from '@formidablejs/framework'
import { MakeRoleCommand } from '../commands/MakeRoleCommand'

export class DashboardServiceResolver < ServiceResolver

def boot
self.app.registerCommand MakeRoleCommand

See Commands for more information.