In a recent webinar, the second in our 2018 PHP expert talks series, Enrico Zimuel showed you how to develop web APIs in PHP using the popular Zend Expressive framework. In this post, we answer your questions from the webinar.
Can we use user-friendly URLs instead of a query string? For example, can we use “/api/users/page/2″ instead of “/api/users?page=2″?
Yes! You can define anything you want for routes, and have your handlers pull the relevant details from the request. All route parameters are available from the request attributes by default.
One note, however: Things like pagination, sorting, filtering, etc. are generally best done as query string arguments. This is because the path should represent the resource. Page numbers, offsets, sorting, and filtering are things that modify representation of the resource, but you are still requesting the same resource. Additionally, as you add more of these modifiers, your routes become complex, as they need to either accept all possible modifiers or do conditional matching. Query string arguments are built for this specific purpose.
An interesting discussion around this can be here.
Is it better if you can access “vendor/bin/expressive” by just typing “express” only?
You can control where binaries are installed using a Composer configuration directive within your project’s
composer.json. The following would install all binaries from all packages in the root folder:
That said, if you do that, most operating systems will still require that you prefix the binary with “./”, as “.” generally is not in a user’s
We do not set up that configuration directive by default, as we do not want to clutter the root directory, and most Composer users are accustomed to installing binaries in
One thing you can do, however, is alter your
$PATH environment variable to include
./vendor/bin/. This approach will accomplish exactly what you illustrate.
Why if we try the same get with a browser, the response is XML, while with HTTP GET :8080, the response is JSON?
This will vary based on the default
Accept header sent by your browser. Most browsers send an
Accept header that prioritizes HTML and XML over other content types; as such, since we can provide XML, we do provide it.
HTTPie, which is the CLI tooling Enrico used in his talk, sends an
Accept header that prioritizes JSON.
Generally, in your browser, if you are contacting an API, you will send your request using XmlHttpRequest or the Fetch API. When you do, you can specify the
Accept header value manually to force returning JSON from the API.
Can we implement Expressive on Symfony?
You can dispatch middleware from Symfony using the Symfony PSR-7 Bridge.
Does Expressive autogenerate response for OPTIONS method?
zend-expressive-router, which is shipped by default with the Expressive skeleton, we provide
Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware, which determines if a route failure is due to the HTTP method, and, if so, reports a 405 response status, with an
Allow header indicating which HTTP methods are allowed for the route. This middleware is composed in the default pipeline provided in the Expressive skeleton.
See here for more information.
Why do we need two middlewares, handle and process?
The two serve separate contexts.
PSR-15 defines two interfaces, one for request handlers and one for middleware.
A request handler receives a request, and MUST return a response. That’s it.
Middleware also receives a request, but additionally receives a request handler. If the middleware can generate a response on its own, it can return it. However, if it cannot, it has the option of calling the request handler to do so.
In Stratigility (our middleware foundation) and, by extension, Expressive, we push our pipeline into a request handler that is then passed from one middleware to the next. Each time
$handler->handle() is called, it advances an internal index to find the next middleware, which it then processes.
In Expressive, we decorate handlers that you push into the middleware pipeline as middleware. However, these will always return a response, so any middleware after them will be skipped. As such, we recommend having handlers as the last items in your pipelines only.
Do we have a timeline when zend-expressive-authentication can be used in production? I mean OAuth2 part.
We do not have a hard deadline set for completion. However, we expect that it will be ready for general consumption by the end of September 2018.
I would like to build a Single Sign-On system using OAuth2. How can I generate a token to be valid only for a subset of applications?
You can use the scopes specified by OAuth2. Scopes provide a way to limit the amount of access that is granted to an access token. A scope basically defines a permission. If you use an application ID as scope, you can control the access to a specific application. You can have a look at the scopes used by
thephpleague/oauth2-server, the OAuth2 server implementation used by
Is there any command in zend-expressive-tooling to create a RESTful skeleton API, ready to go?
Not yet, but it’s planned!
We will likely provide an additional skeleton project, built on top of
zend-expressive-skeleton, for APIs, as well as some tooling for adding middleware, handlers, and routing for REST resources. However, we first need to finalize a few implementation details before jumping into this.
What are the advantages to using a middleware architecture instead of a classic MVC?
In two words: granular workflows.
In most MVC systems, you will have a single workflow that triggers events for specific tasks (routing, dispatch, rendering, etc.). While this is flexible and provides predictable structure, it also means that listeners often need logic to determine if they are REALLY interested in a given event (e.g. Is this event related to a specific module or controller? Have certain other things already happened in the request lifecycle?), and this adds a fair amount of maintenance and performance overhead.
Middleware, on the other hand, allows you to define a workflow specific to your application, to a subpath, to a specific domain, to a specific HTTP method, or even to a specific route. If you do not need to perform authentication or authorization for a given route, you can compose such checks only with the routes that need them. If you do not need to do content validation for specific HTTP methods, you can omit such checks, composing them only where they are relevant.
Because of this, middleware tends to be more stateless as well, which means that it often becomes simpler. The majority of middleware we write has fewer than two dozen lines of code, because by the time we get to the middleware, we know we have exactly what we need, and only need to marshal it for our domain objects.
MVC is useful if you know the framework in question, and want to enforce the specific workflow it follows. Middleware is useful when you want to streamline every possible path through your application.
Missed Enrico’s webinar? Watch the on-demand version. And be sure to attend the next webinar in our 2018 PHP expert talks series, Maxing out performance with Zend Server on PHP 7, hosted by Zeev Suraski. Register once to get access to every talk in this series, both the live and on-demand versions.