What the difference between REST API and RESTful API?

What the difference between REST API and RESTful API?

Representational state transfer (REST) is a style of software architecture. As described in a dissertation by Roy Fielding, REST is an "architectural style" that basically exploits the existing technology and protocols of the Web. RESTful is typically used to refer to web services implementing such an architecture.

The short answer is that REST stands for Representational State Transfer. It’s an architectural pattern for creating web services. A RESTful service is one that implements that pattern.

The long answer starts with “sort of” and “it depends” and continues with more complete definitions.

Defining REST

Let’s start by defining what REST is and is not. For some, REST means a server that exchanges JSON documents with a client over HTTP. Not only is that not a complete definition, but it’s also not always true. The REST specification doesn’t require HTTP or JSON. (The spec doesn’t mention JSON or XML at all.)

The Origins of REST

Roy Fielding introduced the REST architectural pattern in a dissertation he wrote in 2000. The paper defines a means for clients and servers to exchange application data. A key feature is that the client doesn’t need to know anything about the application in advance. The link is to chapter five of his paper. While the entire dissertation describes the hows and whys of REST, that chapter defines the architectural pattern.

Fielding doesn’t mandate specific requirements. Instead, he defines REST regarding constraints and architectural elements.

REST’s Architectural Constraints

Here is a summary of the constraints.

  • Client-server – REST applications have a server that manages application data and state. The server communicates with a client that handles the user interactions. A clear separation of concerns divides the two components. This means you can update and improve them in independent tracks.
  • Stateless – servers don’t maintain any client state. Clients manage their application state. Their requests to servers contain all the information required to process them.
  • Cacheable – servers must mark their responses as cacheable or not. So, infrastructures and clients can cache them when possible to improve performance. They can dispose of non-cacheable Information, so no client uses stale data.
  • Uniform interface – this constraint is REST’s most well known feature or rule, depending on who you ask. Fielding says “The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.” REST services provide data as resources, with a consistent namespace. We’ll cover this in detail below.
  • Layered system – components in the system cannot “see” beyond their layer. So, you can easily add load-balancers and proxies to improve security or performance.

A RESTful service is more than a web server that exchanges JSON, or any other, documents. These constraints work together to create a very specific type of application.

Applying the Constraints

First, the client-server, layered systems and stateless constraints combine to form an application with solid boundaries and clear separations between concerns. Data moves from the server to the client upon request. The client displays or manipulates it. If the state changes, the client sends it back to the server for storage. Fielding specifically contrasts REST with architectures that use distributed objects to hide data from other components. In REST, the client and server share knowledge about data and state. The architecture doesn’t conceal data, it only hides implementations.

The cacheable and uniform state constraints go one step further. Application data is available to clients in a clear and consistent interface and cached when possible.

So, that’s the technical definition of REST. What does it look like in the real world?

RPC Over HTTP vs. RESTful

Often when someone says that a service “isn’t REST,” they’re looking at the URIs or how the service uses HTTP verbs. They’re referring to REST’s presentation of data as a uniform set of resources.

This distinction is sometimes framed as a difference between remote procedures calls (RPC) and REST. Imagine a web service for listing, adding, and removing, items from an e-commerce inventory.

In one version, there’s a single URL that we query with HTTP GETs or POSTs.  You interact with the service by POSTing a document, setting the contents to reflect what you want to do.

Add new items with a POST with a NewItem:

POST /inventory HTTP/1.1
 
{
    "NewItem": {
          "name": "new item",
          "price": "9.99",
          "id": "1001"
      }
}    

Query for items with a POST and an ItemRequest:

POST /inventory HTTP/1.1
 
{
    "ItemRequest": {
          "id": "1001"
      }
}

Some implementations accept a request for a new item with a get, too.

POST /inventory?id=1001 HTTP/1.1

We also change or delete items with a POST and an ItemDelete or ItemUpdate.

POST /inventory HTTP/1.1
 
{
    "ItemDelete": {
          "id": "1001"
      }
}

This isn’t REST. We’re not exchanging the state of resources. We’re calling a function with arguments that happen to be in a JSON document or URL arguments.

A RESTful service has a URI for each item in the inventory.

So, adding a new item would look like the example above.

POST /item HTTP/1.1
 
{
    "Item": {
          "name": "new item",
          "price": "9.99",
          "id": "1001"
      }
}    

But the similarities end there. Retrieving an item is always a GET:

GET /item/1001 HTTP/1.1   

Deleting is a DELETE:

DELETE /item/1001 HTTP/1.1  

Modifying an item is a PUT:

POST /inventory HTTP/1.1
 
{
    "Item": {
          "name": "new item",
          "price": "7.99",
          "id": "1001"
      }
}    

The difference is important. In REST, operations that use distinct HTTP actions. These verbs correspond directly to the activity on the data. GET, POST, PUT, DELETE and PATCH all have specific contracts. Most well-designed REST APIs also return specific HTTP codes, depending on the result of the request.

The critical point is that the URIs operate on the data, not on remote methods.

But there’s another reason why the resource model is essential.

REST vs RESTful and the Richardson Maturity Model

When you model your URIs after resources and use HTTP verbs you make your API predictable. Once developers know how you defined your resources, they can almost predict what the API looks like. Here again, the emphasis is on understanding the data, not the operations.

But even if you can’t make the API entirely predictable, you can document any REST service with hypertext. So, each item returned in the inventory app would contain links for deleting, modifying, or setting the inventory level of the resource. Fielding says that before a service is RESTful, it must provide hypertext media as part of the API.

Many sites don’t meet this requirement but are still called REST. Fact is, many sites break the rules in one way or another. So many that Leonard Richardson created a model breaks down REST into levels of compliance

We’ve already covered the source levels:

  • 0 – exporting an API over HTTP with methods called with arguments
  • 1 – Exporting resources instead of methods
  • 2 – Proper use of HTTP verbs
  • 3 – Exporting hypertext with objects that make all or part of the API discoverable.

Richardson’s model is his own, and it doesn’t map directly into Fielding’s spec. Since Fielding requires level three, he would say that most apps aren’t REST anyway.

The point is many services that we colloquially refer to as REST, technically aren’t.

REST vs RESTful: Does It Matter?

So, does the REST vs. RESTful comparison matter? Probably not. How well your architecture complies with an arbitrary standard isn’t as important with how well it suits your needs and can grow with your business.

The REST architectural pattern has many advantages. Fielding designed it for the web and, 18 years later, most of the constraints he had in mind are still with us. In 2000 we didn’t have Android or the iPhone. IE5 had 50% of the browser market share. It’s biggest rival was Firefox. But Fielding recognized what online applications needed and how web clients would evolve from HTML display engines into complete applications. The tools we use today have grown to suit REST, not the other way around.

Thank you for reading. Hope this tutorial will help you!

What the difference between REST API and RESTful API?

What the difference between REST API and RESTful API?

Representational state transfer (REST) is a style of software architecture. As described in a dissertation by Roy Fielding, REST is an "architectural style" that basically exploits the existing technology and protocols of the Web. RESTful is typically used to refer to web services implementing such an architecture.

The short answer is that REST stands for Representational State Transfer. It’s an architectural pattern for creating web services. A RESTful service is one that implements that pattern.

The long answer starts with “sort of” and “it depends” and continues with more complete definitions.

Defining REST

Let’s start by defining what REST is and is not. For some, REST means a server that exchanges JSON documents with a client over HTTP. Not only is that not a complete definition, but it’s also not always true. The REST specification doesn’t require HTTP or JSON. (The spec doesn’t mention JSON or XML at all.)

The Origins of REST

Roy Fielding introduced the REST architectural pattern in a dissertation he wrote in 2000. The paper defines a means for clients and servers to exchange application data. A key feature is that the client doesn’t need to know anything about the application in advance. The link is to chapter five of his paper. While the entire dissertation describes the hows and whys of REST, that chapter defines the architectural pattern.

Fielding doesn’t mandate specific requirements. Instead, he defines REST regarding constraints and architectural elements.

REST’s Architectural Constraints

Here is a summary of the constraints.

  • Client-server – REST applications have a server that manages application data and state. The server communicates with a client that handles the user interactions. A clear separation of concerns divides the two components. This means you can update and improve them in independent tracks.
  • Stateless – servers don’t maintain any client state. Clients manage their application state. Their requests to servers contain all the information required to process them.
  • Cacheable – servers must mark their responses as cacheable or not. So, infrastructures and clients can cache them when possible to improve performance. They can dispose of non-cacheable Information, so no client uses stale data.
  • Uniform interface – this constraint is REST’s most well known feature or rule, depending on who you ask. Fielding says “The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.” REST services provide data as resources, with a consistent namespace. We’ll cover this in detail below.
  • Layered system – components in the system cannot “see” beyond their layer. So, you can easily add load-balancers and proxies to improve security or performance.

A RESTful service is more than a web server that exchanges JSON, or any other, documents. These constraints work together to create a very specific type of application.

Applying the Constraints

First, the client-server, layered systems and stateless constraints combine to form an application with solid boundaries and clear separations between concerns. Data moves from the server to the client upon request. The client displays or manipulates it. If the state changes, the client sends it back to the server for storage. Fielding specifically contrasts REST with architectures that use distributed objects to hide data from other components. In REST, the client and server share knowledge about data and state. The architecture doesn’t conceal data, it only hides implementations.

The cacheable and uniform state constraints go one step further. Application data is available to clients in a clear and consistent interface and cached when possible.

So, that’s the technical definition of REST. What does it look like in the real world?

RPC Over HTTP vs. RESTful

Often when someone says that a service “isn’t REST,” they’re looking at the URIs or how the service uses HTTP verbs. They’re referring to REST’s presentation of data as a uniform set of resources.

This distinction is sometimes framed as a difference between remote procedures calls (RPC) and REST. Imagine a web service for listing, adding, and removing, items from an e-commerce inventory.

In one version, there’s a single URL that we query with HTTP GETs or POSTs.  You interact with the service by POSTing a document, setting the contents to reflect what you want to do.

Add new items with a POST with a NewItem:

POST /inventory HTTP/1.1
 
{
    "NewItem": {
          "name": "new item",
          "price": "9.99",
          "id": "1001"
      }
}    

Query for items with a POST and an ItemRequest:

POST /inventory HTTP/1.1
 
{
    "ItemRequest": {
          "id": "1001"
      }
}

Some implementations accept a request for a new item with a get, too.

POST /inventory?id=1001 HTTP/1.1

We also change or delete items with a POST and an ItemDelete or ItemUpdate.

POST /inventory HTTP/1.1
 
{
    "ItemDelete": {
          "id": "1001"
      }
}

This isn’t REST. We’re not exchanging the state of resources. We’re calling a function with arguments that happen to be in a JSON document or URL arguments.

A RESTful service has a URI for each item in the inventory.

So, adding a new item would look like the example above.

POST /item HTTP/1.1
 
{
    "Item": {
          "name": "new item",
          "price": "9.99",
          "id": "1001"
      }
}    

But the similarities end there. Retrieving an item is always a GET:

GET /item/1001 HTTP/1.1   

Deleting is a DELETE:

DELETE /item/1001 HTTP/1.1  

Modifying an item is a PUT:

POST /inventory HTTP/1.1
 
{
    "Item": {
          "name": "new item",
          "price": "7.99",
          "id": "1001"
      }
}    

The difference is important. In REST, operations that use distinct HTTP actions. These verbs correspond directly to the activity on the data. GET, POST, PUT, DELETE and PATCH all have specific contracts. Most well-designed REST APIs also return specific HTTP codes, depending on the result of the request.

The critical point is that the URIs operate on the data, not on remote methods.

But there’s another reason why the resource model is essential.

REST vs RESTful and the Richardson Maturity Model

When you model your URIs after resources and use HTTP verbs you make your API predictable. Once developers know how you defined your resources, they can almost predict what the API looks like. Here again, the emphasis is on understanding the data, not the operations.

But even if you can’t make the API entirely predictable, you can document any REST service with hypertext. So, each item returned in the inventory app would contain links for deleting, modifying, or setting the inventory level of the resource. Fielding says that before a service is RESTful, it must provide hypertext media as part of the API.

Many sites don’t meet this requirement but are still called REST. Fact is, many sites break the rules in one way or another. So many that Leonard Richardson created a model breaks down REST into levels of compliance

We’ve already covered the source levels:

  • 0 – exporting an API over HTTP with methods called with arguments
  • 1 – Exporting resources instead of methods
  • 2 – Proper use of HTTP verbs
  • 3 – Exporting hypertext with objects that make all or part of the API discoverable.

Richardson’s model is his own, and it doesn’t map directly into Fielding’s spec. Since Fielding requires level three, he would say that most apps aren’t REST anyway.

The point is many services that we colloquially refer to as REST, technically aren’t.

REST vs RESTful: Does It Matter?

So, does the REST vs. RESTful comparison matter? Probably not. How well your architecture complies with an arbitrary standard isn’t as important with how well it suits your needs and can grow with your business.

The REST architectural pattern has many advantages. Fielding designed it for the web and, 18 years later, most of the constraints he had in mind are still with us. In 2000 we didn’t have Android or the iPhone. IE5 had 50% of the browser market share. It’s biggest rival was Firefox. But Fielding recognized what online applications needed and how web clients would evolve from HTML display engines into complete applications. The tools we use today have grown to suit REST, not the other way around.

Thank you for reading. Hope this tutorial will help you!

What is REST API? – The Complete Guide to RESTful APIs

What is REST API? – The Complete Guide to RESTful APIs

We have been using different applications and web pages to get data for various resources. However, have you ever thought, where does this data come from? . So in this article on What is REST API, let us look into how a client communicates with the servers to extract the required information.

The following topics will be covered in this article "What is REST API":

  • Need of REST API
  • What is REST API?
  • Principles of REST API
  • Methods of REST API
  • How to create a REST API?

Now, before I define REST API for you, let me take you through an example to make you understand the need of REST API.

Need of REST API

Consider a scenario where you are using the Book My Show app. Now, obviously, this application needs a lot of Input data, as the data present in the application is never static. Either it is movies getting released on a frequent basis, or various cities showing different languages movies at various times of the day. It’s never static which implies to the fact that data is always changing in these applications.

Now, where do you think we get this data from?

Well, this data is received from the Server or most commonly known as a Web-server. So, the client requests the server for the required information, via an API, and then, the server sends a response to the client.

Over here, the response sent to the client is in the form of an HTML Web Page. But, do you think this is an apt response that you would expect when you send a request?

Well, I am assuming the fact that you would say NO. Since, you would prefer the data to be returned in the form of structured format, rather than the complete Web page.

So, for such reasons, the data returned by the server, in response to the request of the client is either in the format of JSON or XML. Both JSON and XML format have a proper hierarchical structure of data.

Now, this sounds quite simple, right?

But, the only issue which is present in this framework till now is that you have to use a lot of methods to get the required information. To the fact, using these methods to retrieve information, becomes quite cumbersome when you require complex data.

So, this is where REST API comes into the picture. The REST API creates an object, and thereafter send the values of an object in response to the client.

Now, that you know the need of REST, next in this article, let us look into the What is REST API?

What is REST API?

REST suggests to create an object of the data requested by the client and send the values of the object in response to the user. For example, if the user is requesting for a movie in Bangalore at a certain place and time, then you can create an object on the server side.

So, over here, you have an object and you are sending the state of an object. This is why REST is known as Representational State Transfer.

If I have to define REST, then,  Representational State Transfer a.k.a REST is an architectural style as well as an approach for communications purpose that is often used in various web services development.

The architectural style of REST helps in leveraging the lesser use of bandwidth to make an application more suitable for the internet. It is often regarded as the “language of the internet” and is completely based on the resources.

To understand better, let’s dive a little deeper and see how exactly does a REST API work. Basically, the REST API breaks down a transaction in order to create small modules. Now, each of these modules is used to address a specific part of the transaction. This approach provides more flexibility but requires a lot of effort to be built from the very scratch.

So, now that you know what is REST API, let us next understand the constraints or principles which must be satisfied for an application to be regarded as REST API.

Principles of REST API

Well, there are six ground principles laid down by Dr. Fielding who was the one to define the REST API design in 2000. Below are the six guiding principles of REST:

Stateless

The requests sent from a client to a server will contain all the required information to make the server understand the requests sent from the client. This can be either a part of URL,  query-string parameters, body, or even headers. The URL is used to uniquely identify the resource and the body holds the state of the requesting resource. Once the server processes the request, a response is sent to the client through body, status or headers

Client-Server

The client-server architecture enables a uniform interface and separates clients from the servers. This enhances the portability across multiple platforms as well as the scalability of the server components.

Uniform Interface

To obtain the uniformity throughout the application, REST has the following four interface constraints:

  • Resource identification
  • Resource Manipulation using representations
  • Self-descriptive messages
  • Hypermedia as the engine of application state

Cacheable

In order to provide a better performance, the applications are often made cacheable. This is done by labeling the response from the server as cacheable or non-cacheable either implicitly or explicitly. If the response is defined as cacheable, then the client cache can reuse the response data for equivalent responses in the future.

Layered system

The layered system architecture allows an application to be more stable by limiting component behavior. This type of architecture helps in enhancing the application’s security as components in each layer cannot interact beyond the next immediate layer they are in. Also, it enables load balancing and provides shared caches for promoting scalability.

Code on demand

This is an optional constraint and is used the least. It permits a clients code or applets to be downloaded and to be used within the application. In essence, it simplifies the clients by creating a smart application which doesn’t rely on its own code structure.

Now, that you know the principles behind REST API, next let’s look into the Methods of REST API.

Methods of REST API

All of us working with the technology of the web, do CRUD operations. When I say CRUD operations, I mean that we create a resource, read a resource, update a resource and delete a resource. Now, to do these actions, you can actually use the HTTP methods, which are nothing but the REST API Methods. Refer below.

Now that you know what is a REST API and what all you need to mind in order to deliver an efficient application, let’s dive deeper and see the process of building REST API.

How to create a REST API?

In this practical demonstration, I will be creating a simple CRUD REST application using Node.js. To build this application, you will need to install the following:

  1. Node.js
  2. Express.js
  3. Joi
  4. nodemon (Node Monitor)

For this hands-on, I will be using the WebStorm IDE to write and execute the codes. You can use any IDE or code editor according to your choice. So, let’s get started.

Step 1: Create a project directory, which will consist of all the files present in the project. Then, open commands prompt and navigate to the project directory. Refer below.

Step 2: Now, call npm using the below command. This will initialize the npm modules in your system.

npm init

Once you hit enter, Node.js, will ask you to enter a few details related to the project. These details will basically be the metadata for your project. Refer below.

Here you can define your entry point along with several other information. For this demo, I will be using script.js as an entry point.

It will then, ask you for a confirmation for the data you must have mentioned. Just press on Y to confirm. Refer below.

Step 3: Next, you have yo install Express.js using the below command:

npm i express

Express is a web framework which can be used along with Node.js. This web framework will allow you to create Restful APIs, with the help of helper methods, middle layers to configure your application.

Step 3.1: Similarly, you have to install Joi.

npm i joi

This package allows you to create blueprints for JavaScript objects which store information to ensure validation of key information.

Step 3.2: Finally, install the node monitoring package nodemon, using the below command.

npm i -g nodemon

Nodemon, keeps a watch on all the files with any type of extension present in this folder. Also, with nodemon on the watch, you don’t have to restart the Node.js server each time any changes are made. Nodemon will implicitly detect the changes and restart the server for you.

package.json

{
"name": "restapidemo",
"version": "1.0.0",
"description": "Creation of REST API",
"main": "script.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "sahiti_kappagantula",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"joi": "^14.3.1"
}
}

script.js

const express = require('express'); //Import Express
const Joi = require('joi'); //Import Joi
const app = express(); //Create Express Application on the app variable
app.use(express.json()); //used the json file
 
//Give data to the server
const customers = [
{title: 'George', id: 1},
{title: 'Josh', id: 2},
{title: 'Tyler', id: 3},
{title: 'Alice', id: 4},
{title: 'Candice', id: 5}
]
 
//Read Request Handlers
// Display the Message when the URL consist of '/'
app.get('/', (req, res) => {
res.send('Welcome to Edurekas REST API!');
});
// Display the List Of Customers when URL consists of api customers
app.get('/api/customers', (req,res)=> {
res.send(customers);
});
// Display the Information Of Specific Customer when you mention the id.
app.get('/api/customers/:id', (req, res) => {
const customer = customers.find(c => c.id === parseInt(req.params.id));
//If there is no valid customer ID, then display an error with the following message
if (!customer) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;">Ooops... Cant find what you are looking for!</h2>');
res.send(customer);
});
 
//CREATE Request Handler
//CREATE New Customer Information
app.post('/api/customers', (req, res)=> {
 
const { error } = validateCustomer(req.body);
if (error){
res.status(400).send(error.details[0].message)
return;
}
//Increment the customer id
const customer = {
id: customers.length + 1,
title: req.body.title
};
customers.push(customer);
res.send(customer);
});
 
//Update Request Handler
// Update Existing Customer Information
app.put('/api/customers/:id', (req, res) => {
const customer = customers.find(c=> c.id === parseInt(req.params.id));
if (!customer) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;">Not Found!! </h2>');
 
const { error } = validateCustomer(req.body);
if (error){
res.status(400).send(error.details[0].message);
return;
}
 
customer.title = req.body.title;
res.send(customer);
});
 
//Delete Request Handler
// Delete Customer Details
app.delete('/api/customers/:id', (req, res) => {
 
const customer = customers.find( c=> c.id === parseInt(req.params.id));
if(!customer) res.status(404).send('<h2 style="font-family: Malgun Gothic; color: darkred;"> Not Found!! </h2>');
 
const index = customers.indexOf(customer);
customers.splice(index,1);
 
res.send(customer);
});
//Validate Information
function validateCustomer(customer) {
const schema = {
title: Joi.string().min(3).required()
};
return Joi.validate(customer, schema);
 
}
 
//PORT ENVIRONMENT VARIABLE
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}..`));

Step 4: Now, the next step is to check whether the handlers are working properly or not. For that, we will use a Chrome extension called Postman. To install Postman you can visit here and click on ‘Add to Chrome’.

Step 5: Now, once you have installed Postman, open it to test your application.

Step 6: But before that you have to start your server. To start your server, type the following command.

node script.js

You would see the output as below:

Results

Let us start by testing the GET Method.

Step 7: In order to do that you need to select GET from the drop-down list, type in the defined URL and hit send.

If your code is working fine, then you will see the list of all the customers which we have added manually in our code. In the below picture, you can see how my result looks like. Here I have mentioned the URL to be localhost:8080/api/customers

Step 8: Now, let’s try adding a new customer to our stack of customers. For that, select ‘POST’ from the drop-down list and type in the defined URL for the POST method. Then, click on ‘Body’, select ‘raw’ and move on to select ‘JSON’ from the drop-down list as depicted in the below image. Now, in the text area, type in the name of your customer as shown and hit send.

If your POST method is working fine, your response body will contain the new customer’s name along with the Customer ID. Here if you observe, we have only mentioned the name but we did not give the customer ID. This implies that the Customer ID is automatically incremented

Step 9: Now, let’s try to update a Customer name. Let us say we ant to update the name of the Customer ID = 3. So, to update the data, you need to first select ‘PUT’ from the drop-down table and enter the PUT request’s URL along with the customer id you wish to update. Next in the ‘Body’, type in the new customer name and hit enter.

This will give you a response with the customer id and updated customer name.

Step 10: Finally, let’s send a ‘DELETE’ request to delete an existing record. For that select DELETE from the drop-down list and type in the URL of the delete request handler along with the customer’s details, you want to remove and hit enter. Let’s say, I want to delete the details of a customer with id = 3. If your transaction is successful, you will see the complete details of the entry you have removed in the response body.

Now, let’s send a GET request for our final list of customers.

As you can see from the above screenshot, the response body contains a total of five customers with the customer id 3 missing as we have already deleted that entry.





Test Driven Development of a Django RESTful API

Test Driven Development of a Django RESTful API

This post walks through the process of developing a CRUD-based RESTful API with Django and Django REST Framework, which is used for rapidly building RESTful APIs based on Django models.

This post walks through the process of developing a CRUD-based RESTful API with Django and Django REST Framework, which is used for rapidly building RESTful APIs based on Django models.

This application uses:

  • Python v3.6.0
  • Django v1.11.0
  • Django REST Framework v3.6.2
  • Postgres v9.6.1
  • Psycopg2 v2.7.1
Objectives

By the end of this tutorial you will be able to…

  1. Discuss the benefits of using Django REST Framework for bootstrapping the development of a RESTful API
  2. Validate model querysets using serializers
  3. Appreciate Django REST Framework’s Browsable API feature for a cleaner and well-documented version of your APIs
  4. Practice test-driven development
Why Django REST Framework?

Django REST Framework (REST Framework) provides a number of powerful features out-of-the-box that go well with idiomatic Django, including:

  1. Browsable API: Documents your API with a human-friendly HTML output, providing a beautiful form-like interface for submitting data to resources and fetching from them using the standard HTTP methods.
  2. Auth Support: REST Framework has rich support for various authentication protocols along with permissions and throttling policies which can be configured on a per-view basis.
  3. Serializers: Serializers are an elegant way of validating model querysets/instances and converting them to native Python datatypes that can be easily rendered into JSON and XML.
  4. Throttling: Throttling is way to determine whether a request is authorized or not and can be integrated with different permissions. It is generally used for rate limiting API requests from a single user.

Plus, the documentation is easy to read and full of examples. If you’re building a RESTful API where you have a one-to-one relationship between your API endpoints and your models, then REST Framework is the way to go.

Django Project Setup

Create and activate a virtualenv:

$ mkdir django-puppy-store
$ cd django-puppy-store
$ python3.6 -m venv env
$ source env/bin/activate

Install Django and set up a new project:

(env)$ pip install django==1.11.0
(env)$ django-admin startproject puppy_store

Your current project structure should look like this:

└── puppy_store
    ├── manage.py
    └── puppy_store
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

Django App and REST Framework Setup

Start by creating the puppies app and installing REST Framework inside your virtualenv:

(env)$ cd puppy_store
(env)$ python manage.py startapp puppies
(env)$ pip install djangorestframework==3.6.2

Now we need to configure our Django project to make use of REST Framework.

First, add the puppies app and rest_framework to the INSTALLED_APPS section within puppy_store/puppy_store/settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'puppies',
    'rest_framework'
]

Next, define global settings for REST Framework in a single dictionary, again, in the settings.py file:

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [],
    'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

This allows unrestricted access to the API and sets the default test format to JSON for all requests.

NOTE: Unrestricted access is fine for local development, but in a production environment you may need to restrict access to certain endpoints. Make sure to update this. Review the docs for more information.
Your current project structure should now look like:

└── puppy_store
    ├── manage.py
    ├── puppies
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    └── puppy_store
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

Database and Model Setup

Let’s set up the Postgres database and apply all the migrations to it.

NOTE: Feel free to swap out Postgres for the relational database of your choice!
Once you have a working Postgres server on your system, open the Postgres interactive shell and create the database:

$ psql
# CREATE DATABASE puppy_store_drf;
CREATE DATABASE
# \q

Install psycopg2 so that we can interact with the Postgres server via Python:

(env)$ pip install psycopg2==2.7.1

Update the database configuration in settings.py, adding the appropriate username and password:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'puppy_store_drf',
        'USER': '<your-user>',
        'PASSWORD': '<your-password>',
        'HOST': '127.0.0.1',
        'PORT': '5432'
    }
}

Next, define a puppy model with some basic attributes in django-puppy-store/puppy_store/puppies/models.py:

from django.db import models


class Puppy(models.Model):
    """
    Puppy Model
    Defines the attributes of a puppy
    """
    name = models.CharField(max_length=255)
    age = models.IntegerField()
    breed = models.CharField(max_length=255)
    color = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def get_breed(self):
        return self.name + ' belongs to ' + self.breed + ' breed.'

    def __repr__(self):
        return self.name + ' is added.'

Now apply the migration:

(env)$ python manage.py makemigrations
(env)$ python manage.py migrate

Sanity Check

Hop into psql again and verify that the puppies_puppy has been created:

$ psql
# \c puppy_store_drf
You are now connected to database "puppy_store_drf".
puppy_store_drf=# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | michael.herman
 public | auth_group_permissions     | table | michael.herman
 public | auth_permission            | table | michael.herman
 public | auth_user                  | table | michael.herman
 public | auth_user_groups           | table | michael.herman
 public | auth_user_user_permissions | table | michael.herman
 public | django_admin_log           | table | michael.herman
 public | django_content_type        | table | michael.herman
 public | django_migrations          | table | michael.herman
 public | django_session             | table | michael.herman
 public | puppies_puppy              | table | michael.herman
(11 rows)

NOTE: You can run \d+ puppies_puppy if you want to look at the actual table details.
Before moving on, let’s write a quick unit test for the Puppy model.

Add the following code to a new file called test_models.py in a new folder called “tests” within “django-puppy-store/puppy_store/puppies”:

from django.test import TestCase
from ..models import Puppy


class PuppyTest(TestCase):
    """ Test module for Puppy model """

    def setUp(self):
        Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')

    def test_puppy_breed(self):
        puppy_casper = Puppy.objects.get(name='Casper')
        puppy_muffin = Puppy.objects.get(name='Muffin')
        self.assertEqual(
            puppy_casper.get_breed(), "Casper belongs to Bull Dog breed.")
        self.assertEqual(
            puppy_muffin.get_breed(), "Muffin belongs to Gradane breed.")

In the above test, we added dummy entries into our puppy table via the setUp() method from django.test.TestCase and asserted that the get_breed() method returned the correct string.

Add an init.py file to “tests” and remove the tests.py file from “django-puppy-store/puppy_store/puppies”.

Let’s run our first test:

(env)$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.007s

OK
Destroying test database for alias 'default'...

Great! Our first unit test has passed!

Serializers

Before moving on to creating the actual API, let’s define a serializer for our Puppy model which validates the model querysets and produces Pythonic data types to work with.

Add the following snippet to django-puppy-store/puppy_store/puppies/serializers.py:

from rest_framework import serializers
from .models import Puppy


class PuppySerializer(serializers.ModelSerializer):
    class Meta:
        model = Puppy
        fields = ('name', 'age', 'breed', 'color', 'created_at', 'updated_at')

In the above snippet we defined a ModelSerializer for our puppy model, validating all the mentioned fields. In short, if you have a one-to-one relationship between your API endpoints and your models - which you probably should if you’re creating a RESTful API - then you can use a ModelSerializer to create a Serializer.

With our database in place, we can now start building the RESTful API…

RESTful Structure

In a RESTful API, endpoints (URLs) define the structure of the API and how end users access data from our application using the HTTP methods - GET, POST, PUT, DELETE. Endpoints should be logically organized around collections and elements, both of which are resources.

In our case, we have one single resource, puppies, so we will use the following URLS - /puppies/ and /puppies/<id> for collections and elements, respectively:

Routes and Testing (TDD)

We will be taking a test-first approach rather than a thorough test-driven approach, wherein we will be going through the following process:

  • add a unit test, just enough code to fail
  • then update the code to make it pass the test.

Once the test passes, start over with the same process for the new test.

Begin by creating a new file, django-puppy-store/puppy_store/puppies/tests/test_views.py, to hold all the tests for our views and create a new test client for our app:

import json
from rest_framework import status
from django.test import TestCase, Client
from django.urls import reverse
from ..models import Puppy
from ..serializers import PuppySerializer


# initialize the APIClient app
client = Client()

Before starting with all the API routes, let’s first create a skeleton of all view functions that return empty responses and map them with their appropriate URLs within the django-puppy-store/puppy_store/puppies/views.py file:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Puppy
from .serializers import PuppySerializer


@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        return Response({})
    # delete a single puppy
    elif request.method == 'DELETE':
        return Response({})
    # update details of a single puppy
    elif request.method == 'PUT':
        return Response({})


@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        return Response({})
    # insert a new record for a puppy
    elif request.method == 'POST':
        return Response({})

Create the respective URLs to match the views in django-puppy-store/puppy_store/puppies/urls.py:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(
        r'^api/v1/puppies/(?P<pk>[0-9]+)
Update *django-puppy-store/puppy_store/puppy_store/urls.py* as well:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^', include('puppies.urls')),
url(
r'^api-auth/',
include('rest_framework.urls', namespace='rest_framework')
),
url(r'^admin/', admin.site.urls),
]

## Browsable API

With all routes now wired up with the view functions, let’s open up REST Framework’s Browsable API interface and verify whether all the URLs are working as expected.

First, fire up the development server:

(env)$ python manage.py runserver


Make sure to comment out all the attributes in ```REST_FRAMEWORK``` section of our ```settings.py``` file, to bypass login. Now visit ```<a href="http://localhost:8000/api/v1/puppies" target="_blank">http://localhost:8000/api/v1/puppies</a>```

You will see an interactive HTML layout for the API response. Similarly we can test the other URLs and verify all URLs are working perfectly fine.

Let’s start with our unit tests for each route.



## Routes
### GET ALL

Start with a test to verify the fetched records:

class GetAllPuppiesTest(TestCase):
""" Test module for GET all puppies API """

def setUp(self):
    Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    Puppy.objects.create(
        name='Muffin', age=1, breed='Gradane', color='Brown')
    Puppy.objects.create(
        name='Rambo', age=2, breed='Labrador', color='Black')
    Puppy.objects.create(
        name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_all_puppies(self):
    # get API response
    response = client.get(reverse('get_post_puppies'))
    # get data from db
    puppies = Puppy.objects.all()
    serializer = PuppySerializer(puppies, many=True)
    self.assertEqual(response.data, serializer.data)
    self.assertEqual(response.status_code, status.HTTP_200_OK)

Run the test. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != [OrderedDict([('name', 'Casper'), ('age',[687 chars])])]


Update the view to get the test to pass.

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
elif request.method == 'POST':
return Response({})


Here, we get all the records for puppies and validate each using the ```PuppySerializer```.

Run the tests to ensure they all pass:

Ran 2 tests in 0.072s

OK

### GET Single

Fetching a single puppy involves two test cases:
1. Get valid puppy - e.g., the puppy exists
1. Get invalid puppy - e.g., the puppy does not exists

Add the tests:

class GetSinglePuppyTest(TestCase):
""" Test module for GET single puppy API """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffin', age=1, breed='Gradane', color='Brown')
    self.rambo = Puppy.objects.create(
        name='Rambo', age=2, breed='Labrador', color='Black')
    self.ricky = Puppy.objects.create(
        name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_valid_single_puppy(self):
    response = client.get(
        reverse('get_delete_update_puppy', kwargs={'pk': self.rambo.pk}))
    puppy = Puppy.objects.get(pk=self.rambo.pk)
    serializer = PuppySerializer(puppy)
    self.assertEqual(response.data, serializer.data)
    self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_get_invalid_single_puppy(self):
    response = client.get(
        reverse('get_delete_update_puppy', kwargs={'pk': 30}))
    self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != {'name': 'Rambo', 'age': 2, 'breed': 'Labr[109 chars]26Z'}


Update the view:

@api_view(['GET', 'UPDATE', 'DELETE'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

In the above snippet, we get the puppy using an ID. Run the tests to ensure they all pass.



### POST

Inserting a new record involves two cases as well:
1. Inserting a valid puppy
1. Inserting a invalid puppy

First, write tests for it:

class CreateNewPuppyTest(TestCase):
""" Test module for inserting a new puppy """

def setUp(self):
    self.valid_payload = {
        'name': 'Muffin',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }
    self.invalid_payload = {
        'name': '',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }

def test_create_valid_puppy(self):
    response = client.post(
        reverse('get_post_puppies'),
        data=json.dumps(self.valid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_invalid_puppy(self):
    response = client.post(
        reverse('get_post_puppies'),
        data=json.dumps(self.invalid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests. You should see two failures:

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 200 != 400

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201


Again, update the view to get the tests to pass:

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
if request.method == 'POST':
data = {
'name': request.data.get('name'),
'age': int(request.data.get('age')),
'breed': request.data.get('breed'),
'color': request.data.get('color')
}
serializer = PuppySerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


Here, we inserted a new record by serializing and validating the request data before inserting to the database.

Run the tests again to ensure they pass.

You can also test this out with the Browsable API. Fire up the development server again, and navigate to [http://localhost:8000/api/v1/puppies/](http://localhost:8000/api/v1/puppies/ "http://localhost:8000/api/v1/puppies/"). Then, within the POST form, submit the following as ```application/json```:

{
"name": "Muffin",
"age": 4,
"breed": "Pamerion",
"color": "White"
}


Be sure the GET ALL and Get Single work as well.



### PUT

Start with a test to update a record. Similar to adding a record, we again need to test for both valid and invalid updates:

class UpdateSinglePuppyTest(TestCase):
""" Test module for updating an existing puppy record """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffy', age=1, breed='Gradane', color='Brown')
    self.valid_payload = {
        'name': 'Muffy',
        'age': 2,
        'breed': 'Labrador',
        'color': 'Black'
    }
    self.invalid_payload = {
        'name': '',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }

def test_valid_update_puppy(self):
    response = client.put(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
        data=json.dumps(self.valid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_update_puppy(self):
    response = client.put(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
        data=json.dumps(self.invalid_payload),
        content_type='application/json')
    self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests.

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 405 != 400

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 405 != 204


Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
    serializer = PuppySerializer(puppy, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
elif request.method == 'DELETE':
    return Response({})

In the above snippet, similar to an insert, we serialize and validate the request data and then respond appropriately.

Run the tests again to ensure that all the tests pass.



### DELETE

To delete a single record, an ID is required:

class DeleteSinglePuppyTest(TestCase):
""" Test module for deleting an existing puppy record """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffy', age=1, breed='Gradane', color='Brown')

def test_valid_delete_puppy(self):
    response = client.delete(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}))
    self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_delete_puppy(self):
    response = client.delete(
        reverse('get_delete_update_puppy', kwargs={'pk': 30}))
    self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see:

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 200 != 204


Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
    serializer = PuppySerializer(puppy, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
if request.method == 'DELETE':
    puppy.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)

Run the tests again. Make sure all of them pass. Make sure to test out the UPDATE and DELETE functionality within the Browsable API as well!

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on [**Facebook**](https://www.facebook.com/moriohdotcom "**Facebook**") | [**Twitter**](https://twitter.com/moriohdotcom "**Twitter**")
### Learn More

☞ [Python and Django Full Stack Web Developer Bootcamp](http://learnstartup.net/p/r1-quFMgce "Python and Django Full Stack Web Developer Bootcamp")

☞ [Build a Backend REST API with Python & Django - Advanced](http://learnstartup.net/p/VyGrb6hcg "Build a Backend REST API with Python & Django - Advanced")

☞ [Python Django Dev To Deployment](http://learnstartup.net/p/9yr7V_-O8 "Python Django Dev To Deployment")

☞ [Django Core | A Reference Guide to Core Django Concepts](http://learnstartup.net/p/S1eqVBxZg "Django Core | A Reference Guide to Core Django Concepts")

☞ [Build Your First Python and Django Application ](http://dev.edupioneer.net/1935fcab7e%22%22 "Build Your First Python and Django Application ")

☞ [Flask Or Django? An In-Depth Comparison](http://on.morioh.net/108ddbf9b5 "Flask Or Django? An In-Depth Comparison")

☞ [How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 16.04](http://on.geeklearn.net/73b1fadaaf "How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 16.04")

☞ [Building a Universal Application with Nuxt.js and Django](http://on.geeklearn.net/0bda45c395 "Building a Universal Application with Nuxt.js and Django")

☞ [Django Tutorial: Building and Securing Web Applications](http://on.morioh.net/be6171a7c3 "Django Tutorial: Building and Securing Web Applications")

*Originally published on *[*https://realpython.com*](https://realpython.com "*https://realpython.com*")
,
        views.get_delete_update_puppy,
        name='get_delete_update_puppy'
    ),
    url(
        r'^api/v1/puppies/
Update *django-puppy-store/puppy_store/puppy_store/urls.py* as well:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^', include('puppies.urls')),
url(
r'^api-auth/',
include('rest_framework.urls', namespace='rest_framework')
),
url(r'^admin/', admin.site.urls),
]

## Browsable API

With all routes now wired up with the view functions, let’s open up REST Framework’s Browsable API interface and verify whether all the URLs are working as expected.

First, fire up the development server:

(env)$ python manage.py runserver


Make sure to comment out all the attributes in ```REST_FRAMEWORK``` section of our ```settings.py``` file, to bypass login. Now visit ```<a href="http://localhost:8000/api/v1/puppies" target="_blank">http://localhost:8000/api/v1/puppies</a>```

You will see an interactive HTML layout for the API response. Similarly we can test the other URLs and verify all URLs are working perfectly fine.

Let’s start with our unit tests for each route.



## Routes
### GET ALL

Start with a test to verify the fetched records:

class GetAllPuppiesTest(TestCase):
""" Test module for GET all puppies API """

def setUp(self):
    Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    Puppy.objects.create(
        name='Muffin', age=1, breed='Gradane', color='Brown')
    Puppy.objects.create(
        name='Rambo', age=2, breed='Labrador', color='Black')
    Puppy.objects.create(
        name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_all_puppies(self):
    # get API response
    response = client.get(reverse('get_post_puppies'))
    # get data from db
    puppies = Puppy.objects.all()
    serializer = PuppySerializer(puppies, many=True)
    self.assertEqual(response.data, serializer.data)
    self.assertEqual(response.status_code, status.HTTP_200_OK)

Run the test. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != [OrderedDict([('name', 'Casper'), ('age',[687 chars])])]


Update the view to get the test to pass.

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
elif request.method == 'POST':
return Response({})


Here, we get all the records for puppies and validate each using the ```PuppySerializer```.

Run the tests to ensure they all pass:

Ran 2 tests in 0.072s

OK

### GET Single

Fetching a single puppy involves two test cases:
1. Get valid puppy - e.g., the puppy exists
1. Get invalid puppy - e.g., the puppy does not exists

Add the tests:

class GetSinglePuppyTest(TestCase):
""" Test module for GET single puppy API """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffin', age=1, breed='Gradane', color='Brown')
    self.rambo = Puppy.objects.create(
        name='Rambo', age=2, breed='Labrador', color='Black')
    self.ricky = Puppy.objects.create(
        name='Ricky', age=6, breed='Labrador', color='Brown')

def test_get_valid_single_puppy(self):
    response = client.get(
        reverse('get_delete_update_puppy', kwargs={'pk': self.rambo.pk}))
    puppy = Puppy.objects.get(pk=self.rambo.pk)
    serializer = PuppySerializer(puppy)
    self.assertEqual(response.data, serializer.data)
    self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_get_invalid_single_puppy(self):
    response = client.get(
        reverse('get_delete_update_puppy', kwargs={'pk': 30}))
    self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != {'name': 'Rambo', 'age': 2, 'breed': 'Labr[109 chars]26Z'}


Update the view:

@api_view(['GET', 'UPDATE', 'DELETE'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

In the above snippet, we get the puppy using an ID. Run the tests to ensure they all pass.



### POST

Inserting a new record involves two cases as well:
1. Inserting a valid puppy
1. Inserting a invalid puppy

First, write tests for it:

class CreateNewPuppyTest(TestCase):
""" Test module for inserting a new puppy """

def setUp(self):
    self.valid_payload = {
        'name': 'Muffin',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }
    self.invalid_payload = {
        'name': '',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }

def test_create_valid_puppy(self):
    response = client.post(
        reverse('get_post_puppies'),
        data=json.dumps(self.valid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_invalid_puppy(self):
    response = client.post(
        reverse('get_post_puppies'),
        data=json.dumps(self.invalid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests. You should see two failures:

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 200 != 400

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201


Again, update the view to get the tests to pass:

@api_view(['GET', 'POST'])
def get_post_puppies(request):
# get all puppies
if request.method == 'GET':
puppies = Puppy.objects.all()
serializer = PuppySerializer(puppies, many=True)
return Response(serializer.data)
# insert a new record for a puppy
if request.method == 'POST':
data = {
'name': request.data.get('name'),
'age': int(request.data.get('age')),
'breed': request.data.get('breed'),
'color': request.data.get('color')
}
serializer = PuppySerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


Here, we inserted a new record by serializing and validating the request data before inserting to the database.

Run the tests again to ensure they pass.

You can also test this out with the Browsable API. Fire up the development server again, and navigate to [http://localhost:8000/api/v1/puppies/](http://localhost:8000/api/v1/puppies/ "http://localhost:8000/api/v1/puppies/"). Then, within the POST form, submit the following as ```application/json```:

{
"name": "Muffin",
"age": 4,
"breed": "Pamerion",
"color": "White"
}


Be sure the GET ALL and Get Single work as well.



### PUT

Start with a test to update a record. Similar to adding a record, we again need to test for both valid and invalid updates:

class UpdateSinglePuppyTest(TestCase):
""" Test module for updating an existing puppy record """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffy', age=1, breed='Gradane', color='Brown')
    self.valid_payload = {
        'name': 'Muffy',
        'age': 2,
        'breed': 'Labrador',
        'color': 'Black'
    }
    self.invalid_payload = {
        'name': '',
        'age': 4,
        'breed': 'Pamerion',
        'color': 'White'
    }

def test_valid_update_puppy(self):
    response = client.put(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
        data=json.dumps(self.valid_payload),
        content_type='application/json'
    )
    self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_update_puppy(self):
    response = client.put(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
        data=json.dumps(self.invalid_payload),
        content_type='application/json')
    self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests.

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 405 != 400

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 405 != 204


Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
    serializer = PuppySerializer(puppy, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
elif request.method == 'DELETE':
    return Response({})

In the above snippet, similar to an insert, we serialize and validate the request data and then respond appropriately.

Run the tests again to ensure that all the tests pass.



### DELETE

To delete a single record, an ID is required:

class DeleteSinglePuppyTest(TestCase):
""" Test module for deleting an existing puppy record """

def setUp(self):
    self.casper = Puppy.objects.create(
        name='Casper', age=3, breed='Bull Dog', color='Black')
    self.muffin = Puppy.objects.create(
        name='Muffy', age=1, breed='Gradane', color='Brown')

def test_valid_delete_puppy(self):
    response = client.delete(
        reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}))
    self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_invalid_delete_puppy(self):
    response = client.delete(
        reverse('get_delete_update_puppy', kwargs={'pk': 30}))
    self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see:

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 200 != 204


Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
try:
puppy = Puppy.objects.get(pk=pk)
except Puppy.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

# get details of a single puppy
if request.method == 'GET':
    serializer = PuppySerializer(puppy)
    return Response(serializer.data)

# update details of a single puppy
if request.method == 'PUT':
    serializer = PuppySerializer(puppy, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# delete a single puppy
if request.method == 'DELETE':
    puppy.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)

Run the tests again. Make sure all of them pass. Make sure to test out the UPDATE and DELETE functionality within the Browsable API as well!

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on [**Facebook**](https://www.facebook.com/moriohdotcom "**Facebook**") | [**Twitter**](https://twitter.com/moriohdotcom "**Twitter**")
### Learn More

☞ [Python and Django Full Stack Web Developer Bootcamp](http://learnstartup.net/p/r1-quFMgce "Python and Django Full Stack Web Developer Bootcamp")

☞ [Build a Backend REST API with Python & Django - Advanced](http://learnstartup.net/p/VyGrb6hcg "Build a Backend REST API with Python & Django - Advanced")

☞ [Python Django Dev To Deployment](http://learnstartup.net/p/9yr7V_-O8 "Python Django Dev To Deployment")

☞ [Django Core | A Reference Guide to Core Django Concepts](http://learnstartup.net/p/S1eqVBxZg "Django Core | A Reference Guide to Core Django Concepts")

☞ [Build Your First Python and Django Application ](http://dev.edupioneer.net/1935fcab7e%22%22 "Build Your First Python and Django Application ")

☞ [Flask Or Django? An In-Depth Comparison](http://on.morioh.net/108ddbf9b5 "Flask Or Django? An In-Depth Comparison")

☞ [How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 16.04](http://on.geeklearn.net/73b1fadaaf "How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 16.04")

☞ [Building a Universal Application with Nuxt.js and Django](http://on.geeklearn.net/0bda45c395 "Building a Universal Application with Nuxt.js and Django")

☞ [Django Tutorial: Building and Securing Web Applications](http://on.morioh.net/be6171a7c3 "Django Tutorial: Building and Securing Web Applications")

*Originally published on *[*https://realpython.com*](https://realpython.com "*https://realpython.com*")
,
        views.get_post_puppies,
        name='get_post_puppies'
    )
]

Update django-puppy-store/puppy_store/puppy_store/urls.py as well:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^', include('puppies.urls')),
    url(
        r'^api-auth/',
        include('rest_framework.urls', namespace='rest_framework')
    ),
    url(r'^admin/', admin.site.urls),
]

Browsable API

With all routes now wired up with the view functions, let’s open up REST Framework’s Browsable API interface and verify whether all the URLs are working as expected.

First, fire up the development server:

(env)$ python manage.py runserver

Make sure to comment out all the attributes in REST_FRAMEWORK section of our settings.py file, to bypass login. Now visit <a href="http://localhost:8000/api/v1/puppies" target="_blank">http://localhost:8000/api/v1/puppies</a>

You will see an interactive HTML layout for the API response. Similarly we can test the other URLs and verify all URLs are working perfectly fine.

Let’s start with our unit tests for each route.

Routes

GET ALL

Start with a test to verify the fetched records:

class GetAllPuppiesTest(TestCase):
    """ Test module for GET all puppies API """

    def setUp(self):
        Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')
        Puppy.objects.create(
            name='Rambo', age=2, breed='Labrador', color='Black')
        Puppy.objects.create(
            name='Ricky', age=6, breed='Labrador', color='Brown')

    def test_get_all_puppies(self):
        # get API response
        response = client.get(reverse('get_post_puppies'))
        # get data from db
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Run the test. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != [OrderedDict([('name', 'Casper'), ('age',[687 chars])])]

Update the view to get the test to pass.

@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        return Response(serializer.data)
    # insert a new record for a puppy
    elif request.method == 'POST':
        return Response({})

Here, we get all the records for puppies and validate each using the PuppySerializer.

Run the tests to ensure they all pass:

Ran 2 tests in 0.072s

OK

GET Single

Fetching a single puppy involves two test cases:

  1. Get valid puppy - e.g., the puppy exists
  2. Get invalid puppy - e.g., the puppy does not exists

Add the tests:

class GetSinglePuppyTest(TestCase):
    """ Test module for GET single puppy API """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')
        self.rambo = Puppy.objects.create(
            name='Rambo', age=2, breed='Labrador', color='Black')
        self.ricky = Puppy.objects.create(
            name='Ricky', age=6, breed='Labrador', color='Brown')

    def test_get_valid_single_puppy(self):
        response = client.get(
            reverse('get_delete_update_puppy', kwargs={'pk': self.rambo.pk}))
        puppy = Puppy.objects.get(pk=self.rambo.pk)
        serializer = PuppySerializer(puppy)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_get_invalid_single_puppy(self):
        response = client.get(
            reverse('get_delete_update_puppy', kwargs={'pk': 30}))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see the following error:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != {'name': 'Rambo', 'age': 2, 'breed': 'Labr[109 chars]26Z'}

Update the view:

@api_view(['GET', 'UPDATE', 'DELETE'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

In the above snippet, we get the puppy using an ID. Run the tests to ensure they all pass.

POST

Inserting a new record involves two cases as well:

  1. Inserting a valid puppy
  2. Inserting a invalid puppy

First, write tests for it:

class CreateNewPuppyTest(TestCase):
    """ Test module for inserting a new puppy """

    def setUp(self):
        self.valid_payload = {
            'name': 'Muffin',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }
        self.invalid_payload = {
            'name': '',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }

    def test_create_valid_puppy(self):
        response = client.post(
            reverse('get_post_puppies'),
            data=json.dumps(self.valid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_create_invalid_puppy(self):
        response = client.post(
            reverse('get_post_puppies'),
            data=json.dumps(self.invalid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests. You should see two failures:

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 200 != 400

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201

Again, update the view to get the tests to pass:

@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        return Response(serializer.data)
    # insert a new record for a puppy
    if request.method == 'POST':
        data = {
            'name': request.data.get('name'),
            'age': int(request.data.get('age')),
            'breed': request.data.get('breed'),
            'color': request.data.get('color')
        }
        serializer = PuppySerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Here, we inserted a new record by serializing and validating the request data before inserting to the database.

Run the tests again to ensure they pass.

You can also test this out with the Browsable API. Fire up the development server again, and navigate to http://localhost:8000/api/v1/puppies/. Then, within the POST form, submit the following as application/json:

{
    "name": "Muffin",
    "age": 4,
    "breed": "Pamerion",
    "color": "White"
}

Be sure the GET ALL and Get Single work as well.

PUT

Start with a test to update a record. Similar to adding a record, we again need to test for both valid and invalid updates:

class UpdateSinglePuppyTest(TestCase):
    """ Test module for updating an existing puppy record """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffy', age=1, breed='Gradane', color='Brown')
        self.valid_payload = {
            'name': 'Muffy',
            'age': 2,
            'breed': 'Labrador',
            'color': 'Black'
        }
        self.invalid_payload = {
            'name': '',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }

    def test_valid_update_puppy(self):
        response = client.put(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
            data=json.dumps(self.valid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

    def test_invalid_update_puppy(self):
        response = client.put(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
            data=json.dumps(self.invalid_payload),
            content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Run the tests.

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 405 != 400

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 405 != 204

Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

    # update details of a single puppy
    if request.method == 'PUT':
        serializer = PuppySerializer(puppy, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # delete a single puppy
    elif request.method == 'DELETE':
        return Response({})

In the above snippet, similar to an insert, we serialize and validate the request data and then respond appropriately.

Run the tests again to ensure that all the tests pass.

DELETE

To delete a single record, an ID is required:

class DeleteSinglePuppyTest(TestCase):
    """ Test module for deleting an existing puppy record """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffy', age=1, breed='Gradane', color='Brown')

    def test_valid_delete_puppy(self):
        response = client.delete(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

    def test_invalid_delete_puppy(self):
        response = client.delete(
            reverse('get_delete_update_puppy', kwargs={'pk': 30}))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Run the tests. You should see:

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 200 != 204

Update the view:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

    # update details of a single puppy
    if request.method == 'PUT':
        serializer = PuppySerializer(puppy, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # delete a single puppy
    if request.method == 'DELETE':
        puppy.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Run the tests again. Make sure all of them pass. Make sure to test out the UPDATE and DELETE functionality within the Browsable API as well!