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 Variable | Description | How to Generate |
---|---|---|
RUNNABLE_BASE_URL | The base URL for your Runnable instance. This has a default of /admin for Runnable Mini, but should override to be / . | Should just provide '/' |
RUNNABLE_SECRET | A 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_ID | The client ID for the Google authentication provider. | Generated by Google. |
RUNNABLE_AUTH_PROVIDER_GOOGLE_CLIENT_SECRET | The client secret for the Google authentication provider. | Generated by Google. |
RUNNABLE_AUTH_PROVIDER_GOOGLE_HOSTNAME | The hostname for the Google authentication provider. | E.g. https://admin.company-name.com |
RUNNABLE_AUTH_PROVIDER_GOOGLE_HD | The 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();