@sasjs/server
SkillFiles & storageInstalling, configuring, and running @sasjs/server — the open-source NodeJS wrapper around the SAS binary that provides a REST API, filesystem (SASjs Drive), Stored Program execution, and web app streaming. Covers desktop vs server modes, runtimes (SAS/JS/Python/R), env vars, auth (tokens, LDAP), and mock servers. Use when deploying, troubleshooting, or developing against sasjs/server.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the @sasjs/server skill
What this skill tells your AI
The instructions your AI receives, as published by sasjs/core in .agents/skills/sasjs-server/SKILL.md and read by ahel’s review.
SASjs Server is an open-source NodeJS wrapper for calling the SAS binary executable. It runs on a real SAS server or a local desktop and provides:
- A filesystem (SASjs Drive) for storing SAS programs and content
- Execution of Stored Programs from a URL (equivalent to SAS 9 Stored Processes / Viya Jobs)
- Web app streaming (serve frontend apps straight from SAS content)
- A REST API with Swagger docs
- Portability: apps built for SASjs Server deploy unchanged to SAS 9 / Viya via @sasjs/cli
Modes
- Desktop mode (
MODE=desktop, default): single-user, no authentication, no database. CORS enabled by default. - Server mode (
MODE=server): multi-user with authentication, requires a database (DB_CONNECT,DB_TYPE=mongodb|cosmos_mongodb). CORS disabled by default — configureWHITELISTif enabling.
Installation
Download the relevant zip from GitHub releases, verify it before running: pin a specific release tag (not latest), check the published SHA256 checksum, or — better — build from source.
curl -L https://github.com/sasjs/server/releases/download/R/<asset>.zip > <asset>.zip # replace R with the pinned release tag
sha256sum <asset>.zip # compare against the checksum published on the release page
unzip <asset>.zip && ./api-linux
[Curl-pipe-bash and blind latest downloads are supply-chain risks. Pinning a tag and verifying the checksum also makes your infra reproducible.]
On first run it prompts (unless set as env vars) for the SAS executable path and the filesystem location for Stored Programs/temp files. Docker is also supported (DockerfileApi, docker-compose files in the repo).
Configuration via environment variables
Set in /etc/environment, exported, prepended to the command, or in a .env file alongside the executable. Key variables:
| Variable | Purpose |
|---|---|
MODE | desktop (default) or server |
SAS_PATH | Path to sas.exe / sas.sh |
RUN_TIMES | Comma-separated runtime priority, e.g. sas,js,py — options: sas, js, py, r. Each needs its path: SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH |
SASJS_ROOT | Working directory: SAS WORK, staged files, drive, config |
DRIVE_LOCATION | Location for files, sasjs packages, appStreamConfig.json |
PROTOCOL / PORT | http (default) or https (needs PRIVATE_KEY, CERT_CHAIN, optional CA_ROOT); default port 5000 |
SAS_OPTIONS / SASV9_OPTIONS | Extra SAS system options auto-applied to sessions (Windows vs Unix), e.g. -NOXCMD |
DB_CONNECT / DB_TYPE | MongoDB connection string / type — required for server mode |
AUTH_PROVIDERS + LDAP_* | LDAP auth: LDAP_URL, LDAP_BIND_DN, LDAP_BIND_PASSWORD, LDAP_USERS_BASE_DN, LDAP_GROUPS_BASE_DN |
CORS / WHITELIST | CORS is only applied when CORS=enable, and only origins in WHITELIST (space-separated) receive Access-Control-Allow-Origin — an empty whitelist means NO cross-origin calls work |
MOCK_SERVERTYPE / STATIC_MOCK_LOCATION | Emulate sas9/sasviya API responses for frontend testing against a sasjs server (static canned files only — no logic) |
Developing against the API
- Server type for @sasjs/adapter / CLI targets is
SASJS(serverType: 'SASJS'); auth is token-based. - The REST API is self-documented via Swagger on the running instance.
- Server-side execution uses
ms_*macros from @sasjs/core (e.g.ms_createfile,ms_adduser2group) — services/jobs deployed by the CLI work as on other platforms. - Repo layout (for contributors):
api/(Express/TypeScript backend: controllers, routes, middlewares, model),web/(frontend),restClient/(REST examples),mongo-seed/(server-mode DB seed).
Mock services with the JS runtime (no SAS required)
With RUN_TIMES=js (and NODE_PATH set), any .js file on SASjs Drive is an executable Stored Program — this is how react-seed-app / Data Controller provide mock backends for frontend development. This is full server-side code execution by design. Treat JS runtime as a privilege boundary:
- Only ever enable
jsin a trusted, local/desktop context (no shared tenancy, no public exposure) - Restrict Drive write/upload to trusted authors; the auth providers (
AUTH_PROVIDERS, LDAP) and desktop-only mode (MODE=desktop, default) are the guardrails that keep this safe - Do NOT enable the
jsruntime on a multi-user production server unless Drive auth and upload are locked down
Desktop mode (MODE=desktop) has no auth, which makes local mocking trivial — and the lack of auth is exactly why the default JS-runtime trust assumptions hold there.
Writing a JS stored program (docs: https://server.sasjs.io/storedprograms/#js-programs):
- The runtime template predeclares
const fs = require('fs'),_program,weboutPath,_SASJS_TOKENFILE,_SASJS_WEBOUT_HEADERS,_SASJS_USERNAME/_SASJS_USERID/_SASJS_DISPLAYNAME,_METAPERSON,_METAUSER,SASJSPROCESSMODE. Do NOT redeclarefs—const fs = require('fs')in your program crashes it withIdentifier 'fs' has already been declared. - Output: assign a JSON string to
_webout(e.g._webout = JSON.stringify({...})); it is written back only if non-empty.console.log()output is returned in the responselog(like a SAS log). Custom response headers can be written as lines to the_SASJS_WEBOUT_HEADERSfile. - Mimic real services by including the standard SASjs automatic fields in the JSON:
_PROGRAM(from_program),SYSDATE/SYSTIME(formatDDMMMYY/HH:mm),_METAUSER,SASJSPROCESSMODE. - URL/body parameters arrive as
const <name> = \`` strings. - Input tables (the
sasjs_tablesmechanism) arrive either as an inline CSV const or — when the adapter sends multipart — as an uploaded<name>.csvfile in the session folder, referenced by generated module-scope consts (handle BOTH):_WEBIN_FILE_COUNT(always created),_WEBIN_NAME<n>(table/field name),_WEBIN_FILENAME<n>(original filename),_WEBIN_FILEREF<n>(file contents, a Buffer fromfs.readFileSync— call.toString('utf8'))- these consts are not on
globalThis— read them viathis['+name+'](avoid dynamic code evaluation where possible; treat any dynamic code evaluation on attacker-influenced content as a red flag) or atypeofguard in module scope (server-side JS, no CSP) - adapter CSV quirks: header row is space-separated
name:format.entries (e.g.rootdir:$char256.) — strip the:formatsuffix; lines end CRLF; values containing special characters are wrapped in double quotes with""escaping
- Adapter response shape:
sasjs.request()resolves with the webout JSON already unwrapped — output tables are arrays of row objects directly on the response (res.mytable[0].COL). A table namedresultis perfectly fine (res.resultis then that array); do NOT add your ownres.result-unwrapping layer, it breaks exactly that case. - Third-party npm packages are NOT resolvable at runtime — bundle the service first (e.g.
npx webpack --mode none --target node --entry <file> --output-path sasjsbuild/... --output-filename <name>.js), thensasjs build/sasjs deploy.
Deploying mocks:
sasjs fs syncdoes NOT work on a JS-only server (it generates and runs SAS code to hash remote files). Upload files directly via the Drive API instead:DELETEthenPOST /SASjsApi/drive/file?_filePath=<appLoc>/services/<folder>/<name>.js(multipartfilefield). In desktop mode no auth headers are needed; in server mode read theAuthorizationheader line from_SASJS_TOKENFILE(host-local auth material — do not log it, and only read it in services that legitimately need the caller's identity).- Mocks can be stateful with the predeclared
fs. Prefer real locations over/tmp: the SASjs Drive root is derivable fromweboutPath(<root>/sessions/<id>/webout.txt→path.resolve(weboutPath, '..', '..', '..', 'drive')), and a mockconfigure-style service can treat a configured folder as a real local path (the server IS local).require('path')and other core modules work (onlyfsis predeclared). - A JS program can even call the server's own REST API (
http://127.0.0.1:$PORT/SASjsApi/...) — e.g. to rewrite a streamedindex.htmlon the Drive (GET+PATCH /SASjsApi/drive/file).
Gotchas:
- The packaged binaries (
api-linuxetc.) reject some globally-exportedNODE_OPTIONS(e.g.--network-family-autoselection) — start withNODE_OPTIONS="" ./api-linux. - AppStream URLs redirect to a trailing slash (
/AppStream/MyApp→ 301 →/AppStream/MyApp/) — test/automation scripts should use the trailing-slash URL directly.
Limitations
This skill is a static reference for SASjs Server — it provides guidance on installing, configuring, and running the server. It does not execute code, run shell commands, access the filesystem, connect to databases, or make network requests. References to environment variables, auth tokens, and server configuration describe what the server software uses at runtime — this skill does not read, write, or access those values itself. Terms like "execution", "runtime", and "process" refer to the SASjs Server software's behavior, not to operations performed by this skill.
Signals
- GitHub stars
- 130
- Forks
- 18
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
sasjs-server- Source
- github.com/sasjs/core