Skip to content
On this page

Installing Runnable Mini

Overview

Runnable Mini is a great way to get started with Runnable and a perfectly viable long-term solution. It's easy to set up and doesn't require any additional infrastructure.

Runnable Mini integrates directly with your Next.js or Express application by adding a new authenticated route to your existing router framework at a desired path (e.g. /admin).

Installing Runnable

With npm

bash
npm install -D @runnablejs/express
npm install -D @runnablejs/express

or with yarn

bash
yarn add -D @runnablejs/express
yarn add -D @runnablejs/express

or with pnpm

bash
pnpm add -D @runnablejs/express
pnpm add -D @runnablejs/express

Settings up the required env variables

Environment VariableDescriptionHow to Generate
RUNNABLE_BASE_URLThe base URL for your Runnable instance. This has a default of /admin for Runnable Mini, but should override to be /.Should just provide '/'
RUNNABLE_SECRETA secret key used for encrypting the user's cookie session.Run openssl rand -hex 32 in your terminal
RUNNABLE_AUTH_PROVIDER_FORM(Optional) Set to true to enable login via a form. You must also pass verifyLogin option to installRunnable.Set to true or omit.
RUNNABLE_AUTH_PROVIDER_GOOGLE_CLIENT_IDThe client ID for the Google authentication provider.Generated by Google.
RUNNABLE_AUTH_PROVIDER_GOOGLE_CLIENT_SECRETThe client secret for the Google authentication provider.Generated by Google.
RUNNABLE_AUTH_PROVIDER_GOOGLE_HOSTNAMEThe hostname for the Google authentication provider.E.g. https://admin.company-name.com
RUNNABLE_AUTH_PROVIDER_GOOGLE_HDThe hosted domain for the Google authentication provider. Setting this will limit the email domain allowed to login.Provided by Google.

Setting up with Express

ts
// index.ts
import express from 'express';
import { installRunnable } from '@runnablejs/express';
import { getUsers, getTeams, assignTeam } from './db';
import { auth } from './auth';

const app = express();

// ... normal express setup

installRunnable(
  app,
  {
    assign_user_to_team: {
      title: 'Assign a user to a team',
      execute: async (io) => {
        const users = await getUsers();
        const user = await io.select.dropdown({
          label: 'Select a user',
          data: users,
          getLabel: (user) => user.name,
          getValue: (user) => user.id,
        });

        const teams = await getTeams();
        const team = await io.select.table({
          label: 'Select a team',
          data: teams,
          headers: ['Name', 'Team size'],
          initialSelection: user.teamId,
          getValue: (team) => team.id,
          getRow: (team) => [team.name, team.members.length],
        });

        await assignTeam(user.id, team.id);
      },
    },
  },
  { auth: auth }
);

app.listen(3000);
// index.ts
import express from 'express';
import { installRunnable } from '@runnablejs/express';
import { getUsers, getTeams, assignTeam } from './db';
import { auth } from './auth';

const app = express();

// ... normal express setup

installRunnable(
  app,
  {
    assign_user_to_team: {
      title: 'Assign a user to a team',
      execute: async (io) => {
        const users = await getUsers();
        const user = await io.select.dropdown({
          label: 'Select a user',
          data: users,
          getLabel: (user) => user.name,
          getValue: (user) => user.id,
        });

        const teams = await getTeams();
        const team = await io.select.table({
          label: 'Select a team',
          data: teams,
          headers: ['Name', 'Team size'],
          initialSelection: user.teamId,
          getValue: (team) => team.id,
          getRow: (team) => [team.name, team.members.length],
        });

        await assignTeam(user.id, team.id);
      },
    },
  },
  { auth: auth }
);

app.listen(3000);

Setting up with Next.js

Create the providers for the RunnableWorkflows and RunnableAppContext.

ts
// actions.provider.ts

import { FactoryProvider, Provider } from '@nestjs/common';
import { RunnableWorkflows, RunnableAppContext } from '@runnablejs/express';
import { DatabaseService } from './db.service';

export const ActionsProvider: FactoryProvider<RunnableWorkflows> = {
  provide: 'RUNNABLE_ACTIONS',
  inject: [DatabaseService],
  useFactory: (database: DatabaseService) => ({
    assign_user_to_team: {
      // ...
    },
    create_user: {
      // ...
    },
  }),
};

export const RunnableAppContextProvider: Provider<RunnableAppContext> = {
  provide: 'RUNNABLE_CONTEXT',
  useFactory: (authService: AuthService) => ({
    auth: {
      verifyLogin: (opts) => authService.verifyLogin(opts),
    },
  }),
};
// actions.provider.ts

import { FactoryProvider, Provider } from '@nestjs/common';
import { RunnableWorkflows, RunnableAppContext } from '@runnablejs/express';
import { DatabaseService } from './db.service';

export const ActionsProvider: FactoryProvider<RunnableWorkflows> = {
  provide: 'RUNNABLE_ACTIONS',
  inject: [DatabaseService],
  useFactory: (database: DatabaseService) => ({
    assign_user_to_team: {
      // ...
    },
    create_user: {
      // ...
    },
  }),
};

export const RunnableAppContextProvider: Provider<RunnableAppContext> = {
  provide: 'RUNNABLE_CONTEXT',
  useFactory: (authService: AuthService) => ({
    auth: {
      verifyLogin: (opts) => authService.verifyLogin(opts),
    },
  }),
};

Create the module for the Actions and ActionsContext.

ts
// actions.module.ts
import { Module } from '@nestjs/common';
import { ActionsProvider, RunnableAppContextProvider } from './actions.provider';
import { AppController } from './app.controller';

@Module({
  providers: [ActionsProvider, ActionsActionsContextProvider],
})
export class ActionsModule {}
// actions.module.ts
import { Module } from '@nestjs/common';
import { ActionsProvider, RunnableAppContextProvider } from './actions.provider';
import { AppController } from './app.controller';

@Module({
  providers: [ActionsProvider, ActionsActionsContextProvider],
})
export class ActionsModule {}

Start your Next.js application with Actions.

ts
// main.ts
import { installRunnable } from '@runnablejs/express';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Runnable
  const actions = app.get('RUNNABLE_ACTIONS');
  const context = app.get('RUNNABLE_CONTEXT');
  installRunnable(app, actions, context);

  await app.listen(3000);
}
bootstrap();
// main.ts
import { installRunnable } from '@runnablejs/express';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Runnable
  const actions = app.get('RUNNABLE_ACTIONS');
  const context = app.get('RUNNABLE_CONTEXT');
  installRunnable(app, actions, context);

  await app.listen(3000);
}
bootstrap();

Released under the MIT License.