JSON API design - express - api

I want to write a JSON API.
My problem is, that sometimes I want to query for an ID, sometimes for a String.
One option would be to add a querystring, for example:
example.com/user/RandomName
example.com/user/1234556778898?id=true
and use it like:
api.get('user/:input', function(req, res) {
if(req.query.id) {
User.find({ '_id': req.params.input }, cb);
} else {
User.find({ 'name': req.params.input }, cb);
}
};
But this seems like bad practice to me, since it leads to a bunch of conditional expressions.
Are there more elegant ways?

I would suggest handling two endpoints. One for getting ALL the users and one for getting a SPECIFC user by ID.
example.com/users
example.com/users/:id
The second endpoint can be used to find a specific user by id.
The first endpoint can be used to find all users, but filters can be applied to this endpoint.
For example: example.com/users?name=RandomName
By doing this, you can very easily create a query in your Node service based on the parameters that are in the URL.
api.get('/users', function(req, res) {
// generate the query object based on URL parameters
var queryObject = {};
for (var key in req.query) {
queryObject[key] = req.query[key];
}
// find the users with the filter applied.
User.find(queryObject, cb);
};
By constructing your endpoints this way, you are following a RESTful API standard which will make it very easy for others to understand your code and your API. In addition, you are constructing an adaptable API as you can now filter your users by any field by adding the field as a parameter to the URL.
See this response for more information on when to use path parameters vs URL parameters.

Related

How can I make a Jira REST API call to get a specific value?

I am making a Jira REST API call using this example url:
http://example.com/rest/api/2/search/?jql=project=example%20and%20type=test&fields=customfield_14600
Here is an example of the returned JSON
{
"expand":"names",
"startAt":0,
"maxResults":50,
"total":2,
"issues":[
{
"expand":"examples",
"id":"1111",
"self":"https://example.com/rest/api/2/issue/111111",
"key":"EX-1111",
"fields":{
"customfield_14600":{
"self":"https://example.com/rest/api/2/customFieldOption/1111",
"value":"Common",
"id":"11111",
"disabled":false
}
}
},
{
"expand":"examples",
"id":"1111",
"self":"https://example.com/rest/api/2/issue/111111",
"key":"EX-1111",
"fields":{
"customfield_14600":{
"self":"https://example.com/rest/api/2/customFieldOption/1111",
"value":"Uncommon",
"id":"11111",
"disabled":false
}
}
}
]
}
Here is an image of the returned JSON with better formatting
What URL would I use to only return the issues with the value "Common" for the customfield_14600? Basically I am trying to return the number of issues with the "Common" value.
Thank you.
Hello Yousuf and welcome to StackOverflow.
Since you are using a JQL query, you could add another filter to check that the custom field you need has the value you require, as such:
project = example AND type = test AND cf[14600] = "Common"
Or, if you know the name of the custom field and/or prefer it to be readable:
project = example AND type = test AND "Field name" = "Common"
You can check the manual for more operators/keywords.
On another topic, I would recommend using the POST endpoint instead of the GET one for searches that include complex queries. Check the REST API documentation for instructions.

Convert snake_case to camelCase field names in apollo-server-express

I'm new to GraphQL and Apollo Server, though I have scoured the documentation and Google for an answer. I'm using apollo-server-express to fetch data from a 3rd-party REST API. The REST API uses snake_case for its fields. Is there a simple way or Apollo Server canonical way to convert all resolved field names to camelCase?
I'd like to define my types using camel case like:
type SomeType {
id: ID!
createdTime: String
updatedTime: String
}
but the REST API returns object like:
{
"id": "1234"
"created_time": "2018-12-14T17:57:39+00:00",
"updated_time": "2018-12-14T17:57:39+00:00",
}
I'd really like to avoid manually normalizing field names in my resolvers i.e.
Query: {
getObjects: () => new Promise((resolve, reject) => {
apiClient.get('/path/to/resource', (err, response) => {
if (err) {
return reject(err)
}
resolve(normalizeFields(response.entities))
})
})
}
This approach seems error prone, given that I expect the amount of resolvers to be significant. It also feels like normalizing field names shouldn't be a responsibility of the resolver. Is there some feature of Apollo Server that will allow me to wholesale normalize field names or override the default field resolution?
The solution proposed by #Webber is valid.
It is also possible to pass a fieldResolver parameter to the ApolloServer constructor to override the default field resolver provided by the graphql package.
const snakeCase = require('lodash.snakecase')
const snakeCaseFieldResolver = (source, args, contextValue, info) => {
return source[snakeCase(info.fieldName)]
}
const server = new ApolloServer({
fieldResolver: snakeCaseFieldResolver,
resolvers,
typeDefs
})
See the default field resolver in the graphql source code
I'd imagine you can place the normalizeFields function inside a graphql middleware right before it returns the results to the client side. Something like so Graphql Middleware.
A middleware would be a good centralized location to put your logic, so you don't need to add the function each time you have a new resolver.
If you are using Knex.js, I highly recommend using an ORM such as Objection.js (https://vincit.github.io/objection.js/). An ORM has lots of very useful features that make querying easier in Node.js, including a function called knexSnakeCaseMappers, which, when passed to the Knex object, will automatically convert snake_case table and column names to camel case before they ever reach your server. Thus your entire server can be written in camel case, matching your GraphQL schema and your client code. Learn more here.

How to translate the following rally lookback api request to the Ext request equivalent?

So I have this lookback API request:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/xxxxxxx/artifact/snapshot/query.js?find={"ObjectID":92444754348,"__At":"2017-02-23T00:00:00Z"}&fields=true&start=0&pagesize=10&removeUnauthorizedSnapshots=true
How can I make that request using the Ext equivalent. I have tried many ways, including this one:
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
}
});
return snapshot.load();
This example returns an object that has the field "raw", which to my understanding is supposed to have all the artifact's fields along with the values they had at the specified time. But, "raw" only has ObjectID, Project, _ValidFrom, and _ValidTo.
Right now I'm able to solve my issue by using an ajax GET request and parsing the JSON; but I would like to use the Ext solution instead (which seems to be the recommended one).
Thanks.
If you include a fetch in your config when you're creating the store it will autocreate the correct model for you.
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
},
fetch: ['ObjectID'] //add all the fields you want here
});
fields=true is a nice shorthand to get all the data back, but the store/model have no idea how to interpret that...
The store also has config properties for compress, removeUnauthorizedSnapshots and most of the other parameters Lookback Api supports.

Having trouble making a OAuth 1.0a signed request to the Tumblr API using HelloJS

I'm trying to interface with the Tumblr API to pull a list of followers. I'm brand new the whole OAuth thing, so I was trying to model my calls off the demos at https://adodson.com/hello.js/demos/tumblr.html . Unfortunately, the example they give only requires the API key for identification (https://www.tumblr.com/docs/en/api/v2#posts) where as getting the followers needs a signed OAuth request (https://www.tumblr.com/docs/en/api/v2#followers).
The call I'm using is:
function getFollowers(blog){
hello('tumblr').api('blog/'+blog+'/followers/').then(function(r){
console.log("r", r);
//Bellow here not really relevant
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
}
This generates the request url from the proxy:
https://auth-server.herokuapp.com/proxy?path=https%3A%2F%2Fapi.tumblr.com%2Fv2%2Fblog%2Fnmlapp.tumblr.com%2Ffollowers%2F%3Fapi_key%3DREDACTED08u%26callback%3D_hellojs_9kvqxi31&access_token=&then=redirect&method=get&suppress_response_codes=truee
and Tumblr's API returns
_hellojs_9kvqxi31({"meta":{"status":401,"msg":"Not Authorized"},"response":[]});
I can see that the login call has all of the OAuth info in the Query String Parameters field, and the one I'm trying to make does not, but I'm not sure what the right way to include that through helloJS is.
Got it, the function had to be wrapped in the login method. This was shown in the other example, but the way that it called parameters from the api object had me confused.
function doTheThing(network){
hello( network ).login({force:false}).then( function(r){
hello('tumblr').api('followers').then(function(r){
console.log("r", r);
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
});
}
//...
tumblr:{
get: {
//...
//This next part needs to be generated dynamically, but you get the idea
'followers': 'blog/BLOGNAME.tumblr.com/followers',
}
callback(p.path);
}
},
post: {
//...
'followers': function(p, callback) {
p.path = 'followers';
query(p, callback);
}
},

Getting results from api

I am trying to do a domain availability search using an API from free domain API.
After i create an account, it shows:
**Make a REST request using this URL:**
http://freedomainapi.com/?key=11223344&domain=freedomainapi.com
And looking in the documentation page, it has only:
Request http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME
Result:
{
"status": "success",
"domain": "freedomainapi.com",
"available": false
}
I am very new to APIs...
What I need is to show a domain search box, and when the user enters, it should return with result.
It claims to show domain suggestions as well. I hope it will also work.
Using jquery and a jsonp proxy
http://jsfiddle.net/mp8pukbm/1/
$.ajax({
type: 'GET',
url: "https://jsonp.nodejitsu.com/?callback=?",
data: {url: 'http://freedomainapi.com?key=14ejhzc5h9&domain=freedomainapi.com'},
dataType: "jsonp",
success: myfn
});
function myfn(data) {
console.log(data);
}
you have to use the proxy because cross domain json is not permitted
EDIT:
i made an update to show the result in a div (stringified)
http://jsfiddle.net/mp8pukbm/2/
EDIT #2: i created a test key on that site, you have to use your own
EDIT #3: and there's your combo: http://jsfiddle.net/mp8pukbm/4/
Assuming that you will use java script for showing the search box, you can use AJAX feature of java script (or jQuery or Dojo) ... All you need to do is a "GET" request that like you can pasted and you will get the result back on the response object. To try out the API you can use "Postman" application in Chrome. https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en
In the response object of the AJAX call you will get a JSON object which you can parse and display the result.
Normally when we use REST we need to differentiate one REST call from another.
Assuming this url
http://freedomainapi.com/checkAvailability?key=YOUR_API_KEY&domain=DOMAIN_NAME
In Application layer we need to write an interface
#GET
#Path("/checkAvailability")
#Produces({MediaType.APPLICATION_JSON})
public ReturnObject getDomainAvailability(#QueryParam("key") String key,
#QueryParam("domain") String doaminName );
Once interface is done you need to write your implementation class.
This class will intract with business layer and perform search task and based on
result collected will create ReturnObject.
ReturnObject => will contain status, domain and availability
On screen
$.ajax({
type: "GET",
url: 'root/checkAvailability',
success: function(jsonData)
{
// read json and perform operation
}
,
error: function (error)
{
// handle error
}
});
If you are using JAVA as backend then you can use gson to parse the result, which is a json. After parsing you can read the values from result and display accordingly :)
Any API is a way to extend a given software. (Might be a website or an application)
In both ways there is a certain way to communicate with the software. In your example freedomainapi.com allows you to fetch if given domain is avaiable. There is no such thing as a suggestion tho, atleast i cannot find any suggestions at all.
Given output is a message format know as JSON. It can be easily interpreted by many major Languages such as Java, Javascript and PHP.
Given String might be easily interpreted as a Map consisting of a status (String), a domain (string) and avaiable (boolean)
A domain availability search could not be easier, assuming K is your key, D is your search input (Domain):
Download http://freedomainapi.com/checkAvailability?key=K&domain=D as input
Parse JSON from input as json
return json["status"] == "success" and json["avaiable"]
Depending on your language you might need to use methods to access properties of json, but that does not influence the basic usage of this api.
on user enters, it calls click_button function and I am assuming your result displaying div id is "main_container" you can give domain suggestions by passing related DOMAIN_NAME s as arguments to click_button function
function click_button(DOMAIN_NAME){
$.ajax({
url : 'http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME',
type: 'GET',
crossDomain: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
data=JSON.parse(data);
if(data['available']){
$('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Available');
else{
$('#main_container').html($('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Not Available');
}//success
});//ajax
}
hope it helpful !