<p>Sure – the <code>element-api.php</code> config file is just plain ol' PHP, and the functions that make up the individual endpoints (e.g. <code>'news.json' =&gt; function () { ...</code>) doesn't necessarily have to be declared inside that file.</p>

<p>One way to split up the file would be to create one PHP class per element type or section. You could compose these classes however you'd like, but something like the below would work (this is an example file containing two endpoints for the <code>news</code> section, and the example assumes that the file is saved under <code>/config/endpoints/News.php</code>):</p>

<pre><code>&lt;?php

namespace config\endpoints;

use craft\elements\Entry;
use craft\helpers\UrlHelper;

class News {

    public static function index()
    {
        return [
            'elementType' =&gt; Entry::class,
            'criteria' =&gt; ['section' =&gt; 'news'],
            'transformer' =&gt; function(Entry $entry) {
                ...
            }
        ];
    }

    public static function view($entryId)
    {
        return [
            'elementType' =&gt; Entry::class,
            'criteria' =&gt; ['id' =&gt; $entryId],
            'one' =&gt; true,
            'transformer' =&gt; function(Entry $entry) {
                 ...
            }
        ];
    }
}
</code></pre>

<p>The next step is to make the file available to the <code>element-api.php</code> config file. One way to do it, is to simply <code>include()</code> it at the top of the <code>element-api.php</code> file:</p>

<pre><code>include __DIR__ . '/endpoints/News.php';
</code></pre>

<p>Then, you can add the endpoints from the <code>News</code> endpoint class to the config in <code>element-api.php</code>, i.e.:</p>

<pre><code>&lt;?php

include __DIR__ . '/endpoints/News.php';

use config\endpoints\News;

return [
    'endpoints' =&gt; [
        // use an anonymous function to prevent errors when running via cli
        'news.json' =&gt; function () {
            return News::index();
        },
        'news/&lt;entryId:\d+&gt;.json' =&gt; function ($entryId) {
            return News::view($entryId);
        },
    ],
];
</code></pre>

<p>If you want to take this a step further, a much nicer approach than using <code>include()</code> is to have Composer autoload your endpoint classes by adding the below to your <code>composer.json</code> file (again, this assumes that the custom endpoint classes live under <code>/config/endpoints/</code>):</p>

<pre><code>"autoload": {
    "psr-4": {
        "config\\endpoints\\": "config/endpoints"
    }
},
</code></pre>

<p>After editing your <code>composer.json</code> file, make sure to regenerate the autoloader by running the following from your command line (root of project, same directory where you keep your <code>composer.json</code>:</p>

<pre><code>composer dump-autoload -o
</code></pre>

<p>Then, you can strip out the <code>include()</code> statement(s) in your <code>element-api.php</code> file, because Composer will autoload all the classes under <code>/config/endpoints</code>.</p>

<p>Finally, to make things super clean you could of course <a href="https://nystudio107.com/blog/enhancing-a-craft-cms-3-website-with-a-custom-module" rel="nofollow noreferrer">create a custom Yii Module</a> (or a Craft plugin) to contain your endpoint classes. This would also avoid having to manually add the classpath to the <code>composer.json</code> file, for the autoloading.</p>

CC BY-SA 4.0
2018-08-22T09:32:29.917Z
I've given it a try as you've described but for some reason the slug / entry ID don't get passed through to the view function at all so it just returns the most recent entry every time. So you have any idea why this wouldn't be working?
George Feast-Parker
CC BY-SA 4.0
2018-08-22T09:52:42.060Z
@GeorgeFeast-Parker Yeah, I took another look at it, and I messed up the original  example. Check out the latest edit; it should work.
Mats Mikkel Rummelhoff
CC BY-SA 4.0
2019-11-12T19:15:03.943Z
Wonderful, thanks a lot for that! 

One question: why does it need the anonymous function when a parameter is passed, what is the reason `'news/<entryId:\d+>.json' => News::view($entryId);` won't work?
Urs
CC BY-SA 4.0
2019-11-24T08:39:19.467Z
And here's the answer to that from Pixel&Tonic: "make sure all of your endpoint config are inside an anonymous function. Doing so will ensure your endpoint config will only be generated when the endpoint is actually requested, rather than on every web & console request". When element-api was triggerd by cli, I got many errors. I'll edit above.
Urs
CC BY-SA 4.0
2018-08-03T09:42:53.090Z
2019-11-25T19:08:05.037Z
2019-11-25T19:08:05.037Z
Mats Mikkel Rummelhoff
<p>Is there some way I can split the element-api.php file into separate files and import them all? It's getting quite long and unwieldy.</p>

<plugin-elementapi>
Is there a way to split the element-api.php file?
# Is there a way to split the element-api.php file?

(Asked by: username_0 on 2018-08-03)

Is there some way I can split the element-api.php file into separate files and import them all? It's getting quite long and unwieldy.

**Tags:** plugin-elementapi


---

## 1 answers

### Answer by username_1 on 2018-08-03. Score: 7

Sure – the element-api.php config file is just plain ol' PHP, and the functions that make up the individual endpoints (e.g. 'news.json' => function () { ...) doesn't necessarily have to be declared inside that file.
One way to split up the file would be to create one PHP class per element type or section. You could compose these classes however you'd like, but something like the below would work (this is an example file containing two endpoints for the news section, and the example assumes that the file is saved under /config/endpoints/News.php):
 Entry::class,
            'criteria' => ['section' => 'news'],
            'transformer' => function(Entry $entry) {
                ...
            }
        ];
    }

    public static function view($entryId)
    {
        return [
            'elementType' => Entry::class,
            'criteria' => ['id' => $entryId],
            'one' => true,
            'transformer' => function(Entry $entry) {
                 ...
            }
        ];
    }
}

The next step is to make the file available to the element-api.php config file. One way to do it, is to simply include() it at the top of the element-api.php file:
include __DIR__ . '/endpoints/News.php';

Then, you can add the endpoints from the News endpoint class to the config in element-api.php, i.e.:
 [
        // use an anonymous function to prevent errors when running via cli
        'news.json' => function () {
            return News::index();
        },
        'news/.json' => function ($entryId) {
            return News::view($entryId);
        },
    ],
];

If you want to take this a step further, a much nicer approach than using include() is to have Composer autoload your endpoint classes by adding the below to your composer.json file (again, this assumes that the custom endpoint classes live under /config/endpoints/):
"autoload": {
    "psr-4": {
        "config\\endpoints\\": "config/endpoints"
    }
},

After editing your composer.json file, make sure to regenerate the autoloader by running the following from your command line (root of project, same directory where you keep your composer.json:
composer dump-autoload -o

Then, you can strip out the include() statement(s) in your element-api.php file, because Composer will autoload all the classes under /config/endpoints.
Finally, to make things super clean you could of course create a custom Yii Module (or a Craft plugin) to contain your endpoint classes. This would also avoid having to manually add the classpath to the composer.json file, for the autoloading.

> **Comments:**
> **username_0**: I've given it a try as you've described but for some reason the slug / entry ID don't get passed through to the view function at all so it just returns the most recent entry every time. So you have any idea why this wouldn't be working?  
> **username_1**: @GeorgeFeast-Parker Yeah, I took another look at it, and I messed up the original  example. Check out the latest edit; it should work.  
> **username_2**: Wonderful, thanks a lot for that!   One question: why does it need the anonymous function when a parameter is passed, what is the reason `'news/<entryId:\d+>.json' => News::view($entryId);` won't work?  
> **username_2**: And here's the answer to that from Pixel&Tonic: "make sure all of your endpoint config are inside an anonymous function. Doing so will ensure your endpoint config will only be generated when the endpoint is actually requested, rather than on every web & console request". When element-api was triggerd by cli, I got many errors. I'll edit above.  

---

