Creating the API

Learning Microservices with Weightlifting

In this section we will setup the API microservice. We start with an example express server then create two API endpoints that return hard coded data. You can view the final code from this blog post on GitHub

Setup

Create directory for the application and initialize an empty git repository. The entire project will be tracked in the same git repository.

Screenshot-1.png

Each microservice will live within its own subdirectory of the backend directory. The tech stack for each microservice can be different so we will setup a node project and install dependencies just for the api microservice.

Screenshot-2.png

express

Express is a fast, unopinionated, minimalist web framework for Node.js. We will use it to serve our API.

nodemon

nodemon is a utility for live reloading our application as we modify the source code. This is used during development to save us from having to manually restart our application to test changes.

Basic working server with live reload

We will create and test a minimal working server using example code from the express documentation.

// index.js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!\n");
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

Add a script to package.json to run our microservice with nodemon for development.

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start:dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^2.0.15"
  },
  "dependencies": {
    "express": "^4.17.2"
  }
}

Start our microservice in development mode.

Screenshot-3.png

Test the API with curl.

Screenshot-4.png

Now we will update index.js to demonstrate that nodemon will detect the file change and reload our application.

// index.js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Testing live reload!\n");
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

Check the terminal window with nodemon running, you will see the message restarting due to changes. This will allow us to quickly test changes to our code during development.

Screenshot-5.png

Verify the changes went in to affect by sending a request to the server with curl.

Screenshot-6.png

Creating two API Endpoints

For this iteration of our API we will have two endpoints:

  • GET /workouts <- Returns an array of all workout IDs
  • GET /workouts/:workoutId <- Returns an object for a specific workout

Hard Coded Data

Later in the evolution of our application we will retrieve workout data from another microservice. For now we will hard code the initial data.

Create a file data/default.json with the following content:

[
  {
    "id": "7118c175-e666-46db-8f91-4ca9dc627f56",
    "date": "2022-01-13T10:00:00-0700",
    "exercises": [
      { "name": "benchpress", "quantity": "5x5", "weight": "225" },
      { "name": "squat", "quantity": "5x5", "weight": "295" },
      { "name": "deadlift", "quantity": "5x5", "weight": "365" }
    ]
  },
  {
    "id": "007d6592-7f84-4598-b32f-bc115d6d14cf",
    "date": "2022-01-14T10:00:00-0700",
    "exercises": [
      { "name": "benchpress", "quantity": "5x5", "weight": "225" },
      { "name": "squat", "quantity": "5x5", "weight": "295" },
      { "name": "deadlift", "quantity": "5x5", "weight": "365" }
    ]
  }
]

Creating the endpoints

Make the following changes to index.js.

Remove the default `app.get()' function call.

Import our default data.

const defaultData = require("./data/default.json");

Add a route for all workouts

app.get("/workouts", (req, res) => {
  res.json(defaultData.map((x) => x.id));
});

Add a route for a specific workout

app.get("/workouts/:workoutId", (req, res) => {
  const workout = defaultData.find(
    (value) => value.id === req.params.workoutId
  );
  if (!workout) {
    res.status(404);
    res.send("workout not found");
    return;
  }

  res.json(workout);
});

Test route for all workouts

We will use the curl command to hit the all workouts endpoint. I pipe the output of the curl command to a utility called jq to make the JSON more readable.

Screenshot-7.png

Test route for a specific workout

Screenshot-8.png

Whats next?

Now that we have created a minimally functioning API we can create a front end to interact with that API.

Next post coming soon.

Additional Reading