REST APIs with JSON

Learn how modern applications serving APIs communicate with the world and their relationship with JavaScript.

For many years, the primary goal of the PHP application was to generate HTML pages. Now it’s changing.

  • First, single-page applications are becoming more common. In these applications, the server sends a single HTML file containing only a link to a JavaScript bundle that builds the whole document on the client-side.
  • Second, monoliths are getting replaced with collections of microservices. Most PHP services are never meant to be called directly by the browser but instead, by other services.

In both cases, PHP applications need to provide a convenient API for other programs. And, that’s why the REST API is popular.

What is the REST API?

Representational state transfer (REST) is an architectural style for distributed hypermedia systems proposed by Roy Thomas Fielding. It doesn’t instruct how to implement such systems but instead applies some constraints to their elements.

  • It has to be a client-server architecture in which the client and server can evolve independently.
  • Client-server requests have to be stateless. The server cannot store session data. The client is responsible for keeping the session’s context.
  • The server has to be able to label some responses as cacheable.
  • There has to be a uniform interface between all the components in the system.
  • The client can’t indicate whether the server that it sends the request to is the end server that can handle it. It can be a layered system in which each component can only see the next layer.
  • The last (optional) constraint is a code-on-demand style. It allows the client to extend the functionality by downloading code from the server as applets and scripts.

The unit of information in a REST API is a resource. A resource can be any information we can name and give a unique identifier.

Clients usually don’t see resources in the same form that the server uses to store them. Instead, clients see resource representations that are conceptually separate from resources. Clients can manipulate the resources via resource representations.

REST APIs in practice

For those feeling puzzled right now, the original section above describes the REST architecture style but doesn’t provide any implementation details. So, we’ll have to decide independently. But, some practices used to define HTTP API are strongly associated with REST

HTTP methods

There are two common ways to use HTTP methods. The first way is to use GET for all the read requests and POST for all the write requests. All applications producing web pages do so because the HTML form element only supports GET and POST methods.

The second way is to know and utilize different methods described in the HTTP standard. Here’s how we can use them in a blog engine:

  • GET is used to access resources and retrieve data from the server. GET requests should be read-only (i.e., they should never trigger database changes). Also, both the client and server can cache these requests.
    • Use case: Viewing a blog post.
  • POST is used to send data to the server. It’s common to use POST only to create new resources, contrary to website applications.
    • Use case: Creating a new blog post.
  • PUT is used to update an existing resource on the server. PUT requests should be idempotent, meaning multiple identical PUT requests should have the same effect as one.
    • Use case: Saving a blog post after editing.
  • PATCH is used to partially update an existing resource on the server. A PATCH request provides instructions on how to update the resource. It doesn’t have to be idempotent.
    • Use case: Increasing the like count of a blog post.
  • DELETE is used to delete resources on the server.
    • Use case: Deleting a blog post.

Composing URLs

Every resource and every resource collection needs to have different URLs. So it’s common to include both resource types and IDs in the URLs. Collection URLs would look like /resource-type, and item URLs would look like /resource-type/{resource-id}.

Resources can also be nested hierarchically in the URLs, like /resource-type/{resource-id}/subresource-type and /resource-type/{resource-id}/subresource-type/{subresource-id}. Usually, we don’t need to add verbs like get or create to the URLs because we have them in the HTTP method. But, we can also add some verb terms to support operations not covered by HTTP methods.

Here are some routes we might find in a blog engine:

  • GET /posts: Gets a list of blog posts.
  • GET /posts/search?query=api: Searches for blog posts with the word API.
  • POST /posts: Adds a new blog post.
  • GET /posts/123: Views blog post #123.
  • GET /posts/123/comments: Lists comments in blog post #123.
  • PUT /posts/123: Saves blog post #123 after editing.
  • DELETE /posts/123/comments/456: Deletes comment #456 in blog post #123.

At the point the DELETE /posts/123/comments/456 route is created, we might remember that, in this specific application, the comment ID is unique among all the blog posts, and maybe we should get rid of the /posts/123 in the URL and use only the /comments/456 part. There’s no correct answer to this.

Response statuses

As well as embracing HTTP for API requests, we embrace them for our responses. The common practice is to learn the list of HTTP status codes and use them to indicate our response statuses. This works well for successful and redirect status codes, but it’s even more important to use them to explain error statuses. And, that’s where we run into issues.

The first issue is that the memorized list of HTTP status codes is a static list of codes, and there might be no matching status code for a specific error in your application. Because of that, people often use the status codes 400 Bad Request and 500 Internal Server Error as umbrella terms for client-caused and server-caused errors, respectively.

The second issue is that these status codes are for HTTP servers to process requests before PHP. The servers can respond with one of these statuses without involving our application.

These issues can complicate error handling. By receiving the 400 Bad Request as a response to a registration request, we can’t say if we have sent an invalid HTTP message, used the wrong field names, or if a user with the same email already exists. The preferred method of handling these kinds of cases is to show a descriptive error message to the user or notify the developer.

Because of these issues, some developers propose to return 200 OK every time the request reaches the application and to return the actual application-level status inside the response body.

JSON format for message bodies

Let’s look at the message contents now. We need to send potentially complex data structures, and we want them to be easily processable. HTML won’t work for that. It’s hard to parse and has too many formatting-specific features. But, JSON format fits this task perfectly.

JSON, short for JavaScript Object Notation, is an open, human-readable data format that implements a simple idea—let’s encode data the same way we define variables in JavaScript. It’s well-known, and many modern programming languages support it.

JSON can hold the following:

  • Numbers like 42 and 13.5.
  • Strings like "Hello, world!" and "First line\nSecond line".
  • Boolean values like true and false.
  • Null values like null.
  • Arrays, ordered collections of values of any type, like ["spam", "ham"] and [1, 2, "three"].
  • Objects, unordered key-value maps with string keys and values of any type, like {"question": "2 + 2", "answer": 4}.

Using these rules, we can build complex, nested structures, like this one:

Get hands-on with 1200+ tech skills courses.