End-to-End Apps with Claude Code, Claude Sonnet 3.7

Published: Mar 11, 2025 by Isaac Johnson

Claude Code has gotten a lot of buzz since Anthropic quietly announced it via a blog post Feb 24th along with Claude Sonnet 3.7 (skipping 3.6 altogether).

I wanted to play with both and I will call out the “Research Preview” of Claude Code does require ‘approval’, so you’ll want to request access via the Anthropic Console (for reference, took me a bit over a day).

I’ll cover all that and more as we build out a full Python app and release it using both Claude Code, Claude Sonnet 3.5 and 3.7 via VS Code, and other AI tools.

The goal is to end-to-end build an app from scratch (and I’m going to focus on building a Meeting app that I could use to help manage a weekly morning men’s group of which I take part and we get by with Google calendar)

Claude Code

We need to have at least NodeJS 18.x or higher

builder@LuiGi:~/Workspaces/flaskAppBase$ nvm list
       v16.20.2
       v18.20.2
->      v21.7.3
         system
default -> stable (-> v21.7.3)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v21.7.3) (default)
stable -> 21.7 (-> v21.7.3) (default)
lts/* -> lts/iron (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.3 (-> N/A)
lts/gallium -> v16.20.2
lts/hydrogen -> v18.20.4 (-> N/A)
lts/iron -> v20.15.1 (-> N/A)

If we want enhanced search, we need rg

builder@LuiGi:~/Workspaces/flaskAppBase$ which rg
/home/linuxbrew/.linuxbrew/bin/rg

We can install with npm

builder@LuiGi:~/Workspaces/flaskAppBase$ npm install -g @anthropic-ai/claude-code

added 3 packages in 8s

2 packages are looking for funding
  run `npm fund` for details

I then just launched claude in my checked out Forgejo repo

/content/images/2025/03/claudecode-01.png

Next, I need to log into my Anthropic account

/content/images/2025/03/claudecode-02.png

Launching with WSL pops up these Firefox

/content/images/2025/03/claudecode-03.png

I authed, but was told at this time they were at capacity

/content/images/2025/03/claudecode-04.png

That night I saw the email that “my spot was confirmed”

/content/images/2025/03/claudecode-16.png

Claude via VS Code

I can try using Claude via VS Code using the Sonnet 3.5 preview model. It’s not the new 3.7, but it is quite excellent

/content/images/2025/03/claudecode-05.png

I’ll see if it can add a team page

@workspace Can you add a picker for teams after the user logs in? They can pick from existing teams or create a new team. Store team membership in the database and show Team membership when the user logs in (if they are in a team).

It came back with some updates

/content/images/2025/03/claudecode-06.png

I can now see a new “manage teams” after I create an account

/content/images/2025/03/claudecode-07.png

Clicking that lets me join or add a team

/content/images/2025/03/claudecode-08.png

I can create a new team

/content/images/2025/03/claudecode-09.png

I’m redirected back to the page that now shows my team membership

/content/images/2025/03/claudecode-10.png

I can use another browser to test joining the team

/content/images/2025/03/claudecode-11.png

I pushed that commit and asked Claude 3.5 via Copilot to now add an admin user

can you add a default admin user with password password123. This admin user, when logging in, should see a table of all users with their team membership as well as all teams. the user and teams table should be sortable.

And I can see the new generated code

/content/images/2025/03/claudecode-12.png

It did create an admin user which showed teams

/content/images/2025/03/claudecode-13.png

I realized I have a left a gap in that users cannot change passwords. Having an admin user with a fixed password that cannot be changed would be a rather big miss.

The next updates worked quite well, even if I’m not keen on the UI sizing of the password change

/content/images/2025/03/claudecode-14.png

While I didn’t ask for it, the admin dashboard lets me reset other user’s passwords which is a handy feature

/content/images/2025/03/claudecode-15.png

Picking back up with Claude Code

Within a day or so I saw I was granted a spot

/content/images/2025/03/claudecode-17.png

A bit of cleverness here is that yes, I have access, and 7 days to use it. However, to use it I have to move to the paid tier of Claude.

/content/images/2025/03/claudecode-18.png

Let’s see how far US$10 takes me

/content/images/2025/03/claudecode-19.png

Looks like I’m in

/content/images/2025/03/claudecode-20.png

Now I can login with Claude Code

/content/images/2025/03/claudecode-21.png

I get some security notes then tips for usage

/content/images/2025/03/claudecode-22.png

I confirm I trust files in the folder

/content/images/2025/03/claudecode-23.png

Asking for some UI changes

First, I’ll setup an Init file

/content/images/2025/03/claudecode-24.png

It then basically read and reviewed my code to build a logical markdown of style guidelines and usage. Pretty clever

/content/images/2025/03/claudecode-25.png

Saving it, as I would expect, I saw CLAUDE.md get created in my workspace

/content/images/2025/03/claudecode-26.png

Let’s ask it to improve the Password block

/content/images/2025/03/claudecode-27.png

Let’s see how it does

Since I have this in GIT, I can see the changes reflected

/content/images/2025/03/claudecode-29.png

I fired a build and tested - looks good

/content/images/2025/03/claudecode-30.png

I’m pretty good with this base. I think I’ll make a branch and start to build an App with it and see how far it goes.

Calendar and Assignments

I’m going to push it to make a calendar and assignment section. It’s a pretty big ask, let’s see how it does:

On first look, seems nice

/content/images/2025/03/claudecode-32.png

I can pick specific days, but it does not re-occur

/content/images/2025/03/claudecode-33.png

The other issue is that when I join a team, it does not share the schedule with team members, so it must be stored on the user, not team

/content/images/2025/03/claudecode-34.png

I can then ask Claude via Cursor AI to create an ICS calendar

/content/images/2025/03/claudecode-35.png

I can let it setup local python and test it without Docker which is nice

/content/images/2025/03/claudecode-36.png

Not only did Claude Code fix the Team scheduling aspect of the calendar, it did a fantastic job create a working ICS

/content/images/2025/03/claudecode-37.png

I can see we need to likely to add iCalendar to our requirements as Claude Code did it on the fly

...
> That worked great, thanks.  Can you create a per-team ICS format calendar that I could use in google calender.

● I'll create a per-team ICS calendar format for Google Calendar integration. Let me implement this feature.

● Read(file_path: "app.py", limit: 50, offset: 0)…
  ⎿  from flask import Flask, session, request, jsonify, redirect, url_for, render_template, send_from_directory
     from flask_sqlalchemy import SQLAlchemy
     import os
     ... (+47 lines)

● Bash(pip3 install icalendar --break-system-packages)…
  ⎿  Collecting icalendar
       Downloading icalendar-6.1.1-py3-none-any.whl.metadata (10 kB)
     Collecting python-dateutil (from icalendar)
       Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB)
     Collecting tzdata (from icalendar)
       Downloading tzdata-2025.1-py2.py3-none-any.whl.metadata (1.4 kB)
     Collecting six>=1.5 (from python-dateutil->icalendar)
       Downloading six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
     Downloading icalendar-6.1.1-py3-none-any.whl (200 kB)
     Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
     Downloading tzdata-2025.1-py2.py3-none-any.whl (346 kB)
     Downloading six-1.17.0-py2.py3-none-any.whl (11 kB)
     Installing collected packages: tzdata, six, python-dateutil, icalendar
     Successfully installed icalendar-6.1.1 python-dateutil-2.9.0.post0 six-1.17.0 tzdata-2025.1

● Now I'll implement the calendar export functionality by adding a new route to generate ICS files for teams. Let's add this to our app.py file:

● Update(file_path: app.py)…
  ⎿ Updated app.py with 4 additions and 1 removal
      1  from flask import Flask, session, request, jsonify, redirect, url_for, render_template, send_from_directory
      1  from flask import Flask, session, request, jsonify, redirect, url_for, render_template, send_from_directory, Response
      2  from flask_sqlalchemy import SQLAlchemy
      3  import os
      4  import datetime
      5  import calendar
...

I can use Copilot to quickly get the requirements.txt file Updated

/content/images/2025/03/claudecode-38.png

Out of habit I started to use Copilot to add a mount point to the Dockerfile and a local DB spot as I wanted to use a PVC when the time came for Kubernetes.

/content/images/2025/03/claudecode-39.png

But I stopped myself and went back to Claude to add the next part - some kind of password for the Team.

What dawned on me is that if I expose this, anyone could join any team and that would not be desired.

Here we can see how Claude Code handles this ask and a quick test showing we need to deal with our existing database

That worked great

/content/images/2025/03/claudecode-40.png

Now I want the ability to leave teams and add notes. As this took a bit more thinking, I paused the recording at times

I can now see edit notes available

/content/images/2025/03/claudecode-41.png

And again, the test worked great

Now if I really want to test this, I need to create a build and release file I can use with Kubernetes.

If this works, I’ll set up a CICD pipeline, but to stat with, I just want to push it up to Dockerhub so i can test it

builder@LuiGi:~/Workspaces/flaskAppBase$ docker tag mytest123:1.5 idjohnson/devotionapp:1.5
builder@LuiGi:~/Workspaces/flaskAppBase$ docker push idjohnson/devotionapp:1.5
The push refers to repository [docker.io/idjohnson/devotionapp]
1361a1f741f3: Pushed 
f22b09a73a21: Pushed 
4bf371b00791: Pushed 
522dac915f0a: Pushed 
597792c21051: Pushed 
cb8559ac3925: Pushed 
5da80621c140: Pushed 
ee6e0d2a97f1: Pushed 
6022e9b5727d: Mounted from idjohnson/k8sresourceviewer 
e0dfbff797f9: Mounted from idjohnson/k8sresourceviewer 
0eaf13317391: Mounted from idjohnson/k8sresourceviewer 
7914c8f600f5: Mounted from idjohnson/k8sresourceviewer 
1.5: digest: sha256:34d4bd4f4600e5185de682412db786078bd3d1c26d7f317e5d0018c2208c99e5 size: 2823

/content/images/2025/03/claudecode-44.png

I could have fired up Claude Code to create the Dockerfile. But why pay for that? I know Copilot can kick that out in a heartbeat

/content/images/2025/03/claudecode-45.png

I can now test

$ kubectl apply -f k8s-deployment.yaml
persistentvolumeclaim/devotion-db-pvc created
service/devotion-app-service created
deployment.apps/devotion-app created

The Deployment was right, but I missed the type on the service so it’s trying to make a LoadBalancer instead of a ClusterIp

builder@LuiGi:~/Workspaces/flaskAppBase$ kubectl get po -l app=devotion-app
NAME                            READY   STATUS    RESTARTS   AGE
devotion-app-67c9b94f46-w7qns   1/1     Running   0          72s
builder@LuiGi:~/Workspaces/flaskAppBase$ kubectl get svc devotion-app-service
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
devotion-app-service   LoadBalancer   10.43.203.159   <pending>     80:30268/TCP   74s

That was just a quick fix to type

apiVersion: v1
kind: Service
metadata:
  name: devotion-app-service
spec:
  selector:
    app: devotion-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 5000
  type: ClusterIP

I might have the idea this would help my Men’s Group meetings, but it really could be used for any kind of recurring meetup.

I’ll create a quick DNS record for “meet” in my Azure DNS

$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 75.73.224.240 -n meet
{
  "ARecords": [
    {
      "ipv4Address": "75.73.224.240"
    }
  ],
  "TTL": 3600,
  "etag": "5c3eca87-ba80-4ac3-b729-2e6dff4e8960",
  "fqdn": "meet.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/meet",
  "name": "meet",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}

Then I just need to create an Ingress and launch it

builder@LuiGi:~/Workspaces/flaskAppBase$ cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
  generation: 1
  name: meet
spec:
  ingressClassName: nginx
  rules:
  - host: meet.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: devotion-app-service
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - meet.tpk.pw
    secretName: meet.tpk.pw-tls
builder@LuiGi:~/Workspaces/flaskAppBase$ kubectl apply -f ./ingress.yaml
ingress.networking.k8s.io/meet created

That didnt work, let me rework it

builder@LuiGi:~/Workspaces/flaskAppBase$ cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
  name: meetingress
spec:
  rules:
  - host: meet.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: devotion-app-service
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - meet.tpk.pw
    secretName: meet-tls
builder@LuiGi:~/Workspaces/flaskAppBase$ kubectl apply -f ./ingress.yaml
ingress.networking.k8s.io/meetingress created

I realized the issue is this ClusterIssuer was 361d old and that prompted me to check to see if the SP had an expired credential.

Indeed, I had set this Service Principals secret to expire in a year and that just happened

/content/images/2025/03/claudecode-46.png

That time it worked

$ kubectl get cert meet-tls
NAME       READY   SECRET     AGE
meet-tls   True    meet-tls   92s

Testing

We can test the login and see our iCal link is now showing

One issue (and it took testing on my phone to figure out), is that the ICS file is a static grouping of specific date entries that can import as events in an existing calendar, but doesn’t really work as a “remote” calendar that would sync.

Adaptive UI

Another issue I have from using a very basic flask App UI is a lack of adaptive UI. That is, it doesn’t optimize for mobile when that is available.

The login works, but it’s awfully small

/content/images/2025/03/claudecode-48.jpg

though the main app is okay

/content/images/2025/03/claudecode-49.jpg

Let’s try and fix both

The calendar ics file does not have a proper end tag (END:VCALENDAR). please fix the ics link so i can use it with google calendar. Also, the login page does not have responsive design so it does not format properly for mobile. Can you update the design to work better for mobile devices.

/content/images/2025/03/claudecode-50.png

I wanted to set up a basic CICD pipeline. I decided to try building a pipeline in AzDO that can pull from Forgejo and push to my Harbor

/content/images/2025/03/claudecode-51.png

and I can verify it pushed to Harbor

/content/images/2025/03/claudecode-52.png

The login works fine for the web

/content/images/2025/03/claudecode-54.png

But now the phone’s size is proper

/content/images/2025/03/claudecode-55.jpg

I tested the calender and it works great!

/content/images/2025/03/claudecode-53.png

I asked to break out notes and make the landing page have responsive design as well.

It is now much more full screen on the computer

/content/images/2025/03/claudecode-56.png

and it looks great on mobile as well

API

I had spent some considerable time letting Claude work on creating an API I could use with REST.

/content/images/2025/03/claudecode-59.png

It did not work at first, but with some work, I managed to figure it out.

At this point, for all the work I had Claude Code do to build this app, I was curious what kind of costs I might have incurred

/content/images/2025/03/claudecode-58.png

It does seem an okay price but I might avoid using Claude for as much in the future as I think some of my work could have been done just as well with Copilot feature

Summary

So I finished to the point I was happy enough to share with my men’s group. I asked my wife to look at my app, and she was nice enough, but then just said “yeah, i mean, moms just use signup genius”. I had no idea what that app was. Perhaps that is a my fault for spending too much doing AI work and only really looking at social networks like LinkedIn and Meetup.

That said, I am pretty happy with this app and the value Claude Code provided. I feel it was effective, but I’m not sure about the pricing. It’s not so amazing I’m willing to go to Claude Code first each time. I might save it for when all the rest fail me.

Even as I was writing the summary I was stewing on using the rest of my credits to build out the Android and iOS accompanying apps if I wanted to go mobile native and even started the work locally.

However, I also realized I missed a favicon, so i used favicon.io to create a quick copy of my logo as a fav icon then asked Copilot (Free) to solve it - it was an “easy” ask (and my lazy butt could have just done it as well - i didn’t need AI for that).

/content/images/2025/03/claudecode-60.png

As it stands, this app cost me about US$5.50 to build in Anthropic money (A few cents of that Claude Code bill was likely spent demoing to colleagues). The question is was it worth it?

I might think differently - if some tech buddy helped code as much as Claude did this time, would I buy my buddy Claude a fancy Starbucks drink for about the same amount? Damn straight I would. So yeah - I guess I think it’s priced fair.

AI Claude Python ClaudeCode

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes