Embarking on a Cloud-native Journey with a Todo API
Tags: cloudnative, aiassisted, todo, api
Published: 2023-11-11 19:49:29

---
title: Embarking on a Cloud-native Journey with a Todo API
published: true
date: 2023-11-10 00:00:00 UTC
tags: Cloudnative,AIassisted,todo,api
canonical_url: http://dfberry.github.io/2023-11-11-cloud-native-api.md
---

Our cloud-native adventure begins with the API layer - the magical bridge between the front-end UI and the back-end services. For our Todo project, we're keeping the API simple and efficient. Express.js is our chosen framework, a tried-and-true Node.js project. With the power of Copilot Chat, we'll be speeding through the process in no time!

[How do you typically approach building a new API for a cloud-native project?](https://bit.ly/3SBV3zx)

## Fire Up the Dev Container[​](#fire-up-the-dev-container "Direct link to Fire Up the Dev Container")

In the previous chapter of our journey, [002-developer-environment-setup](https://github.com/dfberry/cloud-native-todo/tree/002-developer-environment-setup), we set up a robust dev environment. Now, it's time to bring it to life! Open it in GitHub Codespaces or locally on your computer with Visual Studio Code (Docker installation required).

Whether you're the lead developer or part of a team, whether you're working on a familiar project or exploring new territories, you've got options. Visual Studio Code and the dev container for local work, or Codespaces for a cloud-based approach.

## Stay in the dev container[​](#stay-in-the-dev-container "Direct link to Stay in the dev container")

If you are like me, your local computer may not be a workhorse so docker may not be started when you start working on your project. You can start using Copilot chap locally, then realize you need the dev container for something. The Copilot chat stays with the environment, it doesn't move (at this time). If you are 20 questions into your conversation with a few side trips here and there, switching environments and not having the chat to reference is frustrating.

If you are using dev containers and Copilot chat, start and stay in the container for the entire conversation.

## Building an API with Copilot Chat[​](#building-an-api-with-copilot-chat "Direct link to Building an API with Copilot Chat")

In just half an hour, Copilot Chat helped me create a fully functional API, complete with types, linting, tests, and a build-test workflow. Here's a sneak peek into the prompts I used:

1. Building a todo microservice with Node.js and TypeScript.
2. Adding tests for the todo API.
3. Refactoring server.ts for both server and test.
4. Modifying server.ts for CRUD operations.
5. Providing initial sample data.
6. Creating an OpenAPI yaml for the API.
7. Adding an OpenAPI UI route.
8. Setting up ESLint with Prettier.
9. Deciding .gitignore contents.
10. Moving openapi.yaml to the dist folder using tsc.
11. Creating a GitHub action for linting, building, and testing.
12. Identifying missing microservice elements.
13. Adding type safety.

## Refining Types and Refactoring[​](#refining-types-and-refactoring "Direct link to Refining Types and Refactoring")

There was some back-and-forth over types and refactoring. Copilot shone in evaluating incoming API request data. After a few prompts, the validation looked like this:

```
export interface Todo {
  id: number;
  title: string;
}

export interface PartialTodo {
  id?: unknown;
  title?: unknown;
}

const todoSchema = Joi.object({
  id: Joi.number().greater(0).required(),
  title: Joi.string().min(1).max(1000).required(),
}).unknown(false);

const todoPartialSchema = Joi.object({
  id: Joi.number().greater(0),
  title: Joi.string().min(1).max(1000).required(),
}).unknown(false);

export interface TodoValidation {
  valid: boolean;
  error: Error | null | string | ValidationErrorItem[];
  todo: Todo | PartialTodo | null;
}

export const validateTodo = (
  todo: PartialTodo,
  isNewTodo: boolean = false
): TodoValidation => {
  const schema = isNewTodo ? todoPartialSchema : todoSchema;
  const { error } = schema.validate(todo);
  if (error) {
    return {
      valid: false,
      error: error.details,
      todo: null,
    };
  }
  return { valid: true, error: null, todo };
};
```

<button type="button" aria-label="Copy code to clipboard" title="Copy"><span aria-hidden="true"><svg viewbox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewbox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button>

Copilot also excelled in creating logging handlers for requests and responses, and in adding those handlers to the route.

## Copilot's Strengths[​](#copilots-strengths "Direct link to Copilot's Strengths")

Copilot gave me a flying start. While some answers lacked details, running the app or tests quickly revealed any errors, which were easy to fix.

## Copilot's Weaknesses[​](#copilots-weaknesses "Direct link to Copilot's Weaknesses")

Despite the conversation and the wealth of examples, I encountered more issues than expected. For more obscure subjects, I'd recommend breaking down the steps more atomically.

## Why Not Use Existing Code?[​](#why-not-use-existing-code "Direct link to Why Not Use Existing Code?")

Sure, there are plenty of examples on GitHub. But navigating licenses and attributions can be tricky. I preferred to avoid any potential missteps.

## Time Investment[​](#time-investment "Direct link to Time Investment")

From start to finish, the project took about 2 hours. There were a few hiccups along the way, but each step was small, making issues manageable. Considering everything, 2 hours is a solid benchmark for a proof-of-concept project.