Rest Gateway API for Lime Survey
  • TypeScript 97.2%
  • Smarty 1.7%
  • JavaScript 0.6%
  • Dockerfile 0.5%
Find a file
Josh 8417862011 docs(helm): add chart readme and post-install notes
README documents the install flows (inline dev vs existingSecret prod)
so operators don't leak credentials into values files checked into
git. NOTES.txt surfaces port-forward commands and warns when the API
key guard is off.
2026-04-16 21:23:26 -04:00
.settings docs(settings): add team skills and feature specs 2026-04-16 20:50:51 -04:00
charts/livesurvey-api docs(helm): add chart readme and post-install notes 2026-04-16 21:23:26 -04:00
cypress test(e2e): add cypress specs for swagger ui and try-it-out 2026-04-16 20:52:37 -04:00
src test(lime): add opt-in live smoke test against survey.ilab.zone 2026-04-16 20:52:43 -04:00
tests/fixtures test(gateway): add mock upstream and full integration harness 2026-04-16 20:52:31 -04:00
.env.example chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
.gitignore chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
.nvmrc chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
cypress.config.ts test(e2e): add cypress specs for swagger ui and try-it-out 2026-04-16 20:52:37 -04:00
docker-compose.yml chore(docker): add multi-stage image and compose with postgres sidecar 2026-04-16 20:52:48 -04:00
Dockerfile chore(docker): add multi-stage image and compose with postgres sidecar 2026-04-16 20:52:48 -04:00
package.json chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
pnpm-lock.yaml chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
README.md docs: add readme with quickstart, swagger, and n8n recipes 2026-04-16 20:52:53 -04:00
tsconfig.build.json chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
tsconfig.json chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00
vitest.config.ts chore: scaffold typescript project with vitest and express tooling 2026-04-16 20:50:57 -04:00

LiveSurvey-API

Read-only LimeSurvey JSON-RPC client, CLI, and REST gateway for N8N integration.

LimeSurvey (survey.ilab.zone)
    ↕ JSON-RPC 1.0 over HTTPS
LiveSurvey-API Gateway  (Express, port 3200)
    ↕ REST JSON over HTTP
N8N  (HTTP Request nodes)
    ↕ SQL
PostgreSQL

Install

cp .env.example .env       # fill in LIMESURVEY_* and optional API_KEY
pnpm install
pnpm build

Node >=20 and pnpm are required.

Configuration

Variable Required Default Purpose
LIMESURVEY_URL yes — Full RPC endpoint, e.g. https://survey.ilab.zone/admin/remotecontrol
LIMESURVEY_USER yes — LimeSurvey admin username
LIMESURVEY_PASSWORD yes — LimeSurvey admin password
LIMESURVEY_AUTH_PLUGIN no Authdb Auth plugin (Authdb, AuthLDAP, etc.)
GATEWAY_PORT no 3200 Port the REST gateway listens on
API_KEY no (none) If set, gateway requires X-API-Key: <value> on /api/*

Running

Gateway (local)

pnpm dev:gateway            # tsx watch
pnpm start:gateway          # compiled dist/gateway/server.js
curl http://localhost:3200/healthz
open http://localhost:3200/docs      # Swagger UI
curl http://localhost:3200/openapi.json   # raw OpenAPI 3.1 spec

Interactive API docs

Swagger UI is mounted at /docs and serves an interactive explorer for every route. The underlying OpenAPI 3.1 spec is at /openapi.json and can be imported into Postman, Insomnia, or any OpenAPI-aware tool.

When API_KEY is configured, use the Authorize button in Swagger UI to set X-API-Key once per session — all Try-it-out calls then carry it automatically.

Gateway (docker)

docker compose up -d --build
curl http://localhost:3200/healthz

CLI

pnpm cli list-surveys
pnpm cli survey-info 42
pnpm cli list-groups 42
pnpm cli list-questions 42 --group 3 --language en
pnpm cli fieldmap 42
pnpm cli export-responses 42 --format csv
pnpm cli export-responses 42 --format xls --out /tmp/out.xls
pnpm cli export-stats 42 --format pdf --out /tmp/stats.pdf
pnpm cli list-participants 42 --limit 200
pnpm cli timeline 42 --type day --start 2026-04-01 --end 2026-04-30

All commands print JSON to stdout. Binary formats (xls, pdf, doc, html) require --out.

REST endpoints

All paths under /api. Set X-API-Key if API_KEY is configured.

Method Path RC2 method
GET /api/surveys list_surveys
GET /api/surveys/:id get_survey_properties
GET /api/surveys/:id/summary get_summary
GET /api/survey-groups list_survey_groups
GET /api/surveys/:id/groups list_groups
GET /api/groups/:id get_group_properties
GET /api/surveys/:id/questions list_questions
GET /api/questions/:id get_question_properties
GET /api/surveys/:id/quotas list_quotas
GET /api/quotas/:id get_quota_properties
GET /api/surveys/:id/fieldmap get_fieldmap
GET /api/surveys/:id/responses export_responses
GET /api/surveys/:id/responses/by-token export_responses_by_token
GET /api/surveys/:id/statistics export_statistics
GET /api/surveys/:id/timeline export_timeline
GET /api/surveys/:id/response-ids get_response_ids
GET /api/surveys/:id/participants list_participants
GET /api/surveys/:id/participants/:tid get_participant_properties
GET /api/surveys/:id/language get_language_properties
GET /api/surveys/:id/files get_uploaded_files
GET /api/site/settings/available get_available_site_settings
GET /api/site/settings/:name get_site_settings
GET /api/users list_users

Key query parameters

  • /api/surveys/:id/responses?format=csv|json|xls|pdf|doc&completion=complete|incomplete|all&heading=code|full|abbreviated&responseType=short|long&language=en&fromId=N&toId=N&fields=f1&fields=f2
  • /api/surveys/:id/responses/by-token?token=<token>&format=json
  • /api/surveys/:id/statistics?format=pdf|xls|html&language=en&graph=true
  • /api/surveys/:id/timeline?type=day|hour&start=YYYY-MM-DD&end=YYYY-MM-DD
  • /api/surveys/:id/response-ids?token=<token>
  • /api/surveys/:id/participants?start=0&limit=100&unused=false
  • /api/surveys/:id/files?token=<token>&responseId=<id>

Response shapes

  • JSON endpoints return parsed JSON.
  • format=csv → text/csv with Content-Disposition: attachment.
  • format=xls|pdf|doc|html → appropriate binary content type with attachment disposition.
  • Errors: {"error": {"code": "<CODE>", "message": "...", "status"?: "..."}} with HTTP status:
    • 400 — malformed query/path
    • 401 — missing/invalid API key
    • 403 — LimeSurvey reports no permission
    • 404 — invalid survey/group/question/quota id, or {status: "No <X> found"}
    • 502 — RPC/auth/transport failure upstream

N8N integration

  1. HTTP Request → GET http://gateway:3200/api/surveys (add X-API-Key if configured).
  2. Item Lists / Split in Batches over survey IDs.
  3. HTTP Request → GET http://gateway:3200/api/surveys/{{$json.sid}}/responses?format=json — returns an already-decoded JSON array.
  4. Code / Set → reshape fields for your schema.
  5. Postgres → INSERT/UPSERT into target table.
  6. Schedule Trigger → cron the whole workflow for periodic sync.

Option B — Execute Command with the CLI

If the gateway cannot be reached from your n8n host, call the CLI in an Execute Command node:

limesurvey-cli list-surveys
limesurvey-cli export-responses 42 --format json

Pipe stdout into a Function node that JSON.parses the string.

Scope & limits

  • Read-only. Mutating RC2 methods (add_*, set_*, delete_*, update_*, import_*, activate_*, invite_*, mail_*, remind_*, upload_file) are intentionally not exposed. See .settings/feature/feature-getSurveys.md for the deferral list.
  • Large exports. Response bodies come back as base64 in a single JSON-RPC payload and are held in memory. Exports over ~100 MB may stress the node process — split with fromId/toId if needed.
  • Auto session handling. The client lazy-acquires a session key, caches it, transparently re-acquires on Invalid session key, and releases it on SIGINT / SIGTERM.

Development

pnpm test              # vitest run — unit + route + integration (~170 tests)
pnpm test:watch        # vitest watch
pnpm typecheck         # tsc --noEmit across src/ + cypress/
pnpm build             # tsc to dist/
LIMESURVEY_SMOKE=1 pnpm test src/lime/smoke.live.test.ts   # opt-in live hit

Test pyramid

Layer Runner Where What it proves
Unit vitest src/**/*.test.ts Transport, error mapping, session, every lime method, config, CLI, middleware
Integration vitest src/gateway/integration.test.ts Real Express + mock LimeSurvey upstream, every route
E2E Cypress cypress/e2e/**.cy.ts Swagger UI renders all routes, Try-it-out hits gateway, a11y checks
Live smoke vitest src/lime/smoke.live.test.ts (opt-in) Real survey.ilab.zone reachable with configured creds

End-to-end (Cypress + mock LimeSurvey)

pnpm e2e              # start mock + gateway, run Cypress headless, tear down
pnpm cypress:open     # interactive mode — boot e2e:server in another shell first
pnpm e2e:server       # boot mock+gateway standalone for manual browser poking

The E2E suite:

  • Boots tests/fixtures/lime-mock.ts on port 4101 — an Express app that replies to every RC2 method with deterministic canned data.
  • Boots the real gateway on port 3200 pointed at the mock.
  • Drives Swagger UI headlessly via Cypress, including a11y checks with cypress-axe.

Live-endpoint verification

With credentials in .env:

LIMESURVEY_SMOKE=1 pnpm test src/lime/smoke.live.test.ts

This is skipped by default so CI without credentials still passes.