Mocking APIs with Microcks

Learn how to work with a powerful API mocking tool: Microcks, and how to integrate it with your existing API documentation flow.

Mocking APIs with Microcks

This was originally written for Bump.sh, but I wanted to share it with a wider audience because Microcks is a very impressive OpenAPI mocking tool.

You’ve created beautiful documentation for your API powered by OpenAPI, what’s next? If you’ve been following the API design-first workflow the next phase is gathering feedback on the proposed design before investing loads of time building it.

Sharing the documentation is a good start, but you can get even better feedback by giving stakeholders a mock server to interact with. A mock server simulates an API, allowing stakeholders to see if all the data they need is available, and give feedback on how easily their workflows can be solved based on the endpoints in the API. This can be thought of like a study group, something user researchers are used to doing for frontends, but is just as valuable for APIs.

This used to be done with “prototypes” or manually made mock servers, but seeing as most Bump.sh users have already got the API described entirely by OpenAPI already, setting up mock servers is a whole lot easier using OpenAPI-powered tools like Microcks to do the hard work for you. Using OpenAPI instead of code means changes can be made to the mock server far quicker than getting the API developments to change loads of code.

Let’s take a look at using Microcks specifically, and see how we can tie it into your existing Bump.sh documentation.

Step 1: Set up Microcks locally

Microcks is a Java (Spring Boot) application, which comes packaged as a Docker container, so it can be deployed anywhere either of those environments are happy. Setting it up locally is probably the first step to see how it works.

Whether you use Docker or prefer Podman, grab one of those applications to install Microcks within a container.

Assuming you’ve got Docker installed and running, you can use following command to get Microcks running on your computer.

docker run -p 8585:8080 -it --rm quay.io/microcks/microcks-uber:latest-native

When that’s done, open a browser tab and point to the http://localhost:8585 endpoint, changing the port if you picked a different one.

Microcks dashboard in a browser window running on http://localhost:8585

Step 2: Add Your First API

There are several ways to get OpenAPI into Microcks, but for the sake of simplicity we’re going to use the web interface to upload your OpenAPI. If you are just starting out and don’t have any OpenAPI yet, why not use the Train Travel API for now.

Once we’ve got Microcks loaded in the browser, click on “Importers”, and click the “Upload” button.

The "Direct upload artifact" modal window

The modal that pops up is asking for an Artifact, which is referring to various documents that could describe an API, like a Postman Collection and other “API description documents”. We can pop our OpenAPI in there, which is the same openapi.yaml document that you deploy to Bump.sh.

The question about primary or secondary artifacts can be ignored for now.

The API View contains the title and version of the uploaded API, a list of endpoints, and shows whether it found "samples" (examples)

Once you’ve uploaded, go to “APIs / Services” and click on your new API, which should have the same name as whatever was in the info.title of your OpenAPI. Below it you’ll see a bunch of operations, which are all the endpoints that Microcks found in your OpenAPI.

Step 3: Try out the Mock Endpoints

Open up one of the operations and see what Microcks thinks about it.

The API view further down has a list of operations, which have been expanded to reveal more information about the mock URL and sample response

You should see a URL which you can copy, and the sample JSON it has pulled from the OpenAPI examples object.

http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings

It’s basically:

<wherever microcks is running>/rest/<info.name>/<info.version>/<operation path>

If that operation has a GET method defined you can copy a curl command that’ll look something like this:

curl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' -H 'Accept: application/json'

Running this command will then return the sample Microcks was showing the web interface.

{
  "data": [
    {
      "id": "bfc5af2c-f477-43c4-8bdf-a00bdb939d65",
      "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
      "passenger_name": "John Doe",
      "has_bicycle": true,
      "has_dog": true
    },
    {
      "id": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
      "trip_id": "b2e783e1-c824-4d63-b37a-d8d698862f1d",
      "passenger_name": "Jane Smith",
      "has_bicycle": false,
      "has_dog": false
    }
  ],
  "links": {
    "self": "https://api.example.com/bookings",
    "next": "https://api.example.com/bookings?page=2"
  }
}

Getting a collection is a handy first example, but next lets try working with resources.

Microcks uses examples with a particular naming contention, pairing up request examples and response examples with the same name. The naming convention is is <resource>_<id>, so sticking with the Train Travel API, we could do this:

 /bookings/{bookingId}:
    get:
      summary: Get a booking
      operationId: get-booking
      parameters:
        - name: bookingId
          in: path
          required: true
          description: The ID of the booking to retrieve.
          schema:
            type: string
            format: uuid
          examples: 
            booking_1725ff48-ab45-4bb5-9d02-88745177dedb:
              value: 1725ff48-ab45-4bb5-9d02-88745177dedb
            booking_bfc5af2c-f477-43c4-8bdf-a00bdb939d65:
              value: bfc5af2c-f477-43c4-8bdf-a00bdb939d65
      responses:
        '200':
          description: The booking details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Booking'
              examples:
                booking_1725ff48-ab45-4bb5-9d02-88745177dedb:
                  summary: John Doe
                  value:
                    id: 1725ff48-ab45-4bb5-9d02-88745177dedb
                    trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e
                    passenger_name: John Doe
                    has_bicycle: true
                    has_dog: true
                booking_bfc5af2c-f477-43c4-8bdf-a00bdb939d65:
                  summary: Billy Bikeless
                  value:
                    id: bfc5af2c-f477-43c4-8bdf-a00bdb939d65
                    trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e
                    passenger_name: Billy Bikeless
                    has_bicycle: false
                    has_dog: true

Upload that to Microcks again and it will now be aware of these two examples, and show you the mock URLs for both.

Now you can call either of them, and get different HTTP responses with the appropriate example data being used for each.

curl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb' -H 'Accept: application/json'

curl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings/bfc5af2c-f477-43c4-8bdf-a00bdb939d65' -H 'Accept: application/json'

This is already really useful, you could use this to build a read-only API client on your computer without needing the actual API to actually exist.

If you need to send POST, PUT or PATCH requests you can do that too. Let’s try sending a POST request. The “Copy to clipboard” functionality in Microcks won’t automatically give you a predefined request body, but pop back over to your Bump.sh API docs to get a better curl example and it should look a bit like this.

curl -X POST 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' \
  -H 'Accept: application/json' \
  -H 'Content-type: application/json' \
  -d '{"passenger_name":"New Passenger","has_bicycle":false,"has_dog":false,"trip_id":"4f4e4e1-c824-4d63-b37a-d8d698862f1d"}'
{
  "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
  "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e",
  "passenger_name": "John Doe",
  "has_bicycle": true,
  "has_dog": true,
  "links": {
    "self": "https://api.example.com/bookings/efdbb9d1-02c2-4bc3-afb7-6788d8782b1e"
  }
}

That’s somewhat handy, but it’s showing me a generic response instead of the data sent in the POST request. Named examples would not help here, as we want to use client data from the request body and not just show predetermined examples. How can we New Passenger instead of John Doe, and how can we see the actual trip_id instead of the default one?

Step 4: Customizing Responses

Mocks can be made more powerful with dynamic mocking, which is made possible through Microcks’ templating system. Using a combination of predefined variables, built-in functions, and some JSON Pointers, we can provide a dynamic example in OpenAPI which put together a more useful response.

responses:
  '201':
    description: Booking successful
    content:
      application/json:
        schema:
          # snip
        examples:
          new_booking:
            summary: New Booking
            value: |-
              {
                "id": "{{ uuid() }}",
                "trip_id": "{{ request.body/trip_id }}",
                "passenger_name": "{{ request.body/passenger_name }}",
                "has_bicycle": {{ request.body/has_bicycle }},
                "has_dog": {{ request.body/has_dog }},
                "links": {
                  "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
                }
              }

Go back to Importers, Upload the openapi.yaml document again, and it will merge changes into API so we can see how they look.

**curl** -X POST 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' \
  -H 'Accept: application/json' \
  -H 'Content-type: application/json' \
  -d '{"passenger_name":"New Passenger","has_bicycle":false,"has_dog":false,"trip_id":"4f4e4e1-c824-4d63-b37a-d8d698862f1d"}'

Now we will see the values we sent showing back up, and the computed random UUID.

{
  "id": "a248a638-000a-44b4-b19b-0ca30507a940",
  "trip_id": "4f4e4e1-c824-4d63-b37a-d8d698862f1d",
  "passenger_name": "New Passenger",
  "has_bicycle": false,
  "has_dog": false,
  "links": {
    "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb"
  }
}

Finally, that HATEOAS link at the bottom there is not right. The id is a248a638-000a-44b4-b19b-0ca30507a940, so how can we get that showing up in the link? If we used {{ uuid() }} again it would be generate a second different UUID, Microcks templating has a brilliant feature called capture.

examples:
  new_booking:
    summary: New Booking
    value: |-
      {
        "id": "{{ uuid() > put(bookingId) }}",
        "trip_id": "{{ request.body/trip_id }}",
        "passenger_name": "{{ request.body/passenger_name }}",
        "has_bicycle": {{ request.body/has_bicycle }},
        "has_dog": {{ request.body/has_dog }},
        "links": {
          "self": "https://api.example.com/bookings/{{ bookingId }}"
        }
      }

Upload that again. Run the command again.

{
  "id": "b4441c3d-b659-4f57-95d3-ce6ee592da7c",
  "trip_id": "4f4e4e1-c824-4d63-b37a-d8d698862f1d",
  "passenger_name": "New Passenger",
  "has_bicycle": false,
  "has_dog": false,
  "links": {
    "self": "https://api.example.com/bookings/b4441c3d-b659-4f57-95d3-ce6ee592da7c"
  }
}

Perfect! Now you can do almost anything you need to do with the API.

Step 5: Deploy Microcks Somewhere

Working with Microcks locally was an OK way to get the hang of it, and could be useful for some solo developers, but the chances are you’ll want to get this off your laptop and share it with people.

You can deploy Microcks anywhere that takes Docker/Kubernetes instances, and they have lots of documentation on your deployment options for Microcks.

One approach is to deploy the Docker container into a managed Kubernetes cluster on Google Kubernetes Engine, which has free trials and free tiers to ease you into the process.

A screenshot showing Google Cloud's Kubernetes Engine section, on the Create a deployment page

This puts Microcks behind a publicly accessible load balancer, so you can stick some DNS on it and have everyone able to call the hosted version of the mock server.

curl -X GET 'https://mocks.example.com/rest/Train+Travel+API/1.0.0/bookings'

Once this is done we can replace the manual uploads with some automatic solutions too.

Step 6: Automate Mock Updates

Most Bump.sh users use some form of Continuous Integration (CircleCI, GitHub Actions, Jenkins, etc.) to push changes to their API documentation whenever the source code is changed, and you can work this way with Microcks, or there are some alternatives you can try out.

Update Mocks with Continuous Integration

Below is the standard GitHub Action used to deploy API changes to Bump.sh with one modification to also deploy changes to your Microcks server.

# .github/workflows/deploy-docs.yml
name: Deploy API documentation

on:
  push:
    branches:
      - main

jobs:
  deploy-openapi:
    if: ${{ github.event_name == 'push' }}
    name: Deploy API documentation on Bump.sh
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Deploy API documentation
        uses: bump-sh/github-action@v1
        with:
          doc: 68ac0647-184a-4e9d-accc-682a5b1f7189
          token: ${{secrets.BUMP_TOKEN}}
          file: api/openapi.yaml

      - uses: microcks/import-github-action@v1
        with:
          specificationFiles: 'api/openapi.yaml:true'
          microcksURL: 'https://mocks.example.com/api/'
          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}
          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}

You’ll need to set up some secrets on your repository for that Microcks service account, but then you’re done! A fully functioning mock server running on the cloud, which you can interact with internally or externally depending on how you set it up.

Learn more about Microcks Automation to see how to push updates to Microcks using other CI systems, via the API, or using the CLI elsewhere. You can also use the Microcks Scheduler instead, to pull content from a repo on a regular schedule instead of pushing.

Step 7: Add Mock Server to API Documentation

Once the mock server is up and running, you can help make it easier to find by adding it to your Bump.sh API documentation. To do this, we can add the server URL into the servers list like this:

servers:
  - url: https://api.example.com
    description: Production
  
  - url: https://mocks.example.com/rest
    description: Mock Server

Adding this second server URL will offer users a dropdown menu in the Bump.sh documentation.

The select box apears on the Bump.sh API documentation allowing users to pick between servers based on server name

The mock server is now an option, and all of the URLs and example HTTP requests will show up using the chosen server URL.

A screenshot of the API documentation updated to contain the mocks.example.com after mock server has been selected

If there’s no production API only the mock server is ready then only define that:

servers:
  - url: https://mocks.example.com/rest
    description: Mock Server
Perhaps you cannot update the OpenAPI document to get that server added as it’s generated by somebody else or hosted online, in which case take a look Overlays.

Summary

Let us know what you think of Microcks as a mocking tool, and how it compares to Prism, Wiretap, and others. It also has testing functionality which we didn't get into here but will in upcoming articles if you're interested. Just let us know down below and we'll get more articles out on what you're interested in.