Start a new topic
Answered

Unable to use PHP with CURL on UPSERT operation

Hello

I'm building a set of CRUID functions based on PHP with CURL (https://secure.php.net/manual/de/function.curl-setopt.php) based on Kinvey's REST API.

In the end I would like to post it here for everyone but first they all need to work properly.


So far most functions work as they should. I'm struggling on the UPSERT (update/insert) functionality and can't find a proper way to make it work.

Pretty obvious I'm doing something wrong. My document source for this is from: https://devcenter.kinvey.com/rest/guides/datastore#modifiers


I have tried to find a proper way to make UPSERT working but no option seems to be reliable.


The tests I've done are all processed with these base CURL settings:

curl_setopt ($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);

curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json","Authorization: Kinvey ".<currentauthtoken>, "X-Kinvey-API-Version: 3" ));

curl_setopt ($ch, CURLOPT_POSTFIELDS, json_encode(<myInsUpdJson>) );

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);


Based on the documentation I should use 'PUT' for UPSERT operation. Just because I coudln't make it work I've tried all available CURL options:

- curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // based on documentation

- curl_setopt($ch, CURLOPT_PUT, true); // based on documentation

- curl_setopt($ch, CURLOPT_POST, true); 

- curl_setopt($ch, CURLOPT_HTTPGET, true);

Furthermore I have tested the INSERT and UPDATE behaviour based on the 4 mentioned settings:

- Force INSERT with no ID set: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/")

- Force UPDATE on existing record with ID = 12345: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/12345")

Here are the results depending on using the 4 mentioned operator settings each with INSERT and UPDATE:


Using: curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'PUT') :

Sending no ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/"

-> Expectation: Create a new record (since ID is not set)

-> Behaviour: Returned error message: {"error":"The request was not understood.","request":"PUT /appdata/kid_xxxxxxxxx/mycollection/"} 


Sending ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/12345")

-> Expectation on new ID: Create new record

-> Behaviour: New record created

(return: complete inserted record)

-> Expectation on updating existing ID: Update existing record

-> Behaviour: Existing record updated

(return: complete updated record)


Using: curl_setopt($ch, CURLOPT_PUT, true) :

Sending no ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/"

-> Expectation: Create a new record (since ID was not set)

-> Behaviour: Returned error message: {"error":"The request was not understood.","request":"PUT /appdata/kid_xxxxxxxxx/mycollection/"} 

Sending ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/12345")

-> Expectation: Create a new record (since ID is not set)

-> Behaviour: A record was created but only with _id, _act, _kmd

(return: JSON string with only _id, _act and _kmd) - all other columns are empty


-> Expectation on updating existing ID: Update existing record

-> Behaviour: Update existing record (all columns expect _id, act_, _kmd are emptied)

(return: JSON string with only _id, _act and _kmd) 

Using: curl_setopt($ch, CURLOPT_POST, true) :

Sending no ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/"

-> Expectation: Create a new record (since ID is not set)

-> Behaviour: Record was created

(return: JSON string with Inserted record)


Sending ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/12345"

-> Expectation on updating existing ID: Update existing record

-> Behaviour: Returned error message: {"error":"The request was not understood.","request":"POST /appdata/kid_SkYzM4SHM/games/12345"}


Using: curl_setopt($ch, CURLOPT_HTTPGET, true) :

Sending no ID: curl_setopt ($ch, CURLOPT_URL, "https://baas.kinvey.com/appdata/kid_xxxxxxxxx/mycollection/")

-> Expectation: Create a new record (since ID is not set)

-> Behaviour: Record was created

(return: JSON string with Inserted record)


-> Expectation on updating existing ID: Update existing record

-> Behaviour: Returned error message: {"error":"The request was not understood.","request":"POST /appdata/kid_SkYzM4SHM/games/12345"}


The most obvious CURL option for UPSERTs that should be used is probably " $ch, CURLOPT_CUSTOMREQUEST, 'PUT')" but it expects an ID to be sent otherwise error. In my case I don't want to send a unique _id and let Kinvey create one.


Is there something else I have to consider to make it work properly?


Regards


Best Answer

Tayger,


I implemented your scenario and my observations are given below:


When making a PUT request, you will always have to provide an "_id" value. 

  • If the "_id" is correct, it will update the record.
  • If the "_id" is not recognized (i.e. "_id" is wrong), it will create new record. If you provide your own "_id" like "12345" then a record with that "_id" will be created.
  • If you don't provide an "_id" value, you will always get an error because Kinvey always expects some "_id" value in "PUT /appdata/kid_xxxxxxxxx/mycollection/<_id>" . This is the required URL for PUT/ upsert.



You mentioned "In my case I don't want to send an unique _id and let Kinvey create one". In case of making a POST request, if an "_id" is specified in the request body, the entity is created with the supplied "_id". If an "_id" is not specified, one is automatically generated. Following is the detailed explanation:



Please let me know if anything is unclear.



Thanks,

Pranav

Kinvey





Tayger,


Thanks for your kind words. You conclusion in the earlier comment is true. 

Let me know if you have any other questions.


Thanks,

Pranav

Kinvey

Hmmm, I was a bit too quick. I just figured out that Kinvey doesn't like an existing _id in the sent JSON structure while using POST:


The Kinvey server encountered an unexpected error. Please retry your request","debug":"An entity with that _id already exists in this collection"}

 

The insert works fine if there is no '_id' with the sent _id value. The second time (after initial insert) I got the error message above.


Conclusion so far:

- Use POST for new records

- Use PUT for existing records (to be updated)

Hi Pranav


Excellent and precise explanation that makes everything clear, thank you! I was not aware that sending an _id is mandatory using PUT. Working with POST offers both option and works great! It's just a pleasure working with Kinvey, removing all the "pain" I have with the big cloud providers!

Answer

Tayger,


I implemented your scenario and my observations are given below:


When making a PUT request, you will always have to provide an "_id" value. 

  • If the "_id" is correct, it will update the record.
  • If the "_id" is not recognized (i.e. "_id" is wrong), it will create new record. If you provide your own "_id" like "12345" then a record with that "_id" will be created.
  • If you don't provide an "_id" value, you will always get an error because Kinvey always expects some "_id" value in "PUT /appdata/kid_xxxxxxxxx/mycollection/<_id>" . This is the required URL for PUT/ upsert.



You mentioned "In my case I don't want to send an unique _id and let Kinvey create one". In case of making a POST request, if an "_id" is specified in the request body, the entity is created with the supplied "_id". If an "_id" is not specified, one is automatically generated. Following is the detailed explanation:



Please let me know if anything is unclear.



Thanks,

Pranav

Kinvey




Login or Signup to post a comment