Charles Proxy In Action: Mocking And Manipulating API Behavior With A Local Proxy Server – Part 3

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.

In diesem Artikel:

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:

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

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!

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):

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:

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.

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

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:

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!

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.

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.

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!

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.

Mehr Artikel zu API, Tools

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Newsletter Anmeldung
Diese Artikel könnten Sie interessieren

Git Fixup: Wie repariere ich meine Historie?

Git gibt uns mit der fixup-Option eine einfache aber effektive Möglichkeit, kleine Versäumnisse nachträglich so zu korrigieren, dass sie gar nicht mehr auffallen. Wie das genau geht, wollen wir in diesem Artikel erforschen. Mein Kollege Yannick Baron hat in seiner englischen Artikelserie zu Git bereits den interactive Rebase behandelt. Dort hat er auch die Möglichkeiten des fixup kurz angesprochen. Hier wollen wir uns etwas intensiver mit diesem Feature befassen.

Verwendung für Postman-Tests: ASP.NET Core API-Dokumentation mit Swagger – Teil 10 [Screencast]

Im zehnten und letzten Teil sehen Sie, wie Sie aus der Dokumentation auch Postman-Tests generieren können.

Code-Generierung aus der Dokumentation: ASP.NET Core API-Dokumentation mit Swagger – Teil 9 [Screencast]

Im neunten Teil sehen Sie, wie aus unserer Dokumentation vollständige Clients generiert werden können.

Swagger-UI anpassen: ASP.NET Core API-Dokumentation mit Swagger – Teil 8 [Screencast]

Im achten Teil sehen Sie, welche Modifikationen Sie am Swagger-UI vornehmen können. Zum Beispiel die Einbindung eines eigenen Logos in die Dokumentation oder einige weitere Features die Swagger mitbringt, welche aber nicht von Beginn an aktiviert sind.

Einbindung von Authentication & Authorization: ASP.NET Core API-Dokumentation mit Swagger – Teil 7 [Screencast]

Im siebten Teil sehen Sie, wie eine API, die Authentifizierung benötigt, korrekt dokumentiert wird.

Dokumentation versionierter APIs: ASP.NET Core API-Dokumentation mit Swagger – Teil 6 [Screencast]

Im sechsten Teil sehen Sie wie Sie vorgehen können, um mehrere API-Versionen gleichzeitig zu dokumentieren und wie Sie diese Versionen in SwaggerUI einbinden.