- TypeScript 97.2%
- Smarty 1.7%
- JavaScript 0.6%
- Dockerfile 0.5%
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. |
||
|---|---|---|
| .settings | ||
| charts/livesurvey-api | ||
| cypress | ||
| src | ||
| tests/fixtures | ||
| .env.example | ||
| .gitignore | ||
| .nvmrc | ||
| cypress.config.ts | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
| vitest.config.ts | ||
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/csvwithContent-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/path401— missing/invalid API key403— LimeSurvey reports no permission404— invalid survey/group/question/quota id, or{status: "No <X> found"}502— RPC/auth/transport failure upstream
N8N integration
Option A — REST via HTTP Request nodes (recommended)
- HTTP Request →
GET http://gateway:3200/api/surveys(addX-API-Keyif configured). - Item Lists / Split in Batches over survey IDs.
- HTTP Request →
GET http://gateway:3200/api/surveys/{{$json.sid}}/responses?format=json— returns an already-decoded JSON array. - Code / Set → reshape fields for your schema.
- Postgres → INSERT/UPSERT into target table.
- 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.mdfor 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/toIdif needed. - Auto session handling. The client lazy-acquires a session key, caches it, transparently re-acquires on
Invalid session key, and releases it onSIGINT/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.tson 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.