In this five-part article series, you will learn how to manipulate your API behavior with a local proxy server. After setting the scene with an introduction and learning how to set up and configure Charles Proxy, we will now take a look at Charles Proxy in action. You will gain the skill to manipulate, fake, and replay API responses on your behalf.

Article series

  1. Introduction
  2. Charles Proxy Setup & Configuration
  3. Charles Proxy in Action
  4. Fiddler Setup & Configuration (coming soon)
  5. Fiddler in Action (coming soon)

Mission 1: Testing the frontend against various API responses

The todo demo API returns a JSON array with three objects each with an id and title property. Now we want to test the frontend with a very long title.

We can do this via breakpoints that can be enabled through "Proxy" → "Breakpoint Settings". There we can enable breakpoints per location. For the demo API, we want it on each response:

Charles Proxy: breakpoint settings

After setting up the breakpoints, we're triggering a new request to the API. Charles halts the response:

Charles Proxy: halting breakpoint

With a click on "Edit Response" on the right side we're able to change the response's headers and body. In this case, we leave the headers as they are and only change the title of one object. Note: the Content-Length header will be updated automatically by Charles!

Charles Proxy: edit the response in a breakpoint

After clicking the "Execute" button, the frontend immediately receives the modified response (some frontends may throw a timeout error if it takes too long - please consider this):

Resulting frontend after changing the response in Charles Proxy

This way, we can completely modify the response coming back from our API. We could even add new properties or change the structure of the response completely. Though, we have to modify every single response manually. Since that is not very practical, we are moving forward to our second mission.

Mission 2: Developing against an erroneous API implementation

We have already learned how to set breakpoints and change a single response on the fly. However, if we have an erroneous or outdated API implementation and need to continue developing the frontend, we need a way to receive the modified response each time automatically without interaction. Here, Charles' mapping tools are coming into play.

These allow us to map a request's response to a local file. Charles will replace the original response body with the contents of our local file. Exactly what we want!

There are two options to create this file:

  1. Create it on your own or
  2. save the original response to a file and edit its content with any text editor

Let's take a look at the second option, that in a real-world application is more practical to use:

Charles Proxy: save response

When done, we will edit it so that the JSON matches the requirements of the next development iteration. Since this is going to be a todo app, the todo items from the response need an additional property done. The result is:

    "id": "1",
    "title": "Todo 1",
    "done": false
}, {
    "id": "2",
    "title": "Todo 2",
    "done": false
}, {
    "id": "3",
    "title": "Todo 3",
    "done": true

Last but not least, the /api/todo endpoint needs to get mapped to the local file. When you have a recorded request, you can right-click and then select "Map Local..." at the bottom of the context menu. The following dialog will be pre-filled to match this request. Otherwise, you can do it manually by selecting "Tools" → "Map Local" from the menu bar.

Charles Proxy: map local response file

Now let's test the new response by triggering a new request from the frontend. Oops, something is still not working!

Frontend CORS error

What happened is that Charles created the whole response on its own – forgetting to set the CORS headers. Our records will help us to evaluate the difference.

Response header from API:

HTTP/1.1 200 OK
Server: Cowboy
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Requested-With,Content-Type,Cache-Control,access_token
Content-Type: application/json
Content-Length: 88
Vary: Accept-Encoding
Date: Fri, 11 Oct 2019 08:54:09 GMT
Via: 1.1 vegur
Expires: 0
Cache-Control: no-cache
Connection: keep-alive

Response header from Charles:

HTTP/1.1 200 OK
Content-Length: 156
Content-Type: application/json
X-Charles-Map-Local: /home/haggis/path/to/blog/proxy/demo-files/todo.json
Expires: 0
Cache-Control: no-cache
Connection: keep-alive

The Access-Control-Allow-* headers are important here. When we originally saved the response to a JSON file, Charles only saved the body and ignored the header. There does not seem to be a way to map a request to a file containing both header and body.

Fortunately, Charles also provides the Rewrite Tool. It allows us to alter a response or request automatically. We can choose between different types of actions, such as, adding/removing headers, modifying the body, or changing query parameters. In this case, however, it is enough to add headers:

Charles Proxy: rewrite CORS

When we send the request again, the CORS errors are gone, and the frontend receives the modified response from the local JSON file. Look at how the frontend can now handle the done property of todo items even though the API does not know anything about it!

Frontend handles the new response correctly

Let's continue to our next mission!

Mission 3: Developing against an incomplete API

The todo app is still missing the option to add new todo items. The current API provides only one endpoint: GET /api/todo to list all todo items.

Additionally, we want POST /api/todo to create a new todo item. It should return our new item. Unfortunately, this does not work with Charles. As both endpoints have the same location, we would need a possibility to distinguish requests by their method – which is a missing feature. We cannot give them different responses. In our case, the POST request also returns the list of all todo items. There is no way to work around this.

For the ake of this demonstration, let us assume our creation endpoint is POST /api/add/todo rather than POST /api/todo.

When we trigger this POST request from our client, we receive the expected result 400 (Bad Request) because there is no such route yet. So let us create a local JSON file containing the expected result:

    "id": "4",
    "title": "Todo 4",
    "done": false

Mapping the failed request to this file, and we are done. In an ideal world, we could add a rewrite rule on top to access the request's body and pasting it into the response body. Unfortunately, that is also not possible. So we always get the same response back from Charles.

Fake adding of a new item

Great! We can test various responses and even fake endpoints. But what if we are going to travel and are expecting some offline time in advance?

Mission 4: Offline development against a remote API

We could solve this mission by mapping every single request to a local file. If we have a bigger API then our todo API, this task would become quite exhausting.

Charles provides an easy solution to this:

  1. In Charles: enable recording
  2. On your client: trigger all requests, which you will need later
  3. In Charles: save & map all those requests at once

Step 1. and 2. we've already done in this article. Let's take a look at how batch saving a mapping of requests works.

Charles Proxy: Request groups

As you can see, Charles organizes all requests in a hierarchical tree structure. Instead of right-clicking on a single request, we just have to right-click on a higher level. In this case, we save everything from "api" downwards.

When we get a warning that there are multiple requests for the same location and get asked whether we want to store all, only the first or only the last, this is probably because of the CORS preflight request. We should be fine by choosing to save only the last one.

As a result, we have got a complete folder with saved responses on our disc. To map them, again right-click on the appropriate level (here "api"), then choose "Map Local...", and then select the directory with your recordings. If we have other active mappings for the same API, we recommend disabling those to prevent any unexpected behavior.

Switch off the network for a test and reload your frontend. Oops, now we have an SSL error!

SSL termination error

By looking at Charles' records, it becomes clear that Charles tried to open a tunnel to the remote server – even though there is a matching mapping. Unfortunately, there is no way to disable this behavior.

So no offline HTTPS API for the moment. However, if we are pointing our client to the insecure HTTP variant, it works. Voilà!

We can either do this in our clients. Or with Charles' rewrite tool by creating a URL rewrite rule. If we choose the rewrite option, we also have to disable SSL decryption. Otherwise, Charles is trying to establish an SSL connection before the URL gets rewritten.


We have explored a couple of possibilities in this article, about how frontend developers can get more independent of the API they are working with. We have now gained the skill to manipulate, fake, and replay API responses on our behalf.

Unfortunately, we faced some issues on missions 3 and 4:

  • No different fake responses on the same route distinguished by HTTP methods.
  • No HTTPS fake responses when you are offline. Even if you have SSL decryption turned on and a matching local mapping.

These two issues may be a show stopper for a use case. However, Charles is still a useful tool for debugging and testing.

As you already might have seen, Charles even provides many more tools and possibilities than those, such as Throttling, DNS Spoofing, and more. Take your time to learn more about it, and you have an excellent tool that helps you debugging, testing, and developing your frontend as well as your backend.

In the upcoming article, we will explain how to setup and configure Fiddler.

Don't miss out on news about Tools & more

Subscribe to our free monthly newsletter for our experts' latest technical articles about Angular, .NET, Blazor, Azure, and Kubernetes.

Please enter a valid email address.

Related Articles

Fiddler in Action: Mocking and Manipulating API Behavior With a Local Proxy Server - Part 5
Article series Introduction Charles Proxy Setup & Configuration Charles Proxy in Action Fiddler Setup & Configuration Fiddler in Action Mission 1: Testing the Frontend Against Various API Responses The todo demo API returns a JSON array with three objects, each with an and…
Thinktecture Team
Introduction: Mocking and Manipulating API Behavior With a Local Proxy Server - Part 1
When it comes to developing against an HTTPS/REST API, which we do not control ourselves, we face many problems regularly. Issues and hard to provoke edge cases may include a wrong implementation, an incomplete implementation or connection problems (dial-in issues, dropping…
Thinktecture Team core
Einführung: ASP.NET Core API-Dokumentation mit Swagger: Teil 1 [Screencast]
Thinktecture Backend- und API-Spezialist Sebastian Gingter zeigt in dieser 10-teiligen Screencast-Serie wie man seine ASP.NET Core 3.1 API mithilfe von Swagger dokumentieren kann. Der erste Teil befasst sich damit welche Gründe für eine Dokumentation sprechen und wo der…
Sebastian Gingter core
Einbindung von Versionierung in existierende Projekte: API-Versionierung mit ASP.NET Core 3.0 - Teil 4 [Screencast]
Thinktecture Backend- und API-Spezialist Sebastian Gingter zeigt in dieser vierteiligen Screencast-Serie, wie eine ASP.NET Core 3.0 API versioniert wird. Nach einer Einführung in die API-Versionierung, der Integration in neue Projekte und Hinweisen zur Konfiguration, geht es im…
Sebastian Gingter