38

I have a trigger on the Custom Object that calls a class. That class makes an HTTP GET request to a Web Service.

However I get the following error:

Callout from triggers are currently not supported.

How can I get data to an external Web Service using Apex code?

However i can use @future method but it can only be support void and this method cannot return results, i need to be able to send data to external ervice and receive acknowlegements from it.

3
  • 3
    You seem to be aware of the limitations that exist. I suggest you acknowledge whatever needs acknowledging in the @future method and update the triggered object thereafter, you could have statuses on the object like "Reason for Callout", "Pending", "Success"/"Fail" allowing you to build logic around the current state of an object. Dec 20, 2012 at 10:43
  • @MartinPeters : can you please elaborate your thought? Dec 20, 2012 at 11:13
  • 2
    I don't know enough about your use case to give a detailed suggestion on what you should do. But basically, when you're firing the @future from a trigger you did id because some criteria on the object was met. You can at the same time set that the object is "pending" the result of the callout. The callout, once complete can query the object it originated and update it with appropriate success/failure based on what the endpoint answered. Dec 20, 2012 at 11:17

4 Answers 4

63

Callouts cannot be made from triggers as that would hold up the database transaction until the callout completed, which can be up to 120 seconds from a limits perspective. This could obviously cause significant contention with other transactions and impact performance.

The only way that you can execute a callout from a trigger is to schedule it to run asynchronously, for example by executing a method with the @future method as you have found. The key word here is asynchronously - you can't wait for a response from the callout as that would require the transaction to stall until it completes, which is the reason that synchronous callouts aren't allowed.

The way that I've handled this kind of thing in the past is to have a field on the object that captures the response. I pass the id(s) of the record(s) that I am processing to the @future method. Then when the callout completes, the @future method can retrieve the record(s) from the database and update the field(s).

If you are trying to return a response to the user (as part of a visualforce page maybe) you can still utilise this mechanism, you just have to poll the controller to check the object to see if a response has been received - I've used an actionpoller for this and provided the user with a 'checking' spinner to keep them (hopefully) interested.

1
  • 3
    To add to Bob's response, if you are worried about validating the response, you can also write the id of the object to a custom log table object, have a trigger on that custom object call the @future method, and then update the fields you need to on the object tabel and also the log tabel with success/fail, and maybe alert the user if the update failed with any errors you got back from the web service/sf Dec 21, 2012 at 19:09
12

try using @future(callout=true) http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_annotation_future.htm

The following snippet shows how to specify that a method executes a callout:

  @future (callout=true)
  public static void doCalloutFromFuture() {
   //Add code to perform callout 

}

You can specify (callout=false) to prevent a method from making callouts.

When the callout is done you can get a status 200 code or some response body reponse and make sure the handshake was complete !!! If you want to give the user an idea of the handshake I would suggest create a child object and create a field like " Status of transcation" and insert the response body/ response status code in the field.

To get something from the other end spin up a Rest Endpoint with a httppost method and provide the endpoint to the other vendor so that he can send the data back to you once the transaction is complete at his end.

This way you do not have to wait for the transaction to complete and use future for making callouts!!!

Hope this helps!!

5

The use of a Queueable Apex method is likely preferable to a Future method.

If another future or batch method is what triggers your trigger, you'll encounter this error message.

Future method cannot be called from a future or batch method.

Many people describe Queueable Apex as "Futures 2.0", and recommend using them in most cases instead of an @future annotated method. It makes sense that the other answers recommended a Future method back in 2012 because Queueable Apex was released in Winter 2015.

You can learn more about this in this answer.

2

A very late-to-the-party answer, but one that takes a slightly different approach to the existing answers.

As stated in Bob Buzzard's answer, the Salesforce platform doesn't allow synchronous invocation of a callout here and Salesforce has recommended the use of future methods to take the callout into a separate, asynchronous transaction.

However, there are numerous problems with doing that since:

  • Your trigger can be called many times in a single transaction, for a single (bulk) DML operation or if multiple (smaller) DML operations are performed in your transaction.
  • There are quite restrictive limits on how many future methods you can invoke in a single transaction.
  • You cannot invoke future methods at all in an asynchronous context but your trigger may still be called in such a case.
  • There are daily limits on the number of async processes you can run on your org.

The above is not an exhaustive list but captures the most important ones.

An alternative to future methods is to consider the use of Queueables, also mentioned in existing answers. However, these also suffer significant restrictions, especially in asynchronous contexts. They also use asynchronous apex limits.

My suggestion is to actually use record tagging and Platform Events to resolve this issue. Record tagging (marking the record as needing to be processed in some way, such as here performing a callout) is a good way of identifying those records to be processed in a manner that means you don't rely on Platform Events holding this important information - after all, Platform Events don't have guaranteed delivery and you want to make sure that you do your callout even if one or more Platform Events get lost.

Platform Events are a great vehicle for taking you out of the trigger and into a separate transaction because they don't use any asynchronous limits but still allow you to do your callout, initiated from your trigger.

You can also minimize the number of events you publish (there are hourly limits to this) since you only need one to be created and published from your transaction to initiate the processing you need. That processing can be written to fit in as much as is possible (100 callouts, for example) and if there are still more records to process simply publish a follow-up Platform Event. This leads to selective chaining of the processing while it is needed.

More detail on this can be found in this Apex Hours article, which also references an example implementation (this doesn't do callouts, but illustrates how you could).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .