# Simplify API Testing with PhpStorm HTTP Requests

API testing is a critical aspect of modern software development, ensuring that the services we build and integrate work seamlessly. While developers often rely on dedicated tools like Postman and Insomnia, what many aren't aware of is that JetBrains' IDEs, including PhpStorm, offer powerful built-in features for handling HTTP requests.

In this guide, I'll explore how PhpStorm's HTTP Client streamlines API testing and improves my development workflow right within the IDE. I'll delve into organizing requests, utilizing variables, and even importing and exporting requests. By the end, you'll have a solid grasp of how to leverage PhpStorm's capabilities for effective API testing.

## Getting Started

To keep things organized, I prefer to create a folder in my project called ".http". This allows me to store all my HTTP requests in a single place. To ensure that these requests don't get accidentally committed, I will add the ".http" folder to my global `.gitignore` file. Read my article about [using a global gitignore](https://seankegel.com/git-quick-tip-using-a-global-gitignore) for more information. However, if I ever need to share these requests with my team, I can simply store them in a folder that is committed to the repository.

For this article, I will use the [JSONPlaceholder - Free Fake REST API](https://jsonplaceholder.typicode.com/) website, which provides various API endpoints that can be used for testing purposes.

Once I have the ".http" folder in my project, I can easily create a new request file called "Posts.http". This allows me to organize all my Post-related HTTP requests in one place.

![Create an HTTP Request](https://cdn.hashnode.com/res/hashnode/image/upload/v1697308075005/7ec1051c-566e-484d-9632-4313542321aa.png align="center")

## Posts Requests

### Get Posts List

In my "Posts.http" file, I create a request to list all posts. I can either type this request manually or use the UI to add it. With the UI, I will click on the plus `+` icon and select the request type.

![Create a GET Request](https://cdn.hashnode.com/res/hashnode/image/upload/v1697312556501/afbac4d8-ebcb-4188-b1a6-f716982a3f04.png align="center")

For listing posts, I will create a `GET` request. This will add the following content to the file:

```http
GET http://localhost:80/api/item?id=99
Accept: application/json

###
```

I will update it to use the [JSONPlaceholder - Free Fake REST API](https://jsonplaceholder.typicode.com/). I will remove the `Accept` header for now.

```http
GET https://jsonplaceholder.typicode.com/posts

###
```

Once the request is ready, it's time to run it. I click the "Play" button, and the results appear in the console below.

![Run a Request](https://cdn.hashnode.com/res/hashnode/image/upload/v1697308783644/70a0378e-900e-412e-8e00-c4637192c667.png align="center")

### Get Post

To get a specific post, I can add a new request to the file. I can even add comments to describe the requests, either using `#` or `//`.

```http
# List Posts
GET https://jsonplaceholder.typicode.com/posts

###

# Get Post
GET https://jsonplaceholder.typicode.com/posts/1
```

Also, I make sure to separate the requests with `###` which is required by the `.http` syntax.

### Create Post

To create a new post, I’ll add a `POST` request and a JSON body.

```http
# List Posts
GET https://jsonplaceholder.typicode.com/posts

###

# Get Post
GET https://jsonplaceholder.typicode.com/posts/1

###

# Create Post
POST https://jsonplaceholder.typicode.com/posts
Content-Type: application/json

{
  "title": "Post title",
  "body": "Post body",
  "userId": 1
}
```

It can also be helpful to add the `Content-Type` header to specify that JSON is being passed to the server.

### Update Post

Updating a post is similar to creating a post, but I’ll use a `PUT` request.

```http
# List Posts
GET https://jsonplaceholder.typicode.com/posts

###

# Get Post
GET https://jsonplaceholder.typicode.com/posts/1

###

# Create Post
POST https://jsonplaceholder.typicode.com/posts
Content-Type: application/json

{
  "title": "Post title",
  "body": "Post body",
  "userId": 1
}

###

# Update Post
PUT https://jsonplaceholder.typicode.com/posts/1
Content-Type: application/json

{
  "title": "New title"
}
```

### Delete Post

Finally, I can delete a post using a `DELETE` request.

```http
# List Posts
GET https://jsonplaceholder.typicode.com/posts

###

# Get Post
GET https://jsonplaceholder.typicode.com/posts/1

###

# Create Post
POST https://jsonplaceholder.typicode.com/posts
Content-Type: application/json

{
  "title": "Post title",
  "body": "Post body",
  "userId": 1
}

###

# Update Post
PUT https://jsonplaceholder.typicode.com/posts/1
Content-Type: application/json

{
    "title": "New title"
}

###

# Delete Post
DELETE https://jsonplaceholder.typicode.com/posts/1
```

## Variables

Variables are a powerful tool when it comes to managing complex API testing scenarios. PhpStorm offers various types of variables, starting with Environment Variables.

### Environment Variables

Environment variables can be used to store information such as usernames, passwords, authentication tokens, and more. They can also be used to support multiple environments. PhpStorm provides both public and private variables, which are merged before a request is made. To begin, create a public environment file.

![Create a Public Environment File](https://cdn.hashnode.com/res/hashnode/image/upload/v1697308944939/5fcea05e-d021-4a08-bbc7-9e723f08c237.png align="center")

This creates the file below:

![Public Environment File](https://cdn.hashnode.com/res/hashnode/image/upload/v1697308960142/ee40791f-f1ca-45b4-9f3d-f6d1123eebbe.png align="center")

I will update the file to store a dev and production environment with a host and authentication token.

```json
{
    "dev": {
        "host": "<https://jsonplaceholder.typicode.com>",
        "token": ""
    },
    "prod": {
        "host": "<https://jsonplaceholder.typicode.com>",
        "token": ""
    }
}
```

I will leave the token empty for now and add it to the private environment file. I’ll create that now.

![Create a Private Environment File](https://cdn.hashnode.com/res/hashnode/image/upload/v1697308980325/1b67701a-19a8-4422-bea7-a9c48290e32f.png align="center")

In this file, I will add the authentication tokens. It can also store usernames and passwords depending on the server authentication.

```json
{
    "dev": {
        "token": "DEVELOPMENT_TOKEN"
    },
    "prod": {
        "token": "PRODUCTION_TOKEN"
    }
}
```

Now, I can add these variables to the requests.

```http
# List Posts
GET {{host}}/posts
Authorization: Bearer {{token}}

###

# Get Post
GET {{host}}/posts/1
Authorization: Bearer {{token}}

###

# Create Post
POST {{host}}/posts
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "title": "Post title",
  "body": "Post body",
  "userId": 1
}

###

# Update Post
PUT {{host}}/posts/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "title": "New title"
}

###

# Delete Post
DELETE {{host}}/posts/1
Authorization: Bearer {{token}}
```

This approach saves me from having to repeat the full URL everywhere and works exceptionally well in scenarios where different environments require different hosts or tokens.

For APIs that have authorization, I can add an `Authorization` header with a value of `Bearer {{token}}`.

Before making a request, I will set the environment I want to use to replace my variables.

![Select Environment](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309067494/274055c6-f616-4e73-b223-23365362a180.png align="center")

### Per-Request Variables

Per-request variables allow me to set variables before specific requests, offering flexibility in customization. Here's how I set a per-request variable for the "Get Post" request:

```http
# Get Post
< {%
    request.variables.set("postId", "10")
%}
GET {{host}}/posts/{{postId}}
Authorization: Bearer {{token}}
```

Now, when running the request, it will automatically replace `postId` in the URL with “10” from the `postId` variable. Note that request variables must be a string value.

![Making a request with a pre-request variable](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309100591/75a70c8a-25ab-4347-92fb-65c88b370ccd.png align="center")

This method is particularly useful when I need to adjust variables for specific requests without affecting global environment variables.

### Response Variables

Response variables can be really powerful. They allow me to set a new variable based on the response of a request. For example, after fetching the list of posts, I can create a new `postId` variable that grabs the first `id` from the lists of posts.

```http
# List Posts
GET {{host}}/posts
Authorization: Bearer {{token}}

> {%
    client.global.set("postId", response.body[0].id);
%}
```

Now, if I wanted to test updating the first post from the list, I can do the following:

```http
# Update Post
PUT {{host}}/posts/{{postId}}
Authorization: Bearer {{token}}
Content-Type: application/json

{
    "title": "New title"
}
```

The `postId` is set from previously calling the “List Posts” request, so it will be set to “1”. I can test by running the “Update Post” request.

![Using a response variable](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309141688/e6a87210-6c88-462c-b8bb-852a10b4d836.png align="center")

When running the request, I can see in the console that the `postId` was replaced with “1” in the URL.

### Dynamic Variables

Dynamic variables are kind of like helper variables. They allow automatically generating common data on the fly, like UUIDs, random integers, timestamps, and more.

As an example, if I want to delete a random post, I can use the `$random.integer(1, 100)` dynamic variable. This will generate a random number between 1 and 100.

```http
# Delete Post
DELETE {{host}}/posts/{{$random.integer(1, 100)}}
Authorization: Bearer {{token}}
```

When running the request, I see the following output, where the random number is 96.

![Using a dynamic variable](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309184012/6900eee1-e884-4fc8-804b-ece1ccbc2e31.png align="center")

For a complete list of available dynamic variables, refer to the [documentation](https://www.jetbrains.com/help/phpstorm/exploring-http-syntax.html#dynamic-variables).

## Importing and Exporting Requests

PhpStorm's built-in HTTP client simplifies importing and exporting requests, making it easy to transition my workflow. It handles importing cURL requests and even Postman collections.

### cURL

With the following cURL code, I can import it right into a request file.

```bash
curl -X POST --location "<https://jsonplaceholder.typicode.com/posts>" \\ 
-H "Authorization: Bearer DEVELOPMENT_TOKEN" \\ 
-H "Content-Type: application/json" \\ 
-d "{ \\"title\\": \\"Post title\\", \\"body\\": \\"Post body\\", \\"userId\\": 1 }"
```

To import, I click the import button and select the import type, cURL for this example.

![Import cURL Command](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309234404/7e9f9fff-2bff-4ea9-949e-8b05eabd1d81.png align="center")

This will bring up a new window to paste in the cURL code.

![Convert cURL to HTTP Request](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309257208/5ec75e99-f0f3-4799-9a2c-fabb93c2c181.png align="center")

After clicking convert, the cURL request is now added to the file as an HTTP request.

![cURL Request added to HTTP Request File](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309271692/5080317d-6ec5-486d-b83f-78dccf0e78f8.png align="center")

For exporting requests, I simply click the export button, select the environment to use for variable conversion, and the cURL code for the request is copied automatically.

![Export to cURL](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309288169/0e800fd0-7b54-4bd9-beaf-4a53fc227a0a.png align="center")

### Postman

If I'm transitioning from Postman, JetBrains offers a plugin that allows me to import Postman collections into PhpStorm's HTTP requests. This plugin can ease the migration process.

![Import from Postman](https://cdn.hashnode.com/res/hashnode/image/upload/v1697309306174/927f3c32-569a-49ad-9679-a936f9412f11.png align="center")

## Conclusion

PhpStorm's built-in HTTP client and requests functionality offers an array of features that have significantly enhanced my API testing process. Beyond simplifying basic requests, I can execute JavaScript logic within `< {% %}` blocks, handle cookies, and even accommodate GraphQL requests. This all-in-one approach saves me the hassle of switching between different applications like Postman or Insomnia.

For more in-depth information and additional features, consult the PhpStorm documentation:

* [HTTP Client | PhpStorm](https://www.jetbrains.com/help/phpstorm/http-client-in-product-code-editor.html)
    
* [Exploring the HTTP request syntax | PhpStorm](https://www.jetbrains.com/help/phpstorm/exploring-http-syntax.html)
    

Thank you for joining me on this journey exploring PhpStorm’s HTTP client and requests!
