npx skills add ...
npx skills add sasjs/skills --skill sasjs-adapter
Frontend/Node integration with SAS backends using @sasjs/adapter - configuring the SASjs class, the exact request() inputs and response shape, authentication, file upload, and session management. Use when writing TypeScript/JavaScript that calls SAS services or jobs.
npx skills add sasjs/skills --skill sasjs-adapter
@sasjs/adapter is the TypeScript library for calling SAS services/jobs from browsers or Node, with a unified API across three server types: SAS9, SASVIYA, SASJS.
sasJob: relative path (no leading slash) resolved against appLoc, or an absolute path. Becomes the SAS _program parameter.data: an object whose keys become work datasets in SAS. Can be null if the service takes no input.config: overrides merged on top of the constructor config for this call only.data object - tables, not arbitrary JSONEvery key in data must be an array of plain objects. Each key becomes a SAS work dataset named after the key; each object in the array is a row; each property is a column.
On the SAS side these arrive as work.customers and work.config.
Before sending, the adapter validates data (see validateInput). Violations reject the promise with an ErrorResponse:
data must be null or a plain object (not an array, not a primitive)./^[a-zA-Z_][a-zA-Z0-9_]*$/. Numbers at the start are rejected.undefined (it must be null, a string, a number, or a boolean).Each table is converted to CSV via convertToCSV. The conversion is type-aware:
" is escaped as "".. (SAS missing).null/number values and special-missing strings (.a-.z, _), the column is typed as best.. Otherwise the first non-empty value determines chars vs number.name:$char20. id:best. - the SAS side uses this to assign informats.The max length of a string value in SASjs is 32765 characters.$ (e.g. $customers) is treated as a formats definition for the customers table and is not sent as a separate dataset. Its formats property maps column names to SAS formats.The adapter picks the transport based on payload size and server type (see WebJobExecutor):
sasjs{N}data (N = 1, 2, ...). A sasjs_tables field lists the table names space-separated. If a single CSV exceeds 16000 chars it is split into chunks: sasjs{N}data0 holds the chunk count, sasjs{N}data1..N hold the pieces.JSON.stringify(data) exceeds 500000 chars or contains a ;): each table is appended as a CSV file ({tableName}.csv) in multipart form data.In both cases the body is multipart/form-data with debug params (_debug=131, _omittextlog=false, _omitSessionResults=false) when debug: true.
extraResponseAttributesDefault (no extraResponseAttributes): the resolved value is the parsed webout object directly - whatever JSON the SAS service wrote to _webout:
With extraResponseAttributes (e.g. ['log']): the result is wrapped:
ExtraResponseAttributes is 'file' | 'data' | 'log'. Only log is fully implemented in current releases.
%webout/mp_jsonout macro on the SAS side, not the adapter. The adapter passes the webout JSON through as-is.The service must write valid JSON to _webout. Conventionally via %webout (from @sasjs/core), which wraps mp_jsonout. The JSON shape is an object whose keys are table names, each mapping to an array of row objects:
When config.debug is true:
>>weboutBEGIN<< and >>weboutEND<<. The adapter extracts the JSON between the markers.parseSasViyaDebugResponse / parseSasViyaLogDebugResponse, which split out the log and the webout JSON.In all cases, with debug on and extraResponseAttributes: ['log'], the SAS log is available in res.log.
Every executed request is appended to an in-memory history (capped at requestHistoryLimit, default 10):
sourceCode and generatedCode are only populated when debug: true. The history is a ring buffer - oldest entries are dropped when the limit is exceeded.
sasjs.logIn(username, password) (form-based against the stored process server). Session cookie is managed automatically.sasjs add cred) for Node usage. For Node, pass authConfig (client/secret/access/refresh tokens) to request().SASjs - main facade: request(), logIn()/logOut(), uploadFile(), executeScript(), startComputeJob(), deployServicePack()RequestClient - HTTP layer; holds the request history (getRequests()), CSRF tokens, and auth headersWebJobExecutor - SAS 9 STP, Viya web JES, and SASjs server web (multipart form POST)ComputeJobExecutor - Viya compute API (direct code submission on a named context)JesJobExecutor - Viya JES API (job submission via the jobs API, not the web app)SasjsJobExecutor - SASjs server web executionSessionManager - Viya compute session lifecycleSASViyaApiClient / SAS9ApiClient / SASjsApiClient - low-level per-platform clients (rarely needed directly)file/ utilities - file upload to SAS (binary content handling)debug: true to surface the SAS log in responses while developing.response.status / error responses - SAS-side errors (e.g. from %mp_abort) come back in the JSON, not necessarily as HTTP errors.appLoc consistent with the appLoc in sasjsconfig.json used to deploy.null in a numeric input column becomes SAS missing (.); empty string in a char column becomes empty string. Don't use undefined - validation rejects it.The package root index.js is a UMD bundle exposing a global SASjs. Pattern (from the minimal seed app):
"prepare": "cp node_modules/@sasjs/adapter/index.js src/sasjs.js" in package.json (runs on npm i).<script src="sasjs.js"></script> before your app script.<sasjs serverType="SASJS" appLoc="/Public/app/myapp" debug="false"></sasjs> and read attributes with document.querySelector('sasjs'). When the app is streamed by SAS itself, omit serverUrl - same-origin requests just work (CSP default-src 'self' safe).This skill is a static reference for the @sasjs/adapter library - it provides guidance on the API, request/response shapes, authentication, and CSV serialization. It does not execute code, run shell commands, access the filesystem, connect to databases, or make network requests. All code examples are illustrative; the user must integrate them into their own application. References to authentication tokens describe what the adapter library manages at runtime - this skill does not read, write, or access those values itself.
sasjs.request(
sasJob: string,
data: { [key: string]: any[] } | null,
config?: { [key: string]: any }, // merged over SASjsConfig
loginRequiredCallback?: () => any,
authConfig?: AuthConfig, // Viya tokens for Node usage
extraResponseAttributes?: ExtraResponseAttributes[] // ['log'] | ['file'] | ['data']
)const res = await sasjs.request('services/common/getdata', {
customers: [
{ id: 1, name: 'Acme', active: true },
{ id: 2, name: 'Globex', active: false }
],
config: [{ rootdir: '/tmp', retries: 3 }] // even a single-row "config" table
})// SAS service wrote: {"mydata":[{"COL1":"x","COL2":1}]}
const res = await sasjs.request('services/getdata', { in: [{ a: 1 }] })
// res === { mydata: [{ COL1: 'x', COL2: 1 }] }
// res.mydata[0].COL1 // note UPPERCASE column namesconst res = await sasjs.request('services/getdata', { in: [{ a: 1 }] }, {}, undefined, undefined, ['log'])
// res === { result: { mydata: [...] }, log: '<sas log string>' }%webout(OPEN)
%webout(OBJ, work.customers) /* -> {"customers":[{...}]} */
%webout(CLOSE)sasjs.getSasRequests()
// SASjsRequest[]: { serviceLink, timestamp, sourceCode, generatedCode, logFile, SASWORK }