@themost/client
v2.16.3
Published
MOST Web Framework Codename Blueshift - Client Common
Downloads
462
Readme
@themost/client
@themost-framework core module for javascript clients.
@themost/client
provides a set of methods for creating OData v4 queries by using javascript closures in both client and server.
e.g. get name and price of products with category equals to 'Laptops'
// GET /Products?$select=name,price&$filter=category eq 'Laptops'
const items = await context.model('Products').select(({name, price}) => {
return {
name,
price
}
}).where(({category}) => {
return category === 'Laptops'
}).getItems();
Javascript closure prototypes introduced by @themost/query uses native language and produces equivalent query expressions for both client and server environments:
import { round } from '@themost/query';
context.model('Products').select((x) => {
return {
name: x.name,
releaseYear: x.releaseDate.getFullYear(),
price: round(x.price, 2)
}
}).where((x) => {
return x.category === 'Laptops';
})
...
which produces the following OData expression /Products?$select=name,year(releaseDate) as releaseYear,round(price,2) as price&$filter=category eq 'Laptops'
or an equivalent SQL statement for server-side enviroments SELECT Products.name AS name, YEAR(Products.releaseDate) AS releaseYear, ROUND(Products.price,2) AS price FROM Products WHERE Products.category = 'Laptops'
node.js client
@themost/node is a client module for node.js applications which are going to use @themost-framework as backend api server.
angular client
@themost/angular is a client module for angular 2.x+ applications which are going to use @themost-framework as backend api server.
react client
@themost/react is a client module for react applications which are going to use @themost-framework as backend api server.
jQuery client
@themost/jquery is a client module for JQuery scripts and applications which are going to use @themost-framework as backend api server.
Usage
use ClientDataContext
which is being provided by your environment and initialize an instance of ClientDataQueryable
class.
System Query Options
Read OData v4 specification for more information about system query options:
http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31360955
select(expr: QueryFunc, ...params: any[])
Define $select
system query option by using a javascript closure:
const items = await context.model('Orders')
.asQueryable()
.select((x) => {
return {
id: x.id,
customer: x.customer.description,
orderDate: x.orderDate,
product: x.orderedItem.name
}
})
.where((x) => {
return x.paymentMethod.alternateName === 'DirectDebit';
}).orderByDescending((x) => x.orderDate)
.take(10)
.getItems();
/Orders?$select=id,customer/description as customer,orderDate,orderedItem/name as product&$filter=paymentMethod/alternateName eq 'DirectDebit'&$orderby=orderDate desc&$top=10
where(expr: QueryFunc, ...params: any[])
Define $filter
system query option by using a javascript closure:
const items = await context.model('Orders')
.asQueryable()
.where((x, orderStatus) => {
return x.orderStatus.alternateName === orderStatus;
}, 'OrderPickup').take(10)
.getItems();
/Orders?$filter=orderStatus/alternateName eq 'OrderPickup'&$top=10
Using parameters
A query expression can accept parameters as additional arguments. The following example demonstrates how to use parameters in a query expression e.g.
const items = await context.model('Orders')
.asQueryable()
.where((x, orderStatus) => {
return x.orderStatus.alternateName === orderStatus;
}, 'OrderPickup').take(10)
.getItems();
where the first parameter is a query closure and the second parameter is a string value which is going to be passed to closure as orderStatus
argument.
const items = await context.model('Orders')
.asQueryable()
.where((x, orderStatus, productCategory) => {
return x.orderStatus.alternateName === orderStatus &&
x.orderedItem.category === productCategory;
}, 'OrderPickup', 'Desktops').take(10)
.getItems();
Logical Operators
Use logical operators while querying data:
const items = await context.model('Products')
.asQueryable()
.where(({category}) => {
return category === 'Laptops' ||
category === 'Desktops';
}).getItems();
/People?$filter=(category eq 'Laptops' or category eq 'Desktops')
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where(({category, price}) => {
return category === 'Laptops' && price <=900;
}).getItems();
/People?$filter=(category eq 'Laptops' and price le 900)
Comparison operators
@themost/client
supports the usage of OData comparison operators like eq
, ne
, lt
, le
etc
equals
const items = await context.model('Orders')
.asQueryable()
.where(({id}) => {
return id === 100;
}).getItem();
/Orders?$filter=id eq 100
not equals
const item = await context.model('Orders')
.asQueryable()
.where(({category}) => {
return category !== 'Desktops';
}).getItems();
/Orders?$filter=category ne 'Desktops'
greater than
const items = await context.model('Orders')
.asQueryable()
.where(({category, price}) => {
return category === 'Desktops' && price > 1000;
}).getItems();
/Orders?$filter=(category eq 'Desktops' and price gt 1000)
greater than or equal
const item = await context.model('Orders')
.asQueryable()
.where(({category, price}) => {
return category === 'Desktops' && price >= 1000;
}).getItems();
/Orders?$filter=(category eq 'Desktops' and price ge 1000)
lower than
const items = await context.model('Orders')
.asQueryable()
.where(({category, price}) => {
return category === 'Desktops' && price < 1200;
}).getItems();
/Orders?$filter=(category eq 'Desktops' and price lt 1200)
lower than or equal
const items = await context.model('Orders')
.asQueryable()
.where(({category, price}) => {
return category === 'Desktops' && price <= 1200;
}).getItems();
/Orders?$filter=(category eq 'Desktops' and price le 1200)
Aggregate functions
@themost/client
supports the usage of aggregate functions like count
, min
, max
for getting
aggregated results
count
import { count } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.select((x) => {
return {
category: x.category,
total: count(x.id)
};
}).groupBy((x) => x.category)
.getItems();
/Products?$select=category,count(id) as total&$groupby=category
min
import { min } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.select((x) => {
return {
category: x.category,
minimumPrice: min(x.price)
};
}).groupBy((x) => x.category)
.getItems();
/Products?$select=category,min(price) as minimumPrice&$groupby=category
min
import { max } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.select((x) => {
return {
category: x.category,
maxPrice: max(x.price)
};
}).groupBy((x) => x.category)
.getItems();
/Products?$select=category,max(price) as maxPrice&$groupby=category
String functions
@themost/client
supports the usage of string functions while querying data
indexof
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.name.indexOf('Intel') >= 0;
})
.getItems();
/Products?$filter=indexof(name,'Intel') ge 0
startsWith
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.name.startsWith('Intel') === true;
})
.getItems();
/Products?$filter=startswith(name,'Intel') eq true
endsWith
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.name.endsWith('Edition') === true;
})
.getItems();
/Products?$filter=endswith(name,'Edition') eq true
toLowerCase
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.category.toLowerCase() === 'laptops';
})
.getItems();
/Products?$filter=tolower(category) eq 'laptops'
toUpperCase
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.category.toUpperCase() === 'LAPTOPS';
})
.getItems();
/Products?$filter=toupper(category) eq 'LAPTOPS'
substring
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return x.category.substring(0,3) === 'Lapt';
})
.getItems();
/Products?$filter=substring(category,0,3) eq 'Lapt'
Date functions
@themost/client
supports also the usage of date functions while querying data
getDate
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getDate() === 0;
})
.getItems();
/Orders?$filter=day(orderDate) eq 19
getMonth
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getMonth() === 0;
})
.getItems();
/Orders?$filter=(month(orderDate) sub 1) eq 0
getFullYear
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getFullYear() === 2019;
})
.getItems();
/Orders?$filter=(month(orderDate) sub 1) eq 0
getHours
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getHours() === 14;
})
.getItems();
/Orders?$filter=hour(orderDate) eq 14
getMinutes
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getMinutes() === 30;
})
.getItems();
/Orders?$filter=minute(orderDate) eq 30
getSeconds
const items = await context.model('Orders')
.asQueryable()
.where((x) => {
return x.orderDate.getSeconds() === 30;
})
.getItems();
/Orders?$filter=second(orderDate) eq 30
Math Functions
floor
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return Math.floor(x.price) <= 177;
})
.getItems();
/Products?$filter=floor(price) le 177
ceil
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return Math.ceil(x.price) >= 177;
})
.getItems();
/Products?$filter=floor(price) ge 177
round
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return round(x.price, 2) >= 177;
})
.getItems();
/Products?$filter=round(price, 2) ge 177
Arithmetic operators
add
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return round(x.price, 2) + 100 >= 277;
})
.getItems();
/Products?$filter=(round(price,2) add 100) ge 277
subtract
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return round(x.price, 2) - 100 <= 277;
})
.getItems();
/Products?$filter=(round(price,2) sub 100) le 277
multiply
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return round(x.price, 2) * 0.75 < 800;
})
.getItems();
/Products?$filter=(round(price,2) mul 0.75) lt 800
divide
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.where((x) => {
return round(x.price, 2) / 1.25 < 800;
})
.getItems();
/Products?$filter=(round(price,2) div 1.25) lt 800
case
import { round } from '@themost/query';
const items = await context.model('Products')
.asQueryable()
.select(({name, price}) => {
return {
name: name,
value: price < 800 ? 'Normal' : 'Expensive'
}
})
.where(({category}) => {
return category === 'Laptops';
})
.getItems();
/Products?$select=name,case(price lt 800:'Normal',true:'Expensive') as value&$filter=category eq 'Laptops'
take(n: number)
Set $top
system query option for defining the number of records to be taken
const items = await context.model('Orders')
.asQueryable()
.where((x, orderStatus) => {
return x.orderStatus.alternateName === orderStatus;
}, 'OrderPickup').take(10)
.getItems();
/Orders?$filter=orderStatus/alternateName eq 'OrderPickup'&$top=10
skip(n: number)
Set $skip
system query option for defining the number of records to be skipped
const items = await context.model('Orders')
.asQueryable()
.where((x, orderStatus) => {
return x.orderStatus.alternateName === orderStatus;
}, 'OrderPickup').take(25)
.skip(25)
.getItems();
/Orders?$filter=orderStatus/alternateName eq 'OrderPickup'&$top=25&$skip=25
orderBy(expr: QueryFunc, ...params: any[])
Define $orderby
system query option for sorting records
const items = await context.model('People')
.asQueryable()
.orderBy(({familyName}) => familyName)
.getItems();
/People?$orderby=familyName
thenBy(expr: QueryFunc, ...params: any[])
const items = await context.model('People')
.asQueryable()
.orderBy(({familyName}) => familyName)
.thenBy(({givenName}) => givenName)
.getItems();
/People?$orderby=familyName,givenName
orderByDescending(expr: QueryFunc, ...params: any[])
const items = await context.model('People')
.asQueryable()
.orderByDescending(({familyName}) => familyName)
.getItems();
/People?$orderby=familyName desc
thenByDescending(expr: QueryFunc, ...params: any[])
const items = await context.model('People')
.asQueryable()
.orderByDescending(({familyName}) => familyName)
.thenByDescending(({givenName}) => givenName)
.getItems();
/People?$orderby=familyName desc,givenName desc
groupBy(...arg: [QueryFunc], params?: any)
Define $groupby
system query option to group records by using javascript closures:
const results = await context.model('Orders')
.asQueryable()
.select(({id, orderStatus}) => {
return {
total: count(id),
orderStatus
}
}).groupBy(({orderStatus}) => orderStatus)
.getItems();
/Orders?$select=count(id) as total,orderStatus&$groupby=orderStatus
expand(...args: (OpenDataQuery | QueryFunc)[])
Define $expand
system query option for getting nested objects
Read more about $expand
at http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31361039
const items= await context.model('Orders')
.asQueryable()
.select(({id, orderStatus, orderDate}) => {
return {
id,
orderStatus,
orderDate
}
}).expand(
(x) => x.customer,
(x) => x.orderedItem
).getItems();
/Orders?$select=id,orderStatus,orderDate&$expand=customer,orderedItem
or use query expressions for applying nested query options:
import { any } from '@themost/query';
const items= await context.model('People')
.asQueryable()
.expand(
any((x) => x.address)
.select(({id, streetAddress, addressLocalilty}) => ({
id, streetAddress, addressLocalilty
}))
).getItems();
/People?$expand=address($select=id,streetAddress,addressLocalilty;$expand=addressCountry)
Using CLI
@themost/client
provides a command line interface for generating client-side type declarations from an OData metadata service.
Connect to an OData service and generate client-side type declarations:
$ npx @themost/client http://localhost:3000/api/
or extract metadata from an OData metadata document:
$ npx @themost/client ./metadata.xml
Use --out-file
option for specifying the output file:
$ npx @themost/client http://localhost:3000/api/ --out-file ./client.d.ts