We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Micro API versioning

Hi,

I've searched quite a bit, but could not find any information as to how one could implement API versioning with the Phalcon Micro framework. What I'm trying to have the micro framework handle is this: https://myapp/api/v1/say/welcome/dude, as well as https://myapp/api/v2/say/hi/dude

But whereas this works:

<?php
$app = new \Phalcon\Mvc\Micro;
$app->get('/say/welcome/{name}', function ($name) {
  echo "<h2>Hi there, $name!</h2>";
});

This does not:

<?php
$app = new \Phalcon\Mvc\Micro;
$app->get('/api/v1/say/welcome/{name}', function ($name) {
    echo "<h2>Hi there, $name!</h2>";
});
$app->notFound(function () use ($app) {
    $app->response->setStatusCode(404, "Not Found")->sendHeaders();
    echo 'This is crazy, but this page was not found!';
});
    $app->handle();

Apache does not find the resource.The notFound handler is not even being invoked. The directory structure is dead simple:

www
└── public
    ├── index.php
    ├── .htaccess
    └── api
        └── v1
            └── index.php
            └── .htaccess

Both .htaccess files files contain the same instructions:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
</IfModule>

I've been trying to fiddle with the RewriteCond and RewriteRule setting in the .htaccess files, but to no avail...

Am I taking the wrong approach for API versioning?

If not, how can I get this to work?

Many thanks for your ideas and input,

Steven

P.S.: Using PHP-fpm 5.5 as FastCGI on Apache 2.2.22 with Phalcon 1.2.6 on a Debian 7 VM



43.9k

Hi, what does apache2 error.log tell you ?



14.7k
edited Mar '14

Hi le51,

Thanks for your reply.

For the request to /say/welcome/dude, it says:

==> 001-centric-access.log <==
172.16.198.1 - - [11/Mar/2014:15:48:15 +0100] "GET /say/welcome/dude HTTP/1.1" 200 315 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/32.0.1700.107 Chrome/32.0.1700.107 Safari/537.36"

For the request to /api/v1/say/welcome/dude, it goes:

==> 001-centric-access.log <==
172.16.198.1 - - [11/Mar/2014:15:51:50 +0100] "GET /api/v1/say/welcome/dude HTTP/1.1" 404 338 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/32.0.1700.107 Chrome/32.0.1700.107 Safari/537.36"

And, my mistake, the notFound handler is invoked though.

Still, I don't see how I can implement my versioning scheme... Why isn't the router /api/v1/say/welcome/{name} in api/v1/index.php called upon this request? How does the Phalcon Micro app handle such requests?

Any help greatly appreciated!

Steven



3.2k
Accepted
answer
edited Mar '14

I don't see why you have that folder structure, phalcon can handle that url (/api/v1/welcome) without a second index.php

Simpy put the two get() calls together and remove the app/v1 folder:

<?php
$app = new \Phalcon\Mvc\Micro;
$app->get('/say/welcome/{name}', function ($name) {
  echo "<h2>Hi there, $name! v2</h2>";
});
$app->get('/api/v1/say/welcome/{name}', function ($name) {
    echo "<h2>Hi there, $name! v1</h2>";
});
$app->notFound(function () use ($app) {
    $app->response->setStatusCode(404, "Not Found")->sendHeaders();
    echo 'This is crazy, but this page was not found!';
});

My guess is that the second index.php never gets executed because the url /api/v1/say/welcome/dude doesn't in fact exists, so the first .htaccess is read and the rule RewriteCond %{REQUEST_FILENAME} !-f evaluates to true, and the whole url gets passed to the first index.php.



14.7k

Hi Maxgalbu,

Thanks for your comment! I had started off the way you suggested before posting here and it didn't work. That's why I added the directory structure.

But I discovered the really stupid error that my initial .htaccess was misplaced: it was under /www instead of /www/public ! After placing the .htaccess, everything works fine.

Really sorry to have wasted your time here :-/



14.7k
edited Mar '14

Hi,

As a follow-up, and for whom it may be of interest, here's what I came up with to be able to serve several API versions on the same domain without having them all under the web server's DocumentRoot.

The predicate is to be able to serve the API documentation from /, API version 1 from /api/v1 and API version 2 from /api/v2.

Here's the Apache VirtualHost config for this:

<VirtualHost *:80>
        ServerAdmin [email protected]

        ServerName myserver

        Alias /api/v1      /www/v1/public
        Alias /api/v2      /www/v2/public

        DocumentRoot /www/public

        <Directory /www/public>
                DirectoryIndex  index.html index.php
                Options FollowSymLinks
                AllowOverride Indexes
                Order allow,deny
                allow from all
        </Directory>

        <Directory /www/v1/public>
                Options FollowSymLinks MultiViews ExecCGI
                AddHandler fcgid-script .php
                FCGIWrapper /usr/lib/cgi-bin/php5 .php
                AllowOverride None
                Order allow,deny
                allow from all
                <IfModule mod_rewrite.c>
                        RewriteEngine On
                        RewriteCond %{REQUEST_FILENAME} !-f
                        RewriteRule ^www/v1/public/(.*)$ index.php?_url=/$1 [QSA,l]
                </IfModule>
        </Directory>

        <Directory /www/v2/public>
                Options FollowSymLinks MultiViews ExecCGI
                AddHandler fcgid-script .php
                FCGIWrapper /usr/lib/cgi-bin/php5 .php
                AllowOverride None
                Order allow,deny
                allow from all
                <IfModule mod_rewrite.c>
                        RewriteEngine On
                        RewriteCond %{REQUEST_FILENAME} !-f
                        RewriteRule ^www/v2/public/(.*)$ index.php?_url=/$1 [QSA,l]
                </IfModule>
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/001-myserver-error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/001-myserver-access.log combined
</VirtualHost>

The directory structure on the server is as follows:

www
├── public
│   ├── index.html
├── v1
│   ├── app.php
│   ├── config
│   │   ├── config.php
│   │   ├── loader.php
│   │   └── services.php
│   ├── controllers
│   │   └── IndexController.php
│   ├── public
│   │   └── index.php
│   ├── models
│   └── views
│       ├── 404.phtml
│       └── index.phtml
└── v2
    ├── app.php
    ├── config
    │   ├── config.php
    │   ├── loader.php
    │   └── services.php
    ├── controllers
    │   └── IndexController.php
    ├── public
    │   └── index.php
    ├── models
    └── views
        ├── 404.phtml
        └── index.phtml

So now API versions are neatly separated, each with it's very own complete micro framework. Easier for the IDE, too: two separate projects.

Ideas or suggestions welcome.

All the best, Steven

Hi svanpoeck,

First, sorry for my english, and thank you for the good post!

I'm trying use the same configuration in nginx, but I cannot do it. Can you help me to do a similar configuration using the nginx? This model application is very cool, once that I want to build my application using angularjs in my frontend.

I thank you.

edited Sep '16

Use subdomain:

  • api.myapp.com/v1/
  • api.myapp.com/v2/
  • api.myapp.com/v3/

or

  • www.myapp.com/api/v1/
  • www.myapp.com/api/v2/
  • www.myapp.com/api/v3/