How we’re building APIs at Ex Libris
As a part of the launch of the Developer Network, we at Ex Libris have done some work on standardizing our external APIs. The goal of standardization is to allow those developing applications against Ex Libris products to learn one style and leverage that knowledge across our products. These standards are being implemented for all new APIs for both Alma and Primo. Adoption for other products depends on the various development road maps and existing styles for those products.
While much of the standardization can be derived from the API documentation and the Getting Started Guide, this blog entry articulates our thinking and style choices.
Happy programming!
Quick Links
REST URLs
- /bibs
- /bibs/ABFE1234
- /courses
- /courses/5586/readinglists
URL Format
HTTP Methods
CRUD Action | HTTP Verb |
---|---|
C / Create | POST |
R / Read | GET |
U / Update | PUT |
D / Delete | DELETE |
This translates to the following style for CRUD actions on Ex Libris entity objects:
Resource | POST Create | GET Read | PUT Update | DELETE Delete |
---|---|---|---|---|
/courses | Create a new course | List of courses | Not supported | Not supported |
/courses/1234 | Not supported | Get course details | Update course | Delete course |
Non-CRUD Services
Default values
Note that for POST we don’t add the entity ID to the URL. It is sent in the XML, or generated automatically by the system. When creating a new entity with POST, missing elements will be given default values (and if they are mandatory, an error message will be returned).
When updating with PUT, the same behavior is followed: missing elements will be given default values. We “swap-all”. Calling applications are expected to run a GET before PUT, and send the entire entity with the relevant fields modified.
API Descriptions
WADLs
OpenAPI
OpenAPI descriptions for available for many APIs. For more information, see OpenAPI Support.
HTTP Responses
We support the following HTTP response codes for all HTTP methods:
HTTP Code | Description |
---|---|
200 | Request was successful |
204 | Request was successful but no content returned e.g. for DELETE requests |
401 | Unauthorized |
400 | Logical error- check the error message, fix your data, and try again |
500 | Server error- try your request again, and if unsuccessful, submit a support case |
Error format
In the case of a 400 or 500 error, the following error object will be returned:
{ "errorsExist":true, "errorList": { "error": [ { "errorCode":"401861", "errorMessage":"User with identifier fdsa was not found.", "trackingId":"E02-0811142608-O9VFN-AWAE57127058" } ] } } }
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <web_service_result xmlns="http://com/exlibris/urm/general/xmlbeans"> <errorsExist>true</errorsExist> <errorList> <error> <errorCode>90101</errorCode> <errorMessage>Table does not exist.</errorMessage> </error> </errorList> </web_service_result>
404 Not Found
There is a long standing argument on the web regarding when to return a 404 Not Found error. We have decided to return 404 only when the service does not exist. If the client requested the correct service but specified an incorrect identifier, we will return a 400 Bad Request with an appropriate code and description in the error object.
For example, a request to https://api-na.hosted.exlibrisgroup.com/almaws/v1/userss/me@library.org (with two s’s in the URL) will return 404. A request to https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/baduser@library.org will return 400 with error code 401861 returned in the error object (as specified here).
Client applications can catch 400 errors and examine the error object to determine the appropriate response to the user.
Content Types
Querystring
https://...?format=json
https://...?format=xml
HTTP Header
GET:
Accept: application/json
Accept: application/xml
POST/PUT:
Content-Type: application/json
Content-Type: application/xml
Other Notes
Dates & Times
We support XML date/time format for all date/time fields. (For those into such things, that’s ISO-8601.) For GET requests, we will always return the time in UTC with a Z at the end. If you don’t provide an offset in your value, we’ll assume the time of the data center for your instance.
Since this is a popular topic, we’ve written a separate blog entry which includes some examples: Time-zones and Dates in Alma APIs.
Clearing/deleting fields
Pagination
- limit- the number of rows to return
- offset- the row number to start with
The response to the GET list API includes the total number of entities in the list in total_record_count attribute. For example: <users total_record_count=”6448″>. This information can be used by the calling application in order to retrieve the list in chunks, untill there is no more entities in the list.
It is common that an application displays the first chunk of entities, and retrieves the next chunk only upon a request from the end user.Examples:
- GET /almaws/v1/users – Retrieve the first 10 users in the list (based on limit/offset defaults)
- GET /almaws/v1/users?offset=10 – Retrieve the 10 users, starting with the 11th user (this is the second chunk of users, in case each chunk is 10 users)
- GET /almaws/v1/users?limit=50 – Retrieve the first 50 users in the list
Codes & Descriptions
<user_group desc="Staff">08</user_group>
Or as a structure in JSON:
"user_group": { "value": "08", "desc": "Staff", }
Brief Search
Often a higher level URL will allow searching or listing the results of the lower level entities. For example, /courses returns a list of courses, each accessible by its own URL (eg. /courses/1234). The /courses URL takes search parameters to limit the results returned.
Use the ‘q’ querystring parameter to limit your search. Expected parameter values are the field name (check each APIs documentation for a list of supported search fields) and the field value, separated by a tilde:
/almaws/v1/courses?apikey={apikey}&q=name~History
To search for a phrase, separate words with an underscore (or in some cases with “+” encoded as %2b ):
/almaws/v1/courses?apikey={apikey}&q=name~Introduction_to_Biology
The AND operator is supported for some of the APIs:
/almaws/v1/courses?apikey={apikey}&q=code~MED%20AND%20year~2015
and in some cases “*” is supported:
/almaws/v1/users?q=all~Joh*%2bSm*h
Brief Objects in Search Results
Order of fields in JSON objects
It should not be assumed that our REST APIs return attributes in any particular order. According to the JSON specification, JSON objects are unordered lists of properties and values. This means that developers should access properties by name and not count on a particular position within the JSON document. For example, it is possible that a user JSON object might be returned as either:
{ "primary_id": 1234, "first_name": "Harold" "last_name": "Jones", }
Or:
{ "first_name": "Harold", "last_name": "Jones", "primary_id": 1234 }
Applications should access the property as user.first_name, such that the order of fields does not matter.