Designing of a RESTful API endpoint filter and search - api

I am in the process of developing some custom API endpoints (using loopback.io), on top of its existing CRUD endpoints.
In the past I've used some other Node RESTful API frameworks for prototyping, and really enjoyed the powerful filtering features they provide out of the box.
What I'd like to do is provide a similar set (or subset) of some kind of filtering for a custom endpoint. The endpoint just does a SQL query (with some JOINs) and returns an array of objects.
Is there any kind of standardized approach that I should use to design some filtering? For example, I may want to filter on fields of the base table, or filter on relations. I like the way loopback.io and sequelize allow relatively easy specification of includes to link related objects, as well as their filtering syntax.
How is this type of problem usually approached when a custom implementation is done?

As you probably noticed with CRUD endpoints, LoopBack provides querying out of the box via filter parameter. You can nicely experiment with it in API Explorer. If you want to expose querying for a custom remote method, just add filter as a parameter too.
example-model.js
module.exports = ExampleModel => {
const search = async (filter = {}) => {
return await ExampleModel.find(filter)
}
ExampleModel.remoteMethod('search', {
description: 'Returns a set of ExampleModel based on provided query.',
accepts: [
{arg: 'filter', type: 'object', required: false}
],
http: {path: '/search', verb: 'get'},
returns: {root: true}
})
ExampleModel.search = search
}

Related

How do I filter fields with Directus

Let's say I make an api call like this
const { data } = await client.getItems(`module/${module.id}`, {
fields: [
'questions.module_question_id.question_text',
'questions.module_question_id.slug',
'questions.module_question_id.type',
'questions.module_question_id.answer_options.*',
],
});
I am grabbing the fields, but I also want to filter out a certain question ala its slug, is there a way to do this at the api level? I know filters exist as a global query api, but have not found examples of them being used in conjunction with fields.
Perhaps you are looking for deep? This should allow you to filter on a deeply nested relational field.
https://docs.directus.io/reference/api/query/#deep

NetSuite Rest Integration - Get Sales Records By Date

I need direction on how I would get the sales transactions from NetSuite for a given date range and then access the details (such as customer information) and sale amount. Are there a set of APIs best for this or a RestLet approach/sample I can follow?
Queries are done through the search API. You would want to perform a search, and then export its results. You can create a restlet that accepts some parameters by which to search as input and exports search results as output. The restlet would be implemented in javascript (in NetSuite's SuiteScript).
The following code is quick pseudocode with some SuiteScript 2..0, just to help you get started. This is not something you can copy paste, and this is off the top of my head, you will need to still do some research.
var orderSearch = search.create({
type: 'salesorder',
filters: [
['mainline', 'is', true],
'AND',
['trandate', 'on', '9/25/2020']
],
columns: [
search.createColumn({ name: 'entity' }),
search.createColumn({ name: 'trandate'}),
search.createColumn({ name: 'total' })
]
});
var firstPageOfResults = [];
var pages = orderSearch.runPaged({ pageSize: 100 });
if (pages && pages.count > 0) {
var page = pages.fetch({ index: 0 });
firstPageOfResults = page;
}
// here is where you do something like return the search results
// to the output of the restlet function
return firstPageOfResults;
So, again, this is just quickly drafted rough code to get you started.
You will need to learn more about the search operators, you might want "between" instead of "on", so you can express some other start date and some other end date.
You can of course return other fields of sales orders, you need to go look up the fields in the "record browser" help section.
And you can learn more about the search API in the netsuite docs on writing suitescript.
You will also need to learn about setting up a restlet. Also, if you plan to call your restlet from outside NetSuite, you need to learn about how to setup token based authentication and an "integration record" in netsuite so that you can get basically a password-like thing to pass in the Authorization header to securely call the restlet.
There are more advanced ways of doing all this stuff. You can also learn about and use SuiteQL. I am not going to go into detail here. It is a way to write a query in SQL and run it. You might want to do that instead.

Zapier lazy load input fields choices

I'm building a Zapier app for a platform that have dynamic fields. I have an API that returns the list of fields for one of my resource (for example) :
[
{ name: "First Name", key: "first_name", type: "String" },
{ name: "Civility", key: "civility", type: "Multiple" }
]
I build my action's inputFields based on this API :
create: {
[...],
operation: {
inputFields: [
fetchFields()
],
[...]
},
}
The API returns type that are list of values (i.e : Civility), but to get these values I have to make another API call.
For now, what I have done is in my fetchFields function, each time I encounter a type: "Multiple", I do another API call to get the possible values and set it as choices in my input field. However this is expensive and the page on Zapier takes too much time to display the fields.
I tried to use the z.dehydrate feature provided by Zapier but it doesn't work for input choices.
I can't use a dynamic dropdown here as I can't pass the key of the field possible value I'm looking for. For example, to get back the possible values for Civility, I'll need to pass the civility key to my API.
What are the options in this case?
David here, from the Zapier Platform team.
Thanks for writing in! I think what you're doing is possible, but I'm also not 100% that I understand what you're asking.
You can have multiple API calls in the function (which it sounds like you are). In the end, the function should return an array of Field objects (as descried here).
The key thing you might not be aware of is that subsequent steps have access to a partially-filled bundle.inputData, so you can have a first function that gets field options and allows a user to select something, then a second function that runs and pulls in fields based on that choice.
Otherwise, I think a function that does 2 api calls (one to fetch the field types and one to turn them into Zapier field objects) is the best bet.
If this didn't answer your question, feel free to email partners#zapier.com or join the slack org (linked at the bottom of the readme) and we'll try to solve it there.

RxJS Is there a way to make ajax get request with params without building url

I am having trouble passing params to ajax get request.
Let's suppose i have to pass params {category: 'cat', type: 'type', searchKey: 'key' } to the url /search and I have the code below:
action$.ofType('QUERY')
.debounceTime(500)
.switchMap(action =>
ajax.get('/search', {//pass some parameters},)
.map(result => ({
type: 'FETCH_SUCCESS',
payload: result.response,
})),
As I am new to RxJs, Please suggest me the right way of doing this.
While it is technically permissible to provide a request body (and corresponding Content-Type header like application/x-www-form-urlencoded) for GET requests, nearly all servers assume GET do not contain one. Instead, POST (creation of a document) or PUT (updating a document) is used when a body is neccesary.
However, if what you're asking for is simply regular old query params, that's pretty normal but there is no built-in support in RxJS for converting an Object to a query string--mostly because there is no official spec on how complex objects/arrays should be serialized so every server has notable differences once you do more than simple key -> value.
Instead, you just include them in the URL. I realize you said "without building url" but the lack of a spec means RxJS will likely never add support because it's highly opinionated. You can just manually generate the string yourself or use a third-party utility that has a convention you like.
ajax.get(`/search?query=${query}&something=${something`)
// or using something like https://www.npmjs.com/package/query-string
ajax.get(`/search?${queryString.stringify(params)}`)
If you're interested in further understanding the opinionated nature of query params, consider how you might serialize { foo: [{ bar: 1 }, { bar: 2 }] }. Some might say it should be ?foo[0][bar]=1&foo[1][bar]=2 but I have also seen ?foo[][bar]=1&foo[][bar]=2, ?foo[bar]=1&foo[bar]=2, and other variants. Thing get even more hairy when dealing with duplicates. ?foo=true&foo=false should foo be true or false? (it's an opinion hehe)

How to search multiple api services

I am developing a search engine with angular 2.
Therefore I use APIs from multiple platforms.
It works if I call the search function from every api service manually.
But is it possible to do the same foreach api service?
Every api service has the same function:
search (query: string): Observable<Array<SearchResult>> { ... }
In the UI I want to separate the results by tabs.
Therefore every api service has a title:
public title: string = "the title";
For storing the search results locally I have a class that is extended by every api service. This class has helper functions etc.
Depending on the behaviour you need you can use merge, concat or forkJoin to merge multiple streams into one.
The code would look pretty much the same.
For example using merge in order to merge 2 streams into one.
If you have a list of apis you need to call for the search. Your code would look like this.
let apis: string[] = [];
let observables = apis.map(api => search(api)); // get an array of observables
let merged = observables.reduce((previous, current) => previous.merge(current), new EmptyObservable()); // merge all obserbable in the list into one.
merged.subscribe(res => doSomething(res));
This article might be helpful.