DEV Community

Daniel Mita
Daniel Mita

Posted on

Building and Deploying a New API (Part 1)

I like to continuously refine my skills and knowledge as a software engineer, dipping my toes into the huge variety of software solutions available, and I've decided to put something together making use of the following tools:

  • OpenAPI, for the specification.
  • oapi-codegen, to generate a server based off the specification.
  • Testify, for straightforward test assertions.
  • Codecov.
  • Docker.
  • PostgreSQL.
  • GORM (mostly because I make seldom use of ORMs and I'm curious).
  • Flyway, for version controlled database migrations.
  • Kubernetes.
  • Terraform.
  • Caddy (probably?).

The API Spec

I'm starting off simple, creating an OpenAPI specification for a user. The user will initially have a name, an email address, and an ID:

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          example: 123
          readOnly: true
        email:
          type: string
          example: me@example.com
          writeOnly: true
        name: 
          type: string
          example: Alice
Enter fullscreen mode Exit fullscreen mode

This schema is used for a POST to /users with a 201 response, and a Problem Details JSON Object is used for a 400 response.

Generating The Code

Moving on to oapi-codegen, a tools.go file has been created as exemplified in its repository. I have also created the file internal/handlers/server.go:

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=server.config.yaml ../../openapi.yaml
//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=types.config.yaml ../../openapi.yaml
package handlers
Enter fullscreen mode Exit fullscreen mode

server.config.yaml is configured for a strict net/http server, and types.config.yaml for the models. A go mod init, followed by a go mod tidy and go generate ./... produces most of what we need to get the new API up and running.

Writing and Testing The Handler

The next steps are adding the following to server.go, creating a server struct which abides by the generated strict server interface:

var _ StrictServerInterface = (*Server)(nil)

type Server struct{}

func NewServer() Server {
    return Server{}
}
Enter fullscreen mode Exit fullscreen mode

Subsequently, a user.go file is created, where a handler for the POST request is created to match the generated interface:

func (*Server) PostUser(ctx context.Context, request PostUserRequestObject) (PostUserResponseObject, error) {
    return nil, nil
}
Enter fullscreen mode Exit fullscreen mode

nil is returned initially, as we're creating user_test.go and using Testify to create tests as we shape the behavior of PostUser. We don't have a DB wired up yet, so the responses will simply be hard-coded structs of what we expect the API to respond with for now to pass the tests.

I'm going to continue hacking away as I write my next blog post. The next steps will likely be building the main.go and Dockerfile to fire up the server, and hooking up Codecov to the repository. The repository can be found here if you would like more context to the snippets given in this post: https://github.com/m-dango/demo-api/tree/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21

Top comments (0)