NGN Logo

NGN

File-based task runner for Node.js with unmatched flexibility and DX.

What is ngn?

ngn is task scheduler for JavaScript and TypeScript. It allows you to write scheduled tasks as simple files, run one command, and get a built-in dashboard with execution history, logs, persistence layer, backup system, alerting and more. Single npm package is all you need to get started.

This product is currently under development and is used only internally by the team at the moment.


Workflow examples

Examples

bash
# Run all tasks
ngn run

# Run tasks in a specific folder
ngn run --match "tasks/reports/**"

# Run single task every 10 seconds
ngn run:single tasks/cleanup.ts --timing "*/10 * * * * *"

# Test a task without scheduling
ngn run:once tasks/send-email.ts

Command Description
ngn init Initialize ngn in current directory
ngn add <file> Create a new task file in tasks/
ngn run Start scheduler and dashboard
ngn run --match <pattern> Run only tasks matching glob pattern
ngn run:single <file> Run single task with optional custom timing
ngn run:once <file> Execute task once (no scheduling)

Writing Tasks

A task file is as simple as this:

typescript
import { TaskContext } from "@apisurf/ngn";

// Cron pattern (6 fields: second minute hour day month weekday)
export const timing = "0 */5 * * * *"; // Every 5 minutes

// Task function
export const task = async (ctx: TaskContext) => {
  // Your code here
};

Task Context

Every task receives a context object with built-in utilities:

typescript
export const task = async (ctx: TaskContext) => {
  // Environment variables from .env
  const apiKey = ctx.env!.API_KEY;

  // Key-value storage (in-memory)
  await ctx.kv.set("lastRun", new Date().toISOString());
  const lastRun = await ctx.kv.get("lastRun");
  await ctx.kv.delete("lastRun");

  // Structured logging (visible in dashboard)
  await ctx.log.info("Processing started");
  await ctx.log.error("Failed to connect");

  // Performance timing
  const recordTime = ctx.timing.start("api-call");
  await fetch("https://api.example.com");
  await recordTime(); // Records duration in dashboard

  // Access configured plugins
  const sqlite = ctx.plugins.sqlite;
};

Lifecycle Hooks

Control task execution with optional exports:

typescript
// Skip execution conditionally
export const shouldSkip = async (ctx: TaskContext) => {
  const lastRun = await ctx.kv.get("lastRun");
  return lastRun === new Date().toDateString(); // Skip if already ran today
};

// Can also skip unconditionally
export const shouldSkip = true;

Configuration

Create a ngn.config.ts file with defineConfig:

In-Memory Storage

Fast, volatile storage - data is lost on restart:

typescript
import { defineConfig } from "@apisurf/ngn";

export default defineConfig({
  dbPath: ":memory:",
  port: 4545,
  match: ["tasks/**/*.ts"],
  envFile: ".env"
});

File-Based Storage

Persistent SQLite database - survives restarts:

typescript
import { defineConfig } from "@apisurf/ngn";

export default defineConfig({
  dbPath: "file:./db.sqlite",
  port: 4546,
  match: ["tasks/**/*.ts"],
  envFile: ".env"
});
Option Example Description
dbPath :memory: or file:./db.sqlite SQLite database path (in-memory or file-based)
port 4545 Dashboard/API server port
match ["tasks/**/*.ts"] Glob patterns for task file discovery
envFile .env Environment variables file to load
plugins [sqlitePlugin(options)] Array of plugin configurations

Plugins

Extend ngn with plugins for databases, email, storage, and more. Plugins are configured in ngn.config.ts and accessed via ctx.plugins.

SQLite Plugin

Configure with migrations in ngn.config.ts, then use in tasks:

typescript
// tasks/sync-users.ts
export const task = async (ctx: TaskContext) => {
  const sqlite = ctx.plugins.sqlite;

  // Execute queries
  const result = await sqlite.execute("SELECT * FROM users");
  await ctx.log.info("Found " + result.rows.length + " users");
};

Resend Plugin (Email)

typescript
// tasks/send-report.ts
export const task = async (ctx: TaskContext) => {
  const resend = ctx.plugins.resend;

  await resend.send({
    from: "[email protected]",
    to: "[email protected]",
    subject: "Daily Report",
    text: "Your report is ready."
  });
};

S3 Plugin (Storage)

typescript
// tasks/backup.ts
export const task = async (ctx: TaskContext) => {
  const s3 = ctx.plugins.s3;

  await s3.upload({
    bucket: "my-bucket",
    key: "backups/data.json",
    body: Buffer.from(JSON.stringify(data)),
    contentType: "application/json"
  });
};

Available Plugins

Plugin Description
@apisurf/ngn-plugin/sqlite SQLite database with migrations support
@apisurf/ngn-plugin/resend Email sending via Resend API
@apisurf/ngn-plugin/s3 AWS S3 file storage operations
@apisurf/ngn-plugin/supabase Supabase client integration
@apisurf/ngn-plugin/alerting Alert notifications

Cron Patterns

ngn uses 6-field cron expressions (includes seconds):

┌────────────── second (0-59)
│ ┌──────────── minute (0-59)
│ │ ┌────────── hour (0-23)
│ │ │ ┌──────── day of month (1-31)
│ │ │ │ ┌────── month (1-12)
│ │ │ │ │ ┌──── day of week (0-6, Sun=0)
│ │ │ │ │ │
* * * * * *

Common patterns:

Pattern Description
*/5 * * * * * Every 5 seconds
0 * * * * * Every minute
0 */5 * * * * Every 5 minutes
0 0 * * * * Every hour
0 0 9 * * * Daily at 9:00 AM
0 0 9 * * 1-5 Weekdays at 9:00 AM