Skip to content

Backend Implementation

FlowDrop is a frontend editor that calls your backend REST API. This guide explains what endpoints to implement, what request/response formats FlowDrop expects, and how to get a working backend running.

Not all endpoints are required. Here they are organized by priority:

These 5 endpoints are the bare minimum to get FlowDrop working:

MethodPathPurpose
GET/healthHealth check (FlowDrop checks this on mount)
GET/nodesList available node types
GET/workflows/:idLoad a workflow
POST/workflowsCreate a new workflow
PUT/workflows/:idUpdate an existing workflow

These endpoints enable the complete sidebar, categories, and port validation:

MethodPathPurpose
GET/categoriesNode category definitions (sidebar groups)
GET/port-configPort data types and compatibility rules
GET/nodes/:idGet a single node’s metadata
GET/workflowsList all workflows
DELETE/workflows/:idDelete a workflow

These enable playground, execution, interrupts, and settings:

MethodPathPurpose
POST/workflows/:id/executeExecute a workflow
GET/workflows/:id/executions/:eid/statusExecution status
POST/playground/sessionsCreate playground session
GET/playground/sessions/:sid/messagesPoll for messages
POST/playground/sessions/:sid/messagesSend user message
GET/interrupts/:idGet pending interrupt
POST/interrupts/:id/resolveResolve an interrupt
GET/system/configRuntime configuration
GET/settingsUser settings
PUT/settingsUpdate user settings

All paths above are relative to a base URL you configure:

import { createEndpointConfig } from '@flowdrop/flowdrop/core';
const endpointConfig = createEndpointConfig('/api/flowdrop');
// Nodes endpoint becomes: GET /api/flowdrop/nodes

FlowDrop calls this to verify the backend is reachable.

Response:

{
"status": "ok",
"version": "1.0.0"
}

Returns all available node types. FlowDrop uses this to populate the sidebar.

Query parameters:

  • category (optional) — filter by category
  • search (optional) — search name/description
  • limit (optional, default: 100)
  • offset (optional, default: 0)

Response:

{
"success": true,
"data": [
{
"id": "text_input",
"name": "Text Input",
"description": "Accepts text from the user",
"type": "simple",
"category": "inputs",
"icon": "mdi:text-box-outline",
"inputs": [],
"outputs": [
{
"id": "output",
"name": "Text",
"type": "output",
"dataType": "string"
}
],
"configSchema": {
"type": "object",
"properties": {
"placeholder": {
"type": "string",
"title": "Placeholder",
"default": "Enter text..."
}
}
}
}
]
}

Key fields in NodeMetadata:

  • id (required) — unique identifier
  • name (required) — display name
  • type — node visual type: workflowNode, simple, square, tool, gateway, terminal, idea, note
  • category — sidebar group: inputs, outputs, models, processing, logic, tools, etc.
  • iconIconify icon ID (e.g., mdi:text-box-outline)
  • inputs / outputs — port definitions with id, name, type, dataType
  • configSchema — JSON Schema defining the configuration form

Creates a new workflow. FlowDrop sends the full workflow JSON.

Request body:

{
"name": "My Workflow",
"description": "A simple workflow",
"nodes": [
{
"id": "node-1",
"type": "simple",
"position": { "x": 100, "y": 200 },
"data": {
"label": "Text Input",
"config": { "placeholder": "Enter text..." },
"metadata": { "id": "text_input", "name": "Text Input", "...": "..." }
}
}
],
"edges": [
{
"id": "edge-1",
"source": "node-1",
"sourceHandle": "output",
"target": "node-2",
"targetHandle": "input"
}
]
}

Response:

{
"success": true,
"data": {
"id": "wf-abc123",
"name": "My Workflow",
"nodes": [],
"edges": [],
"metadata": {
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
}
}

Updates an existing workflow. Same request body format as POST.

Returns a single workflow by ID. Same response format as POST response.

Returns category definitions for the node sidebar.

Response:

{
"success": true,
"data": [
{
"id": "inputs",
"name": "Inputs",
"description": "Data input nodes",
"icon": "mdi:import",
"color": "var(--fd-node-emerald)",
"weight": 10
},
{
"id": "processing",
"name": "Processing",
"description": "Data transformation nodes",
"icon": "mdi:cog",
"color": "var(--fd-node-blue)",
"weight": 30
}
]
}

Returns data type definitions and compatibility rules for port connections.

Response:

{
"success": true,
"data": {
"version": "1.0.0",
"defaultDataType": "string",
"dataTypes": [
{
"id": "string",
"name": "String",
"description": "Text data",
"color": "#10b981",
"category": "basic"
},
{
"id": "json",
"name": "JSON",
"description": "Structured data",
"color": "#f59e0b",
"category": "complex"
}
],
"compatibilityRules": [
{ "from": "string", "to": "json" },
{ "from": "json", "to": "string" }
]
}
}

FlowDrop runs in the browser, so your backend must allow cross-origin requests if served from a different domain:

// Express example
import cors from 'cors';
app.use(
cors({
origin: 'http://localhost:5173', // your frontend URL
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
})
);

When an operation fails, return a consistent error format:

{
"success": false,
"error": "Workflow not found",
"code": "NOT_FOUND",
"message": "No workflow exists with ID 'wf-xyz'"
}

FlowDrop’s API client expects standard HTTP status codes:

  • 200 — success
  • 201 — created
  • 400 — bad request (validation error)
  • 401 — unauthorized (triggers onApiError and auth provider’s onUnauthorized)
  • 404 — not found
  • 500 — server error

For simple use cases, you can serve node metadata as static JSON:

// nodes.json — serve as a static file
const nodes = [
{ id: 'text_input', name: 'Text Input', ... },
{ id: 'http_request', name: 'HTTP Request', ... }
];
app.get('/api/flowdrop/nodes', (req, res) => {
res.json({ success: true, data: nodes });
});

For dynamic use cases, load from a database:

app.get('/api/flowdrop/nodes', async (req, res) => {
const nodes = await db
.collection('nodes')
.find({
...(req.query.category && { category: req.query.category })
})
.toArray();
res.json({ success: true, data: nodes });
});