Creating a REST Service
In this chapter, we'll create a simple REST service.
Assumptions
This chapter assumes you have read and followed both the installation guide and the getting started chapter. If you have not, please do before continuing.
You will need to install and configure the laminas-api-tools/statuslib-example module in order to perform
this tutorial. Follow these steps:
Step 1
Within your application root, execute the following:
$ composer require laminas-api-tools/statuslib-example
You will be prompted as to whether or not you wish to inject the module into configuration; you do, and you should inject it into config/modules.config.php.
Manual registration
If you do not get prompted to inject the module, or accept the default, which does not inject, you will need to add the entry
'StatusLib'toconfig/modules.config.php.
Step 2
Create a PHP file data/statuslib.php that returns an array:
<?php
return [];
Make sure the file is writable by the web server user.
Step 3
Edit the file config/autoload/local.php to add the following configuration:
return [
/* ... */
'statuslib' => [
'array_mapper_path' => 'data/statuslib.php',
],
];
Step 4
Finally, you will need a valid HTTP basic credentials file, usually titled htpasswd. You can
generate one using the standard htpasswd tool provided by
Apache, or use an online htpasswd
generator. Store the htpasswd file as
data/htpasswd in your application. Make a note of the credentials you use so that you can use them
later.
Once those steps are complete, continue with the tutorial.
Terminology
Within the Laminas API Tools documentation, and, in particular, this chapter, uses the following terminology:
- Entity
- An addressable item being returned. Entities are distinguished by a unique identifier present in the URI.
- Collection
- A addressable set of entities. Typically, all entities contained in the collection are of the same type, and share the same base URI as the collection.
- Resource
- An object that receives the incoming request data, determines whether a collection or entity was identified in the URI, and determines what operation to perform.
- Relational Links
- A URI to a resource that has the described relation. Relational links allow you to describe relations between different entities and collections, as well as directly link to them so that the web service client can perform operations on those relations. These are also sometimes called hypermedia links.
REST services return entities and collections, and provide hypermedia links between related entities and collections. Resource objects coordinate operations, and return entities and collections.
Create a REST Service
In this chapter, we're going to build a sample REST service.
In the sidebar, click the "New Service" button. The REST tab is selected by default; provide the value "Status" for the "Service name" field and press the "Create service" button.

REST vs DB-Connected services
When you create a REST service, API Tools creates a stub "Resource" class that defines all the various operations available in the service. These operations return
405 Method Not Allowedresponses until you fill them in with your own code. That means you need to supply the code that performs the actual work of your API; API Tools provides the wiring for exposing that code as an API.DB-Connected services are also REST services. They allow you to specify a database adapter, and then to choose one or more tables to expose as services. API Tools then creates "virtual" Resources which delegate operations to underlying Laminas\Db\TableGateway\TableGateway instances. In other words, DB-Connected is more of a rapid application development (RAD) or prototyping tool.
Once the service has been successfully created, the "Status" page will show up.

API Tools provides a number of sane defaults:
- Collections only allow
GET(fetch a list) andPOST(create a new entity) operations. - Entities allow
GET(fetch an entity),PUT(replace the entity),PATCH(perform a partial update), andDELETE(remove the entity) operations. - If your collection supports pagination, API Tools will limit to 25 items per "page" of results.
- API Tools creates a routing URI based on the service name (e.g.,
/status[/:status_id]).
URI Routing
API Tools runs on top of a Laminas MVC stack, and thus uses its routing engine.
The routes generated by API Tools are all what are known as "Segment" routes. Segment routes allow you to:
- Specify optional portions of the URI, using
[ ]syntax.- Specify named parameters to match using
:varNameor:var_namesyntax.- Specify literal matches; anything not a named parameter, or within braces (
{ }) is considered a literal.For REST services, the URI generated has a literal, mandatory match that, when specified by itself, resolves to a collection; in the example above, this would be the path
/status. It also has an optional segment with a named parameter, what we call the "entity identifer":[/:status_id]. This will match URIs such as/status/foo,/status/2,/status/96fa5ac9-3ae2-45b2-84d5-c346936be292.One note: the
/between the collection URI and the entity URI can only be specified when specifying an entity; you cannot request the collection with a trailing slash.Why? Because one tenet of REST is one URI, one resource. If we allowed a trailing slash, we'd be allowing multiple URIs to resolve to the same resource.
In the REST service page, you'll see a field named "Hydrator Service Name" with a value
of Laminas\Hydrator\ArraySerializableHydrator. We're going to change this to work with our StatusLib
example library. For the "Hydrator Service Name", select the value
Laminas\Hydrator\ObjectPropertyHydrator.

Hydrators
Hydrators are objects that allow for casting an associative array to a specific object type and vice versa. Each hydrator employs a different strategy for how this is done. The default hydrator type that API Tools uses is the
ArraySerializableHydratortype, which expects an object to implement two methods:
getArrayCopy()for extracting an array representationexchangeArray($array)for casting an array to the object(These are the same methods used in PHP's
ArrayObject!)The
ObjectPropertyHydratorhydrator will extract any public properties of an object when creating an array representation, and populate public properties of the object from an arraywhen casting to an object.
For our example, the StatusLib library provides its own Entity and Collection classes. Edit the
"Entity Class" field to read StatusLib\Entity and the "Collection Class" field to read
StatusLib\Collection. Finally click the "Save" button at the bottom of the screen.

Service Classes
When you create a Code-Connected service, API Tools generates four PHP class files for you:
- An Entity class
- A Collection class which extends
Laminas\Paginator\Paginator, which will allow you to provide paginated result sets.- A Resource class for performing operations.
- A Factory class for the Resource created.
Your own code may already define entity and collection classes that you want to use, so you are free to ignore the stub classes API Tools creates. One note, however: if you end up versioning your API, you may find that having version-specific entity and collection classes can be useful, as they can allow you to model only the properties you wish to expose for each specific version.
Next, let's define some fields, and document our API.
Define fields for our service
The fields we'll define are:
-
message- a status message. It must be non-empty, and no more than 140 characters. -
user- the user providing the status message. It must be non-empty, and fulfill a regular expression. -
timestamp- an integer timestamp. It does not need to be submitted, but if it is, must consist of only digits. It will always be returned in representations.
We'll also have an id field, but this will only be for purposes of display.
Navigate to the "Fields" tab, and click the "New field" button. In the text input titled "Name", type the word "message"; for the "Description", enter "A status message of no more than 140 characters", and for the "Validation Failure Message," enter "A status message must contain between 1 and 140 characters". Finally press the "Save" button to create the new field.

Repeat the procedure to create the fields "user" and "timestamp", without descriptions or failure messages.
Following the best practices of "Filter Input, Escape Output", we want to create a filter for the "message" field. Click the + (plus) icon in the Filter column for the "message" field.

Select Laminas\Filter\StringTrim in the "Filter" drop-down list, and click the "Save" button.
We want also to add a validator for the "message" field. Click the "+" (plus) icon in the "Validator"
colum. In the modal window, select Laminas\Validator\StringLength as "Validator", and select the
max option for the "Option". Insert the value 140 for the max option and click on the "Add option"
button; you will see the option appear in the table below.

Now you can click on the "Save" button to save the configuration; you should see the following on your screen:

At this point, perform the following:
- Edit the "user" field:
- Add a description of "The user submitting the status message."
- Add a validation failure message of "You must provide a valid user."
- Add a
Laminas\Filter\StringTrimfilter. - Add a
Laminas\Validator\Regexvalidator; give it apatternoption, with the value/^(mwop|andi|zeev)$/(feel free to substitute or add other names or nicknames as desired).
- Update the "timestamp" field:
- Add a description of "The timestamp when the status message was last modified."
- Add a validation failure message of "You must provide a timestamp."
- Toggle the "Required" flag to read "No."
- Add a
Laminas\Validator\Digitsvalidator.
Below is a screenshot detailing what the "Fields" tab will look like on completion.

Let's move on to documentation.
Documentation
REST services allow you to document not only by HTTP method, but by HTTP method for each of Collections and Entities.
The procedure for documenting a REST service is just like we learned in the Getting Started chapter, with only one difference:
- You will need to document HTTP methods for both collections and entities.
Your exercise now is to document both collection and entity operations:
- Give the service a description of "Create, manipulate, and retrieve status messages."
- Give collections a description of "Manipulate lists of status messages."
- For the
GETmethod, describe it as "Retrieve a paginated list of status messages." - For the
POSTmethod, describe it as "Create a new status messages."
- For the
- Give entities a description of "Manipulate and retrieve individual status messages."
- For the
GETmethod, describe it as "Retrieve a status message." - For the
PATCHmethod, describe it as "Update a status message." - For the
PUTmethod, describe it as "Replace a status message." - For the
DELETEmethod, describe it as "Delete a status message."
- For the
We'll examine the documentation later. For now, let's move on to authentication and authorization.