Skip to content

Customers

Sample REST server demonstrating various styles of REST endpoints

Static

resource names are predefined at endpoint registration time, e.g. {/customers}

Dynamic

the resource name (or part of it) is supplied at call-time, e.g. /db/{tbl}/{col}

API-like

same as static but uses verbs instead of names, e.g. /getCustomers

.rest:.com_kx_rest; / Alias namespace for convenience

//
// @desc App initialization.
//
init:{
    .rest.init[enlist[`autoBind]!enlist[1b]]; / Initialize

    initStatic[];               / Sample static endpoints
    initDynamic[];              / Sample dynamic endpoints
    initApi[];                  / Sample API-like endpoints

    .rest.register[`get;"/help"; / Returns information about registered endpoints
        "Retrieves information about registered REST endpoints";
        {.rest.t};
        ()!()];

    .rest.register[`get;"/hc"; / health-check
        "health-check endpoint";
        {"ok"};
        ()!()];

    .rest.register[`get;"/_ping";
        "for testing";
        {"pong"};
        ()!()]; }

//
// Sample parameter subset to support paging.
//
pagingParams:.rest.reg.data[`i;-6h;0b;0;"Offset of first row"],
    .rest.reg.data[`cnt;-6h;0b;10;"Number of rows to return"];

//
// Wraps `#` to limit to data size.
//
take:{[n;d]min[(n;count d)]#d}

//
// Initialization examples.
//

//
// Static endpoints, and various styles of versioning.
//
initStatic:{
    .rest.register[`get;"/customers";
        "Returns all customers";
        {take[x[`arg;`cnt]]select from customers where i>=x[`arg;`i]};
        pagingParams
        ];

    .rest.register[`get;"/customers.2";
        "Returns all customers (version 2)";
        {take[x[`arg;`cnt]]update newCol:1 from select from customers where i>=x[`arg;`i]};
        pagingParams
        ];

    .rest.register[`get;"/v3/customers";
        "Returns all customers (version 3)";
        {take[x[`arg;`cnt]]update newCol:1, newCol2:10 from select from customers where i>=x[`arg;`i]};
        pagingParams
        ];

    .rest.register[`get;"/customers/{id}";
        "Returns one or more customers by their IDs";
        {select from customers where id in x[`arg;`id]};
        .rest.reg.data[`id;6h;1b;0Ni;"One or more customer IDs"]
        ]; }

initDynamic:{
    .rest.register[`get;"/db";
        "Retrieves list of table names";
        {tables[]};
        ()!()
        ];

    .rest.register[`get;"/db/{table}";
        "Retrieves a table";
        .tbl.getData;
        .rest.reg.data[`table;-11h;1b;`;"Table name"],
        pagingParams
        ];

    .rest.register[`get;"/db/{table}/meta";
        "Retrieves metadata of a  table";
        {0!meta x[`arg;`table]};
        .rest.reg.data[`table;-11h;1b;`;"Table name"]
        ];

    .rest.register[`get;"/db/{table}/{col}";
        "Retrieves a column subset of a table";
        .tbl.getData;
        .rest.reg.data[`table;-11h;1b;`;"Table name"],
        .rest.reg.data[`col;11h;1b;0#`;"Result columns"],
        pagingParams
        ]; }


initApi:{
    .rest.register[`get;"/getCustomers";
        "Returns all customers";
        {([] id:til 10; nm:10?`5)};
        ::
        ]; }

//
// Automatic data retrieval
//

//
// @desc Generic data retrieval handler. Look for {tbl}, {id}, and {rel} arguments
//
.tbl.getData:{
    tn:x[`arg;`table];
    i_:$[`i in key x[`arg];x[`arg;`i];0];
    cnt:$[`cnt in key x[`arg];x[`arg;`cnt];0W];

    if[not tn in tables[]; 'table]; / Check whether table exists
    w:$[""~0N!flt:x[`data];();enlist parse flt]; / If request is a POST, then data is expected to be the where clause
    c:$[`col in key x`arg;{x!x}x[`arg;`col];()]; / Columns to retrieve
    take[cnt]select from ?[tn;w;0b;c] where i>=i_ }

init[]

//
// Test data
//
n:100
customers:([] id:1+til n; name:n?`7)
sn:`aapl`goog`msft`nke`ftse
symbols:1!([] sym:`aapl`goog`msft`nke`ftse; src:count[sn]?`8);
trades:([] ts:$[12h;.z.d]+$[`minute;1]*til n;
    sym:n?(0!symbols)[`sym];
    price:n?10.0;
    size:n?1000)