ForrestJS API »

ForrestJS API »

The Philosopy

You can organize your business logic into a ForrestJS App in order to maximize the Single Responsibility and Open/Close principle. And yes, also to maximize code reusability.

You should divide your source files in two categories:

  • Features
  • Services


A Feature is what your customer is willing to pay for.

It's custom made and SHOULD NOT BE SHARED among different Apps.
It most certainly contains business secrets.

✅ Some examples of Features are:

  • A server-side rendered home page
  • An endpoint that list your users from a database
  • An authentication service that builds JWT tokens


A Service is everything that is irrelevant to your customer, but absolutely needed in order to run your Features.

Services are generic and SHOULD BE REUSABLE across different Apps.
Services belong to NPM or your private version of it.

✅ Some examples of Services are:

  • A function that performs an SQL query towards a DBMS
  • An ApolloClient instance that let you fetch from a GraphQL server
  • A component library for React (like MUI)
  • A routing library for React (like react-router)

Quick Start for NodeJS

You can run a simple Fastify app with custom routing and serve a complex website or REST / GraphQL API:

Quick Start for React

You can render a React App and use the ForrestJS compositional approach in your frontend.


A ForrestJS App is heavily inspired to a React app:

  • it receives some properties
  • it has an internal context
  • it has a (boot) lifecycle
  • it produces a predictable result



type:      Array


type:      Array


type:      Object | (async) Function

Pass configuration to your running App.

Any key that you set into this object will be available to any Service, Feature, and registered Action via getConfig() API.

Settings can be calculated on the fly by passing an async Function that will receive the full App context (it is indeed just a registed Action):{
  settings: async ({ setConfig }) => {
    // Get remote data:
    const services = await getServiceDiscoveryInfo();

    // Use the API to extend the configuration:
    setConfg('', services);


type:      Object

Add dependencies to your runing App.

Any key that you set into this object will be available to any Service, Feature, and registered Action via getContext() API.


type:      String
value:     "compact" | "full"

App Boot Lifecycle in ForrestJS

The Boot Lifecycle is composed by a list of asynchronous Extensions created by ForrestJS during the booting of your App.

// Only available to Services
$START            serie
$SETTINGS         serie

// Availabe to Services and Features
$INIT_SERVICES    parallel
$INIT_SERVICE     serie
$INIT_FEATURES    parallel
$INIT_FEATURE     serie

$START_SERVICES   parallel
$START_FEATURES   parallel

$FINISH           serie

👉 Each lifecycle Extension provides the App's Context as first parameter into the Action's handler.

  target: '$INIT_FEATURE',
  handler: (ctx) => {

🔥 All the lifecycle Extensions are ASYNCHRONOUS.
👉 You can use async/await in your Action's handlers, or return promises:

// Assuming you are running `service-pg`
  target: '$START_FEATURE',
  handler: async ({ getContext }) => {
    const query = getContext('pg.query');
    await query('SELECT NOW()');

Action Execution Order

In the rare case in which you REALLY need to control the execution order of a specific Lifecycle Action, please target the singular Extension: $INIT_FEATURE, $START_SERVICE.

👉 When you run Actions in sync, serie, or waterfall, the priority makes perfect sense as the Actions are executed one AFTER another.

🔥 When uou run Actions in parallel, the priority is only used to set the launch order of the handlers. The execution order is entirely unpredictable for it is asynchronous.

results matching ""

    No results matching ""