Janos Pasztor

Building an API-driven software: The Plan

It’s been a while since I’ve last written a blog post. In the past few months I’ve been busy working on my project, C-Scanner. Most importantly, I’ve been working on an API and user interface for it. So let’s go through how I built it along the lines of clean code.

The backend

My initial requirement was that I wanted to use an OpenAPI specification. OpenAPI is a standard to describe APIs over HTTP, most commonly implemented in a REST-like fashion.

Wait, lots of tech jargon, let’s go through it. An API (Application Program Interface) is basically a way for a program to have a computer system do something without the intervention of a human. HTTP is the protocol (data transfer standard) of the web, which makes it ideal for building public APIs.

REST is simply a way to use HTTP efficiently for APIs. For example, consider this request:

GET /books/1234 HTTP/1.1
Host: example.com

What does this request do? It gets the book with ID 1234. The response can then be a data record of that book, or the text of the book. In contrast, if I request /books, not /books/1234, I will instead receive a list of books. If I wanted to create a new book, I would send a POST request to /books instead of a GET request. Other verbs that can be used include DELETE to delete something, PATCH to change something, PUT to overwrite an existing object.

There is, of course, more to REST, but more about that later. Now, if you think about the description above, do you know how to fetch a book? Yes, you know what address to use, but you have no idea what data fields a book has. You have no idea if you can or cannot DELETE a book. Heck, you don’t even know the data format that is returned. Is it JSON? Excel? PDF?

These might seem trivial if you look at a web address from a humans perspective because you don’t care, you can interpret the information with the software you have on your computer. However, an application, save for some machine learning, does not have the capacity to figure things out on the go. An application will need to know exactly how the API works.

No problem, right? We will just write documentation! Well, if you have read my previous posts you know that I’m not a fan of manually written documentation as it gets outdated very fast and we humans are notoriously good at making mistakes. That’s why, in my eyes, there are only two ways to create this documentation: either write a documentation first, in a formalized fashion, and generate the server code from that, or write the server code and generate the documentation automatically from that.

Luckily, OpenAPI allows us to do both. One method is that you write the API specification in a format called YAML or JSON first, then generate the server code from it. Once the code is generated, you can implement each API call on the server side. The other method, the one I have chosen, is to write the server code first, then generate the documentation from that.

In my case that meant using some annotations in Java:

@RestController
@RequestMapping(
    "/users"
)
@Api(
    tags = "User accounts"
)
@ExposesResourceFor(UserAccount.class)
@RequestScope
public class UserAccountApi {
    @ApiOperation(
        nickname = "createUserAccount",
        value = "Create a user account",
        notes = "Create a user account by supplying an e-mail address. The user is sent an e-mail to confirm their " +
                "account.",
        consumes = "application/json",
        produces = "application/json"
    )
    @ApiResponses({
        @ApiResponse(
            code = 200,
            message = "The created user account resource.",
            response = UserAccountResource.class
        ),
        @ApiResponse(
            code = 400,
            message = "If the supplied e-mail address is invalid.",
            response = ApiException.class
        ),
        @ApiResponse(
            code = 409,
            message = "If an account with the specified e-mail address already exists.",
            response = ApiException.class
        )
    })
    @RequestMapping(
        method = RequestMethod.POST,
        consumes = "application/json",
        produces = "application/json"
    )
    public UserAccountResource create(
        @ApiParam(required = true)
        @RequestBody
            UserAccountCreateRequest request
    ) throws UserAccountAlreadyExistsException, InvalidInputParameters {
        //...
    }
}

Yes, that’s a lot of annotation. In fact, I feel like the amount of annotations is ridiculous, but you get the picture. We add the metadata to a method, which is then read and leads to a formalized document such as this:

swagger: "2.0"
#...
paths:
  /users: 
    post: 
     tags: 
      - User accounts
     summary: "Create a user account"
     description: "Create a user account by supplying an e-mail address. The user is sent an e-mail to confirm their account."
     operationId: createUserAccount
     consumes: 
      - application/json
     produces: 
      - application/json
     parameters: 
      - in: body
        name: request
        description: request
        required: true
        schema: 
         "$ref": "#/definitions/UserAccountCreateRequest"
     responses: 
      200: 
       description: "The created user account resource."
       schema: 
        "$ref": "#/definitions/UserAccount"
      400: 
       description: "If the supplied e-mail address is invalid."
       schema: 
        "$ref": "#/definitions/ApiException"
      409: 
       description: "If an account with the specified e-mail address already exists."
       schema: 
        "$ref": "#/definitions/ApiException"
     deprecated: false
#...

This kind of a document gives us a significant advantage: since the document is standard, we can generate not only documentation (which is nice), but also client SDK-s, libraries in more than a dozen languages using, for example the OpenAPITools Generator. This makes it easy to develop software using our API, including our own frontend.

All this is done using the popular Spring Boot framework, with the help of Spring Fox to generate the API documentation.

Criticism: I absolutely have criticisms towards Spring Boot/SpringFox because it requires a lot of redundant annotations to make OpenAPI work. OpenAPI is a hard requirement for me and I found no better mainstream system.

The frontend

Now on to the frontend. Having an API on the backend makes it easy to write a so-called Single Page App. SPAs work similar to mobile app. The SPA loads once from the server and then runs in the browser. In contrast, traditional webpages load a new page for every “click”. This makes SPAs more responsive and able to create functionality that traditional webpages can’t. These SPAs are running in the browser in a programming language called JavaScript.

One of the popular frameworks for SPAs are ReactJS. To ensure type safety I chose to use Typescript, which is a type-safe language that compiles to JavaScript.

ReactJS allows us to write code like this:

export default class AccountPage extends Page<IAccountProps, IAccountState> {
    //...
    render() {
        return <Card>
            <CardHeader title="Your Account" />
            <CardContent>
                <Table>
                    <TableRow>
                        <TableCell variant={"head"}>
                            E-mail:
                        </TableCell>
                        <TableCell>
                            <span>{this.state.userAccount.email}</span>
                        </TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell variant={"head"}>
                            Password:
                        </TableCell>
                        <TableCell>
                            ******** (secured)
                        </TableCell>
                    </TableRow>
                </Table>
            </CardContent>
        </Card>;
    }
}

If you know the HTML language, this may look similar. In fact, in React you can actually write HTML code, but the upper case tags are actually React components.

React works in a way that each component can have props, which are parameters passed to a component, and it can also have a state, an internal data storage. Whenever either changes, React recalculates the so-called DOM tree, the tree of elements rendered in the browser. The changes are then applied as needed. This is much easier than managing the updates to the UI manually.

To make frontend development easier and not have to reinvent the wheel, we are going to rely on Material-UI, a React implementation of the popular Google Material Design system.

Next up

In the next episode we will be discussing getting started with the backend in Java.

Janos Pasztor

I'm a DevOps engineer with a strong background in both backend development and operations, with a history of hosting and delivering content.

I run an active DevOps and development community on Discord, come in and say hi!

Join the community

Discord

Subscribe

Facebook Facebook Twitter Twitter GitHub GitHub
YouTube YouTube RSS Atom Feed
Do you want more? Click the buttons below!