Running WireMock Server on AWS Lambda
Saurabh Dutta
Posted on February 27, 2020
The serverless paradigm in recent times is getting popular, this was also covered in one of my favorite podcast channel Syntax.fm in their recent episode called Serverless / Cloud Functions - Part 1 — Syntax Podcast 224.
About Serverless architecture
Serverless architecture are application designs that incorporate third-party "Backend as a Service" (BaaS) services, and/or that include custom code run in managed, ephemeral containers on a “Functions as a Service” (FaaS) platform. By using these ideas, and related ones like single-page applications, such architectures remove much of the need for a traditional always-on server component. Serverless architectures may benefit from significantly reduced operational cost, complexity, and engineering lead time, at a cost of increased reliance on vendor dependencies and comparatively immature supporting services. -- reference
In this post, we aim to create a mock REST API server using serverless paradigm with AWS lambda and WireMock, because, why not?
Lambda based REST API mock-server can help us in development for frontend/mobile app, without headaches of maintaining a dedicated server, also AWS provides generous free usage on AWS lambda.
The AWS Lambda free usage tier includes 1M free requests per month and 400,000 GB-seconds of compute time per month. AWS Lambda – Pricing
About WireMock as stated on their website:
WireMock is an HTTP mock server. At its core it is web server that can be primed to serve canned responses to particular requests (stubbing) and that captures incoming requests so that they can be checked later (verification).
Prerequisites
Let's start
Project boilerplate is based on aws-serverless-java-container/samples/springboot2
build.gradle
dependencies {
...
implementation('org.springframework.boot:spring-boot-starter-web:2.1.8.RELEASE') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
implementation group: 'com.amazonaws.serverless', name: 'aws-serverless-java-container-springboot2', version: '1.4'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '2.2.1.RELEASE'
...
}
SpringConfig.java
@Configuration
public class SpringConfig {
@Bean
public WireMockServer getWireMockServer() {
WireMockConfiguration options = new WireMockConfiguration()
// Mapping files from /src/main/resources are availabe on /var/task in lambda
.usingFilesUnderDirectory("/var/task/wiremock")
// Start mock server on port 9090.
.port(9090)
// Enable Verbose logging
.notifier(new ConsoleNotifier(true));
return new WireMockServer(options);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
pet-mapping.json
Wiremock mapping file, for complex mapping capabilities for request matching of WireMock refer to WireMock Request Matching
{
"request": {
"url": "/pets",
"method": "GET"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": "[{\"id\":\"7c78a010-e100-48a5-82d8-278e4cab4824\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Daisy\",\"dateOfBirth\":1171968225028},{\"id\":\"0bbb7980-91e5-4e4f-902a-d21f297229f1\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Buddy\",\"dateOfBirth\":1427625825029},{\"id\":\"51f1d89f-8ca0-4fa7-99b7-ce3b6bb45551\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Cooper\",\"dateOfBirth\":1234262625029},{\"id\":\"deece10b-3b6f-4220-b215-93d5e6ce9f04\",\"breed\":\"Afghan Hound\",\"name\":\"Gracie\",\"dateOfBirth\":1335869025029},{\"id\":\"744b70bd-e61a-479e-9f4d-1a07abb46aad\",\"breed\":\"Dalmatian\",\"name\":\"Riley\",\"dateOfBirth\":1490957025029},{\"id\":\"2608727e-892e-4b7a-9cdb-f613a18cddc0\",\"breed\":\"Afghan Hound\",\"name\":\"Riley\",\"dateOfBirth\":1473504225029},{\"id\":\"555d71ab-e1cd-42d2-9264-d323b3c55026\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Sadie\",\"dateOfBirth\":1167907425030},{\"id\":\"2c8dee30-d566-4622-93ae-49ee7073d132\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Max\",\"dateOfBirth\":1294569825030},{\"id\":\"ce6c8a7b-0240-45e9-8188-d38069ca9e0e\",\"breed\":\"Beagle\",\"name\":\"Bella\",\"dateOfBirth\":1373366625030},{\"id\":\"fa20c0f7-4e5c-4e9b-a957-b685329cd4fa\",\"breed\":\"Bloodhound\",\"name\":\"Abby\",\"dateOfBirth\":1149590625030}]"
}
}
PetsController.java
@RestController
@EnableWebMvc
public class PetsController {
private final WireMockServer mockServer;
private final RestTemplate restTemplate;
private static final String WIRE_MOCK_BASE_URL = "http://localhost:9090";
@Autowired
public PetsController(WireMockServer mockServer, RestTemplate restTemplate) {
this.mockServer = mockServer;
this.restTemplate = restTemplate;
}
@GetMapping(path = "/mappings")
public ObjectNode getWireMockMappings() {
mockServer.start();
ResponseEntity<ObjectNode> responseEntity =
restTemplate.getForEntity(WIRE_MOCK_BASE_URL +"/__admin/mappings", ObjectNode.class);
mockServer.stop();
return responseEntity.getBody();
}
@GetMapping(path = "/pets")
public Pet[] listPetsWireMock() {
mockServer.start();
ResponseEntity<Pet[]> responseEntity =
restTemplate.getForEntity(WIRE_MOCK_BASE_URL + "/pets", Pet[].class);
mockServer.stop();
return responseEntity.getBody();
}
}
Local Testing
Build deployable jar
$ ./gradlew clean build
Run Lambda locally with API Gateway
$ sam local start-api -t sam.yaml
Get wiremock mappings
GET http://localhost:3000/mappings
Accept: */*
Cache-Control: no-cache
Postman-Token: 225f9be7-43d1-4d9e-9d84-04775d28e533
Host: localhost:3000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 1537
Server: Werkzeug/1.0.0 Python/3.7.6
Date: Thu, 27 Feb 2020 22:05:21 GMT
{
"mappings": [
{
"id": "61d821f0-08a5-4d43-9e5c-2b11624be3dc",
"request": {
"url": "/pets",
"method": "GET"
},
"response": {
"status": 200,
"body": "[{\"id\":\"7c78a010-e100-48a5-82d8-278e4cab4824\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Daisy\",\"dateOfBirth\":1171968225028},{\"id\":\"0bbb7980-91e5-4e4f-902a-d21f297229f1\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Buddy\",\"dateOfBirth\":1427625825029},{\"id\":\"51f1d89f-8ca0-4fa7-99b7-ce3b6bb45551\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Cooper\",\"dateOfBirth\":1234262625029},{\"id\":\"deece10b-3b6f-4220-b215-93d5e6ce9f04\",\"breed\":\"Afghan Hound\",\"name\":\"Gracie\",\"dateOfBirth\":1335869025029},{\"id\":\"744b70bd-e61a-479e-9f4d-1a07abb46aad\",\"breed\":\"Dalmatian\",\"name\":\"Riley\",\"dateOfBirth\":1490957025029},{\"id\":\"2608727e-892e-4b7a-9cdb-f613a18cddc0\",\"breed\":\"Afghan Hound\",\"name\":\"Riley\",\"dateOfBirth\":1473504225029},{\"id\":\"555d71ab-e1cd-42d2-9264-d323b3c55026\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Sadie\",\"dateOfBirth\":1167907425030},{\"id\":\"2c8dee30-d566-4622-93ae-49ee7073d132\",\"breed\":\"Bernese Mountain Dog\",\"name\":\"Max\",\"dateOfBirth\":1294569825030},{\"id\":\"ce6c8a7b-0240-45e9-8188-d38069ca9e0e\",\"breed\":\"Beagle\",\"name\":\"Bella\",\"dateOfBirth\":1373366625030},{\"id\":\"fa20c0f7-4e5c-4e9b-a957-b685329cd4fa\",\"breed\":\"Bloodhound\",\"name\":\"Abby\",\"dateOfBirth\":1149590625030}]",
"headers": {
"Content-Type": "application/json"
}
},
"uuid": "61d821f0-08a5-4d43-9e5c-2b11624be3dc"
}
],
"meta": {
"total": 1
}
}
Get pets
GET http://localhost:3000/pets
Accept: */*
Cache-Control: no-cache
Host: localhost:3000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 1149
Server: Werkzeug/1.0.0 Python/3.7.6
Date: Thu, 27 Feb 2020 21:55:27 GMT
[
{
"id": "7c78a010-e100-48a5-82d8-278e4cab4824",
"breed": "Bernese Mountain Dog",
"name": "Daisy",
"dateOfBirth": 1171968225028
},
{
"id": "0bbb7980-91e5-4e4f-902a-d21f297229f1",
"breed": "Bernese Mountain Dog",
"name": "Buddy",
"dateOfBirth": 1427625825029
},
{
"id": "51f1d89f-8ca0-4fa7-99b7-ce3b6bb45551",
"breed": "Bernese Mountain Dog",
"name": "Cooper",
"dateOfBirth": 1234262625029
},
{
"id": "deece10b-3b6f-4220-b215-93d5e6ce9f04",
"breed": "Afghan Hound",
"name": "Gracie",
"dateOfBirth": 1335869025029
},
{
"id": "744b70bd-e61a-479e-9f4d-1a07abb46aad",
"breed": "Dalmatian",
"name": "Riley",
"dateOfBirth": 1490957025029
},
{
"id": "2608727e-892e-4b7a-9cdb-f613a18cddc0",
"breed": "Afghan Hound",
"name": "Riley",
"dateOfBirth": 1473504225029
},
{
"id": "555d71ab-e1cd-42d2-9264-d323b3c55026",
"breed": "Bernese Mountain Dog",
"name": "Sadie",
"dateOfBirth": 1167907425030
},
{
"id": "2c8dee30-d566-4622-93ae-49ee7073d132",
"breed": "Bernese Mountain Dog",
"name": "Max",
"dateOfBirth": 1294569825030
},
{
"id": "ce6c8a7b-0240-45e9-8188-d38069ca9e0e",
"breed": "Beagle",
"name": "Bella",
"dateOfBirth": 1373366625030
},
{
"id": "fa20c0f7-4e5c-4e9b-a957-b685329cd4fa",
"breed": "Bloodhound",
"name": "Abby",
"dateOfBirth": 1149590625030
}
]
Code for the project can be found here: https://github.com/saurabh73/aws-serverless-wiremock-server
Additional Links
Posted on February 27, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.