It would make more sense to me to organize the API around resources and use the verbs (GET, PUT, POST, DELETE) that come with the http protocol, in short: Make it more RESTful, that is surely considered good practice.
This is a good succinct talk on API design that also analyzes popular APIs such as facebook's or twitter's.
I am lost on the difference between your /getX
and /getXForId
so I can't really give you an example of how I would change your API.
Edit
I tried to model the API in Sinatra. It is pretty self explanatory and straight forward. (I saw you are using axis but it's been a while since I did serious Java). If you are serious about RESTful then you probably want to drop the approach with the four ids. However the approach works:
require "sinatra"
require "sinatra/reloader"
def all_ids_given?(ids_given, ids_needed)
ids_needed.reduce(true){|memo, id| memo &= ids_given.include? id}
end
#Your old /getXForId
get '/X/:combined_id' do |id|
"Getting X with combined id #{id}\r\n"
end
#Your old /createX
post '/X' do
post_params = request.body.read
"Creating a new X with #{post_params} \r\n"
end
#Your old /updateXForId
put '/X/:combined_id' do |id|
put_params = request.body.read
"Updating X with combined id #{id}: New values are #{put_params}\r\n"
end
#Your old /removeXForId
delete '/X/:combined_id' do |id|
"Deleting X with combined id #{id}\r\n"
end
#Your old /getX
get '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
"Getting X with the ids #{params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Your old /updateX
put '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
put_params = request.body.read
"Updating X with the ids #{params}: New values are #{put_params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Your old /removeX
delete '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
"Deleting X with combined id #{params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Extensibility
get '/X/:combined_id/Y' do |id|
"Getting all Ys for the X with combined id #{id}\r\n"
end
Testing the API with curl:
base_url="http://localhost:4567/"
echo "Getting a X with a combined id"
echo -n "-> "; curl ${base_url}X/id1234
echo "Creating a new X with a list of properties"
echo -n "-> "; curl --data "property_1=test" ${base_url}X
echo "Updating an existing X with a combined id"
echo -n "-> "; curl -X PUT --data "property_1=test&property_2=another_test" ${base_url}X/id1234
echo "Deleting an existing X with a combined id"
echo -n "-> "; curl -X DELETE ${base_url}X/id1234
echo "Getting a X with 4 ids"
echo -n "-> "; curl ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "Getting a X with 4 ids, but supplying only 3"
echo -n "-> "; curl ${base_url}"X?id1=1&id3=3&id4=4"
echo "Updating a X with 4 ids"
echo -n "-> "; curl -X PUT --data "property_1=test&property_2=another_test" ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "Updating a X with 4 ids"
echo -n "-> "; curl -X DELETE ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "\r\nTesting the extensibility"
echo "Getting all Ys for a single X (Unrelated example: /post/2/comments would give all comments to a single post with the id 2)"
echo -n "-> "; curl ${base_url}X/id1234/Y
echo "This won't work for the 4 ids"
echo -n "-> "; curl ${base_url}"X?id1=1&id2=2&id3=3&id4=4/Y"
The script returns the following:
Getting a X with a combined id
-> Getting X with combined id id1234
Creating a new X with a list of properties
-> Creating a new X with property_1=test
Updating an existing X with a combined id
-> Updating X with combined id id1234: New values are property_1=test&property_2=another_test
Deleting an existing X with a combined id
-> Deleting X with combined id id1234
Getting a X with 4 ids
-> Getting X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4"}
Getting a X with 4 ids, but supplying only 3
-> Error: Need arguments for id1, id2, id3 and id4
Updating a X with 4 ids
-> Updating X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4", "property_1"=>"test", "property_2"=>"another_test"}: New values are property_1=test&property_2=another_test
Updating a X with 4 ids
-> Deleting X with combined id {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4"}
Testing the extensibility
Getting all Ys for a single X (Unrelated example: /post/2/comments would give all comments to a single post with the id 2)
-> Getting all Ys for the X with combined id id1234
This won't work for the 4 ids
-> Getting X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4/Y"}
What you can see is that nesting resources and the four parameter approach does not work well together. Maybe one could model it differently and do something like /X/:id1/:id2/:id3/:id4
.
It depends on how many fields you're talking about, and how they are used. Concrete is preferable for highly structured queries with only a few fields, but if the querying tends to be very free form, then the concrete approach quickly becomes unwieldy with more than three or four fields.
On the other hand, it's very difficult to keep a generic API pure. If you do a simple name search in a lot of places, eventually someone is going to tire of repeating the same five lines of code, and they're going to wrap it in a function. Such an API invariably evolves into a hybrid of a generic query together with concrete wrappers for the most commonly used queries. And I don't see anything wrong with that. It gives you the best of both worlds.
Best Answer
From my experience: If you have to write for embedded systems the core functionality is mostly C or something similar and therefor multi language support is likely to be realized with wrappers because Python, Java, Ruby and likewise are not 100% suitable for the task.
Did I get you right? You think about writing the whole functionality multiple times in different languages? Well, if so that means an order of magnitude more effort to test, because you cannot verify the "core" and once it's done compare the wrapper results with the expected ones from core test but have to write unit tests for each and every function multiple times.
So to me it feels like wrapping is the better choice.
You could have a look into git repositories of famous open source projects, to see what they did to have it ultra accessible.
By the way: Putting wrappers onto wrappers sounds like there might be some good use for the adapter pattern though.