Answers.Body: <p>This is definitely a sub-optimal workaround, but it might work:</p>
  <p>Have your criteria in the API config fetch something you know is there, and there is only one of - your homepage, or a global set (maybe a global set that's called <code>errorMessages</code>?)</p>
  <p>Then do the normal</p>
  <pre><code>$criteria = craft()-&gt;elements-&gt;getCriteria(ElementType::Entry);
  $criteria-&gt;section = 'sectionHandle';
  // maybe some more criteria in here
  $entries = $criteria-&gt;find();
  </code></pre>
  <p>and return your desired message if <code>$entries</code> is <code>null</code>.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-13T19:16:17.323Z
Answers.Text: Thanks megatrond... This is somewhat helpful, though doesn't exactly resolve the problem. I edited my question to elaborate the use-case. I think your solution could potentially be used to achieve the desired effect... but as you said, it's a "sub-optimal workaround". At that rate, I'd wonder why I wasn't just using a JSON-formatted template in the first place?
Answers.UserDisplayName: Lindsey D
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T08:23:26.493Z
Answers.Text: I agree with you, and so I [forked](https://github.com/megatrond/ElementAPI) the ElementApi and added a config param called `handleNotFound`, which whould be a function, and return whatever you'd like if an element is not found. It is only used when `first` is enabled. If no handler is provided, a 404 will be thrown.
Answers.UserDisplayName: megatrond
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T08:48:19.390Z
Answers.Text: Very interesting... thanks for the fork link!
Answers.UserDisplayName: Lindsey D
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2016-01-13T08:26:37.393Z
Answers.LastActivityDate: 2022-11-15T02:42:42.953Z
Answers.LastEditDate: 2022-11-15T02:42:42.953Z
Answers.UserDisplayName: megatrond
Answers.Body: <p>Currently, Element API is hard coded to throw a 404 exception for single element endpoints, if the element criteria returns no results. There's no way to prevent this exception, add "fallback" criteria or to display an error message.</p>
  
  <p>Note that the 404 exception will only be thrown if the <code>first</code> parameter is set to <code>true</code>, though. If it isn't, Element API will just return an empty dataset.</p>
  
  <p>Obviously, omitting the <code>first</code> parameter is not a real solution to your issue. You still have no way to set a custom error message, and it would also mean that all single element endpoints would return arrays, as well as paging metadata etc, instead of a "flat" JSON object.</p>
  
  <p>On that note, my opinion is that the canonical answer to this question should probably be to <strong>fork the Element API plugin</strong> and/or add a <a href="http://feedback.craftcms.com/forums/285221-feature-requests" rel="nofollow">feature request</a>.</p>
  
  <p>However, if you don't want to go down that road, here's a possible workaround. It's not pretty :)</p>
  
  <p>Basically, what you'll need to do is to catch the <code>HttpException</code>, in order to return a custom payload.</p>
  
  <p>To do this, you'll need to write a custom plugin. Important: <strong>make sure that the string returned by your plugin's <code>getName()</code> method comes before "Element API", alphabetically</strong>. The reason for this is that you need to make sure that Craft loads your plugin before the Element API plugin (Craft loads plugins alphabetically, by name). I'll pretend you opt for the name "Awesome Plugin", which would work nicely.</p>
  
  <p>Your plugin will need a controller, so create one called "AwesomeController" or the like. This controller should have a method called <code>actionGetElements</code>, taking the same arguments as the ElementApiController's <code>actionGetElements</code> method.</p>
  
  <p>The only purpose of your controller is to "wrap" the ElementApiController, in order to catch the 404 HttpException fired by Element API, and echo a custom error message. Here's how that could look:</p>
  
  <pre><code>&lt;?php
  namespace Craft;
  
  class AwesomeController extends BaseController
  {
  
      protected $allowAnonymous = true;
  
      public function actionGetElements($configFactory = null, array $config = null)
      {
          try {
  
              // Fire up the Element API controller
              craft()-&gt;runController('elementApi/getElements');
  
          } catch (HttpException $e) {
  
              switch ($e-&gt;statusCode) {
  
                  case 404 :
  
                      // It's a 404. Output a custom error message
                      JsonHelper::sendJsonHeaders();
                      echo JsonHelper::encode(array(
                          'error' =&gt; 'No element found',
                      ));
                      // End the request
                      craft()-&gt;end();
  
              }
          }
      }
  
  }
  </code></pre>
  
  <p>Obviously, the controller will do nothing by itself – the final thing you need to do is to "hijack" the Element API routes; making them point to your controller instead of the ElementApiController. To achieve this, you'll need to create a method called <code>registerSiteRoutes()</code> in your plugin's primary class. Inside this method, you'll basically want to pull all the routes from the Element API plugin, changing their <code>action</code> value to <code>awesomePlugin/getElements</code>.</p>
  
  <p>This is why it's important to make sure that your plugin is loaded by Craft before the Element API plugin: when an existing route is added to Craft, it'll be ignored. This means that for every endpoint in <code>/config/elementapi.php</code>, you can trick Craft into loading your custom controller instead of ElementApiController!:</p>
  
  <p>Here's how that <code>registerSiteRoutes()</code> method could look:</p>
  
  <pre><code>public function registerSiteRoutes()
  {
  
      $routes = [];
  
      // Get the Element API plugin
      $elementApiPlugin = craft()-&gt;plugins-&gt;getPlugin('elementApi');
  
      if (!$elementApiPlugin) {
          return $routes;
      }
  
      // Get the Element API routes
      $elementApiRoutes = $elementApiPlugin-&gt;registerSiteRoutes();
  
      // Set action property
      foreach ($elementApiRoutes as $endpoint =&gt; $config) {
          $config['action'] = 'awesomePlugin/getElements';
          $routes[$endpoint] = $config;
      }
  
      return $routes;
  
  }
  </code></pre>
  
  <p>All in all – not exactly an elegant solution; but if the 404's are a dealbreaker to you, its at least a working workaround that doesn't require you to tamper with the Element API source, and the performance footprint should be minimal, too (no redundant database queries). </p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T17:51:40.967Z
Answers.Text: Great answer, thank you! It still seems like a lot of workaround code to solve a (theoretically) simple problem. I'm going to submit a formal feature request... I feel strongly that the plugin should natively handle the situation more gracefully.
Answers.UserDisplayName: Lindsey D
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T18:05:40.503Z
Answers.Text: Per your suggestion, I've added a formal feature request... Please vote if you agree! [Element API plugin: Better handling of NULL criteria results](http://feedback.craftcms.com/forums/285221-feature-requests/suggestions/11448138-element-api-plugin-better-handling-of-null-criter)
Answers.UserDisplayName: Lindsey D
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T18:13:36.333Z
Answers.Text: I completely agree and voted for your request :)
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T18:41:27.083Z
Answers.Text: +1 for the ridiculous workaround, though. :)
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T19:11:11.257Z
Answers.Text: @BradBell Where there's a will, there's a ridiculous way :)
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-01-14T17:39:34.117Z
Answers.LastActivityDate: 2016-01-14T19:12:20.657Z
Answers.LastEditDate: 2016-01-14T19:12:20.657Z
Answers.UserDisplayName: Mats Mikkel Rummelhoff
Body: <p>Per this code example, it's clear that the API is expecting to return results based on an <strong>existing element</strong>...</p>
  
  <pre><code>'transformer' =&gt; function(EntryModel $entry) {
      ...
  }
  </code></pre>
  
  <p>And when the criteria finds a matching entry, everything works great!</p>
  
  <p>But when there is <strong>no matching criteria results</strong>, the API seems to simply return a generic 404 error.</p>
  
  <p><strong>How can I make the API return a message (in JSON format) instead of a 404?</strong></p>
  
  <hr>
  
  <p><strong>EDIT:</strong></p>
  
  <p><a href="https://craftcms.stackexchange.com/a/13194/45">The solution provided by @megatrond</a> is helpful, but doesn't really nail the problem here. I'm actually using that workaround for a different problem I encountered... I needed to guarantee that the user is authorized to ping my endpoint. In implementing that solution, I created a unique access key, included it in the URL, and checked against it before potentially returning a JSON-formatted error message. Under those circumstances, I was able to <strong>run a useless query</strong> for the sake of allowing me to return JSON.</p>
  
  <p>However, for the issue in <em>this thread</em>... The criteria will either return some results, or no results, and it's not possible to tell before allowing the query to run.</p>
  
  <p>Consider this scenario...</p>
  
  <p>Someone uses my endpoint to target the entry with id 42. Assuming entry #42 exists, the endpoint will successfully return data for it. But, if entry #42 has been <strong>deleted or disabled</strong>, the endpoint doesn't give me the opportunity to return an error message. Instead, it just kicks out a "404 Not Found" error. :(</p>
  
  <p>Ideally, I'm looking for <strong>a fallback for when the criteria returns zero results.</strong></p>
  
Tags: <404><plugin-elementapi>
Title: Element API plugin - Return message when no results are found
ThreadText: # Element API plugin - Return message when no results are found
  
  (Asked by: username_0 on 2016-01-12)
  
  Per this code example, it's clear that the API is expecting to return results based on an **existing element**...
  
  ```
  'transformer' => function(EntryModel $entry) {
      ...
  }
  
  ```
  
  And when the criteria finds a matching entry, everything works great!
  
  But when there is **no matching criteria results**, the API seems to simply return a generic 404 error.
  
  **How can I make the API return a message (in JSON format) instead of a 404?**
  
  ---
  
  **EDIT:**
  
  [The solution provided by @username_1](https://craftcms.stackexchange.com/a/13194/45) is helpful, but doesn't really nail the problem here. I'm actually using that workaround for a different problem I encountered... I needed to guarantee that the user is authorized to ping my endpoint. In implementing that solution, I created a unique access key, included it in the URL, and checked against it before potentially returning a JSON-formatted error message. Under those circumstances, I was able to **run a useless query** for the sake of allowing me to return JSON.
  
  However, for the issue in *this thread*... The criteria will either return some results, or no results, and it's not possible to tell before allowing the query to run.
  
  Consider this scenario...
  
  Someone uses my endpoint to target the entry with id 42. Assuming entry #42 exists, the endpoint will successfully return data for it. But, if entry #42 has been **deleted or disabled**, the endpoint doesn't give me the opportunity to return an error message. Instead, it just kicks out a "404 Not Found" error. :(
  
  Ideally, I'm looking for **a fallback for when the criteria returns zero results.**
  
  **Tags:** 404, plugin-elementapi
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_2 on 2016-01-14. Score: 6 | Accepted answer
  
  Currently, Element API is hard coded to throw a 404 exception for single element endpoints, if the element criteria returns no results. There's no way to prevent this exception, add "fallback" criteria or to display an error message.
  
  Note that the 404 exception will only be thrown if the `first` parameter is set to `true`, though. If it isn't, Element API will just return an empty dataset.
  
  Obviously, omitting the `first` parameter is not a real solution to your issue. You still have no way to set a custom error message, and it would also mean that all single element endpoints would return arrays, as well as paging metadata etc, instead of a "flat" JSON object.
  
  On that note, my opinion is that the canonical answer to this question should probably be to **fork the Element API plugin** and/or add a [feature request](http://feedback.craftcms.com/forums/285221-feature-requests).
  
  However, if you don't want to go down that road, here's a possible workaround. It's not pretty :)
  
  Basically, what you'll need to do is to catch the `HttpException`, in order to return a custom payload.
  
  To do this, you'll need to write a custom plugin. Important: **make sure that the string returned by your plugin's `getName()` method comes before "Element API", alphabetically**. The reason for this is that you need to make sure that Craft loads your plugin before the Element API plugin (Craft loads plugins alphabetically, by name). I'll pretend you opt for the name "Awesome Plugin", which would work nicely.
  
  Your plugin will need a controller, so create one called "AwesomeController" or the like. This controller should have a method called `actionGetElements`, taking the same arguments as the ElementApiController's `actionGetElements` method.
  
  The only purpose of your controller is to "wrap" the ElementApiController, in order to catch the 404 HttpException fired by Element API, and echo a custom error message. Here's how that could look:
  
  ```
  <?php
  namespace Craft;
  
  class AwesomeController extends BaseController
  {
  
      protected $allowAnonymous = true;
  
      public function actionGetElements($configFactory = null, array $config = null)
      {
          try {
  
              // Fire up the Element API controller
              craft()->runController('elementApi/getElements');
  
          } catch (HttpException $e) {
  
              switch ($e->statusCode) {
  
                  case 404 :
  
                      // It's a 404. Output a custom error message
                      JsonHelper::sendJsonHeaders();
                      echo JsonHelper::encode(array(
                          'error' => 'No element found',
                      ));
                      // End the request
                      craft()->end();
  
              }
          }
      }
  
  }
  
  ```
  
  Obviously, the controller will do nothing by itself – the final thing you need to do is to "hijack" the Element API routes; making them point to your controller instead of the ElementApiController. To achieve this, you'll need to create a method called `registerSiteRoutes()` in your plugin's primary class. Inside this method, you'll basically want to pull all the routes from the Element API plugin, changing their `action` value to `awesomePlugin/getElements`.
  
  This is why it's important to make sure that your plugin is loaded by Craft before the Element API plugin: when an existing route is added to Craft, it'll be ignored. This means that for every endpoint in `/config/elementapi.php`, you can trick Craft into loading your custom controller instead of ElementApiController!:
  
  Here's how that `registerSiteRoutes()` method could look:
  
  ```
  public function registerSiteRoutes()
  {
  
      $routes = [];
  
      // Get the Element API plugin
      $elementApiPlugin = craft()->plugins->getPlugin('elementApi');
  
      if (!$elementApiPlugin) {
          return $routes;
      }
  
      // Get the Element API routes
      $elementApiRoutes = $elementApiPlugin->registerSiteRoutes();
  
      // Set action property
      foreach ($elementApiRoutes as $endpoint => $config) {
          $config['action'] = 'awesomePlugin/getElements';
          $routes[$endpoint] = $config;
      }
  
      return $routes;
  
  }
  
  ```
  
  All in all – not exactly an elegant solution; but if the 404's are a dealbreaker to you, its at least a working workaround that doesn't require you to tamper with the Element API source, and the performance footprint should be minimal, too (no redundant database queries).
  
  > **Comments:**
  > **username_0**: Great answer, thank you! It still seems like a lot of workaround code to solve a (theoretically) simple problem. I'm going to submit a formal feature request... I feel strongly that the plugin should natively handle the situation more gracefully.  
  > **username_0**: Per your suggestion, I've added a formal feature request... Please vote if you agree! [Element API plugin: Better handling of NULL criteria results](http://feedback.craftcms.com/forums/285221-feature-requests/suggestions/11448138-element-api-plugin-better-handling-of-null-criter)  
  > **username_2**: I completely agree and voted for your request :)  
  > **username_3**: +1 for the ridiculous workaround, though. :)  
  > **username_2**: @username_3 Where there's a will, there's a ridiculous way :)  
  
  ---
  
  ### Answer by username_1 on 2016-01-13. Score: 1
  
  This is definitely a sub-optimal workaround, but it might work:
  
  Have your criteria in the API config fetch something you know is there, and there is only one of - your homepage, or a global set (maybe a global set that's called `errorMessages`?)
  
  Then do the normal
  
  ```
  $criteria = craft()->elements->getCriteria(ElementType::Entry);
  $criteria->section = 'sectionHandle';
  // maybe some more criteria in here
  $entries = $criteria->find();
  
  ```
  
  and return your desired message if `$entries` is `null`.
  
  > **Comments:**
  > **username_0**: Thanks username_1... This is somewhat helpful, though doesn't exactly resolve the problem. I edited my question to elaborate the use-case. I think your solution could potentially be used to achieve the desired effect... but as you said, it's a "sub-optimal workaround". At that rate, I'd wonder why I wasn't just using a JSON-formatted template in the first place?  
  > **username_1**: I agree with you, and so I [forked](https://github.com/username_1/ElementAPI) the ElementApi and added a config param called `handleNotFound`, which whould be a function, and return whatever you'd like if an element is not found. It is only used when `first` is enabled. If no handler is provided, a 404 will be thrown.  
  > **username_0**: Very interesting... thanks for the fork link!  
  
  ---
  
  