30 Days of Zumo.v2 (Azure Mobile Apps): Day 13 – The HTTP Table Interface

I thought I would take a short step back as my code for the Tasks table and People tables have become somewhat convoluted and will become moreso as I move forward into offline sync. Today, I want to take a step back and discuss the HTTP interface that the SDK exposes to you.

Table Endpoints

There are six distinct endpoints that you can use, and I will discuss those in detail.

Operation Endpoint Description
GET /tables/{tablename} QUERY: Read all or a subset of records from the table
GET /tables/{tablename}/{id} READ: Read a specific ID within the table
POST /tables/{tablename} INSERT: Inserts a new record into the table
POST /tables/{tablename}/{id} UNDELETE: Undeletes a previously deleted record
PATCH /tables/{tablename} UPDATE: Updates the provided record with new data
DELETE /tables/{tablename} DELETE: Deletes (or marks for deletion) the provided record

There are some common error codes:

HTTP Code Reason Meaning
400 Bad Request The request being sent does not conform to expectations
401 Unauthenticated You need to authenticate to use this operation on this table
500 Internal Server Error Something really bad happened – check your code

HTTP Code 400 Bad Request is particularly interesting. In this case, a JSON body is returned with a single property called error that contains the explicit error. If you are using the wrong Client SDK or you are using plain REST calls, it’s a good guess that you forgot to add the ZUMO-API-VERSION header – this is required.

Now, onto the specific routes:

GET /tables/{tablename}

With this request, you can send an OData v3 query that includes a subset of the OData v3 specification. Filters, Selects, Skip/Take (paging) and IncludeTotalCount are supported. What comes back depends on whether $inlinecount is included or not. If you send, for example, /tables/todoitem?$inlinecount=allpages, then you get an object like this:

{
  "results": [],
  "count": 0
}

The results contains the individual records you requested and the count is the total number of records in the table irrespective of how many you requested. This allows you to implement paging properly. If, however, you don’t include the $inlinecount, then you just get an array containing the individual records.

There are two potential results that can be returned:

HTTP Code Reason Meaning
200 OK Success – the body contains the results
404 Not Found The table does not exist

GET /tables/{tablename}/{id}

This gets a single record (by ID) from the table. Assuming the ID exists, the server will return an object containing the result. As with the table equivalent, there are two potential results:

HTTP Code Reason Meaning
200 OK Success – the body contains the results
404 Not Found The ID does not exist in the table

POST /tables/{tablename}

This creates a new record in the table, returning the new record. Note that the additional fields that are required by Azure Mobile Apps (createAt, updatedAt, version and deleted) are updated and returned. Note that you can provide an “id” field – this will be used as the Id for the record. This should be unique across the table, so a GUID is recommended. If you don’t provide one, one will be created. There are several potential responses:

HTTP Code Reason Meaning
201 Created Success – the body contains the new record
400 Bad Request The record provided was invalid
409 Conflict The provided Id already exists

Common reasons for a 400 response include:

  • The “id” field was not a string
  • One of the system fields was provided when it shouldn’t
  • The JSON was incorrect

POST /tables/{tablename}/{id}

This is an UNDELETE route and is only available if you have soft delete enabled. More on that in a future blog post. In the case of soft delete, records are “marked” as deleted by setting the deleted field to true. If you use this route, the deleted field is set to false on the specified Id. Just like reading a specific Id, you can get one of the following:

HTTP Code Reason Meaning
201 Created Success – the body contains the results
404 Not Found The ID does not exist in the table

PATCH /tables/{tablename}/{id}

When you want to update a record, you patch it. Make sure you send a JSON object with the “id” field and any updated fields you want to alter. If you include the “version” field, then you can do conflict resolution (as described in the last article).

HTTP Code Reason Meaning
200 OK Success – the body contains the new record
400 Bad Request The record provided was invalid
404 Not Found The Id does not exist in the table
409 Conflict The version field supplied doesn’t match the server
412 Precondition Failed The version field supplied doesn’t match the server

The major reason for the 400 Bad Request is that the system fields are included. The server manages updatedAt, createdAt and deleted, so don’t include those fields. The Client SDK removes these fields from the update.

There are two methods of doing conflict resolution. If you are using a Client SDK, an If-Match header will be added to add a precondition. The logic is that the version must match. If using If-Match, you will get a 412 Precondition Failed. If you are doing a direct REST call and you don’t include an If-Match header, the server still checks the version. In this case, however, it returns a 409 Conflict to notify you of a conflict that needs resolving.

DELETE /tables/{tablename}/{id}

If you want to delete a record, then call this endpoint. Note that if you have “soft delete” turned on, then the record is marked for deletion – it isn’t actually deleted. More on soft delete later on in the series. Just like the GET response, you can get the following potential results:

HTTP Code Reason Meaning
200 OK Success
404 Not Found The ID does not exist in the table

In the case of soft-delete, you can delete the record multiple times and they will return 200 each time. If, however, soft delete is turned off, the first deletion will result in a 200; future deletes will result in a 404.

Testing

I highly recommend that you obtain a REST test tool for testing endpoints. I use Postman in my daily work. I wish Visual Studio had an in-built REST tool for this. It does have a scaffolding tool, but that’s really not the same thing. For now, use Postman.

Next Steps

Next time, I’m going to talk about what it takes to integrate with existing databases and existing tables.