AIエディタ「Replit」でFastAPIを構築してみた

# Replitとは
ブラウザベースのIDEで、[公式サイト](https://docs.replit.com/)で発行したアカウントを使用してワークスペースにアクセスできる。
無料アカウントもあるが、有料にすることで以下のような開発サポートを受けられる
- 発展的なAIによる開発サポート(無料でもAIサポートは受けられる)
- 多様な言語やフレームワークへの対応
- テンプレートによる高速開発支援
- プライベートプロジェクト作り放題

また、単なるIDEにとどまらずreplitのドメインでデプロイもできるっぽい。アプリの基盤やライフサイクルも管理できると理解してもいいのかも。

## アカウント発行・AI機能

まず、公式サイトから流れに従ってアカウント取得。
下図は無料版
![repl-starter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/f89112a5-f38f-ff3d-bbfb-3ce3aa763a24.png)

有料版にグレードアップしてみる。年間契約のほうがお得らしい。
まあきっと使うでしょのノリでやってみる。
![repl-upgrade-payment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/559db378-de8e-b2dd-8abc-969f2a7a2ab2.png)

グレードアップ後
AI開発支援も、自動デプロイも、ワークスペースのリソース設定も共有もできるよとのこと。
![repl-guide1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/440d5b0f-3ab3-7ea3-05f7-8494593e0a3a.png)

![repl-guide2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/760d134b-75d8-b615-7e1e-7f185164fdad.png)

![repl-guide3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/cb9cd421-c2ab-1bff-d02f-8e096b3bf631.png)


## FastAPIを構築

実際にAPIを開発してみる。
まずは「Create Repl」ボタン押下
![repl-core1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/706bfed0-43c9-b74c-46c3-4cc9bb104a87.png)

![repl-core2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/97c3786f-c6aa-2f50-9229-97c18a8dfa50.png)

テンプレート選択直後の画面
ライブラリの追加は左のサイドバーのpyproject.tomlに記述し、右のサイドバーのConsoleでRunボタン押下でライブラリインストール～API起動まで実施できる
![repl-core3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/2ab1a0ca-21a4-7610-bdf5-3a76cb0f19d7.png)

### コード生成、修正作業

以下の日本語文を与えてみる
```
以下の設計書が与えられたとき、まずStep1とStep2の機能を実装してください。

設計書: オンライン書店の注文管理システム
1. 概要
このシステムは、オンライン書店での注文管理および在庫管理を行うものです。主な機能は以下のとおりです。

Step 1: 書籍の在庫管理と注文の受付
Step 2: 配送待ち行列の管理
Step 3: 配送完了時の顧客への通知管理
ユーザーからの注文に応じて在庫を確認し、配送ステータスを更新します。複数の配送オペレーションを並行して行うため、待ち行列や配送の進捗管理が求められます。

2. クラス設計
2.1 クラス Book
概要: 書籍の情報と在庫状態を管理します。
属性:
book_id: 書籍のID（str）
title: 書籍のタイトル（str）
initial_stock: 初期在庫数（int）
price: 書籍の価格（int）
remaining_stock: 現在の在庫数（int）
メソッド:
__init__(self, book_id, title, initial_stock, price): ID、タイトル、初期在庫、価格を設定し、remaining_stockを初期在庫数と同値に設定。
display_info(self): 書籍情報を出力するメソッド。
2.2 クラス Order
概要: 注文の詳細を管理します。
属性:
order_id: 注文ID（str）
customer_id: 顧客ID（str）
book_id: 注文された書籍のID（str）
quantity: 注文数量（int）
メソッド:
__init__(self, order_id, customer_id, book_id, quantity): 注文ID、顧客ID、書籍ID、注文数量を初期化します。
2.3 クラス Customer
概要: 顧客情報とその注文履歴を管理します。
属性:
customer_id: 顧客ID（str）
name: 顧客名（str）
order_history: 顧客の注文履歴（list）
メソッド:
__init__(self, customer_id, name): 顧客IDと名前を初期化し、注文履歴を空のリストで初期化。
```
結果
main.py
```

from fastapi import FastAPI

from model import Book, Customer, Order

app = FastAPI()

@app.post("/order")
async def place_order(order_data: Order):
    """
    注文を受け付けるAPI。
    """
    book = Book.get_book_by_id(order_data.book_id)
    if book is None:
        return {"status": "error", "message": "書籍が見つかりません"}

    order = Order(
        order_id=order_data.order_id,
        customer_id=order_data.customer_id,
        book_id=order_data.book_id,
        quantity=order_data.quantity)
    order.place_order(book)

    return {"status": "success", "message": "注文が完了しました"}

@app.get("/customers/{customer_id}")
async def get_customer(customer_id: str):
    """
    顧客情報を取得するAPI。
    """
    # 実際の処理ではデータベースやファイルから顧客オブジェクトを取得
    # ここではサンプルとしてダミーデータを返す
    customer = Customer(customer_id=customer_id, name="田中太郎", order_history=[])
    return {"customer": customer}
```

model.py
```
from pydantic import BaseModel


class Book(BaseModel):
  """書籍の情報と在庫状態を管理します。"""
  book_id: str
  title: str
  initial_stock: int
  price: int
  remaining_stock: int

  def update_stock(self, quantity: int):
      """在庫を更新するメソッド。"""
      self.remaining_stock -= quantity

  @staticmethod
  def get_book_by_id(book_id: str):
      """書籍IDから書籍オブジェクトを取得します。"""
      # 実際の処理ではデータベースやファイルから書籍オブジェクトを取得
      # ここではサンプルとしてダミーデータを返す
      if book_id == "B001":
          return Book(
              book_id="B001", 
              title="Python入門", 
              initial_stock=10, 
              price=2000, 
              remaining_stock=10)
      else:
          return None

class Order(BaseModel):
  """注文の詳細を管理します。"""
  order_id: str
  customer_id: str
  book_id: str
  quantity: int

  def place_order(self, book: Book):
      """注文を実行するメソッド。"""
      if book.remaining_stock >= self.quantity:
          book.update_stock(self.quantity)
          print(f"注文が完了しました。注文ID: {self.order_id}")
      else:
          print(f"在庫が不足しています。注文ID: {self.order_id}")

class Customer(BaseModel):
  """顧客情報とその注文履歴を管理します。"""
  customer_id: str
  name: str
  order_history: list[Order] = []
```

ちょっと気になる部分はあるものの、この程度のお題ならGPT4-oを使っていることもあって出力内容はそんなに問題なさそう。

実際に修正しているときの感じ

![repl-core4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/afe5300f-9797-8e0e-a5d7-7bffcffb63c4.png)

ローカルでFastAPI起動すると、サイト内ブラウザでプレビューできる。
使用した感じDevツールのボタンが機能してない様子。
![repl-core5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/569ee9ef-fba3-0eaa-a381-2942cb3a5e34.png)

### KeyValue系Database

replitが管理しているKeyValue形式のデータベースを無料で使えるらしい
![スクリーンショット 2024-11-10 003256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/70f0438f-6df6-61b4-569a-458b9586876b.png)

![スクリーンショット 2024-11-10 003313.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/265aa6e4-ef9e-c7a9-bb3a-c9ed6f5a7669.png)


### デプロイ機能

４つのモードから選んでデプロイできる
![スクリーンショット 2024-11-10 004303.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/1aed55c8-7aed-b328-6fa8-96c9706707d5.png)

デプロイまでに、環境変数やドメインなどを設定可能の様子。
![スクリーンショット 2024-11-10 004730.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1911348/20cdbd36-7c66-31c5-c97b-cf63850c1f7f.png)

## 終わりに

- 集団で開発していて重くならないのかは気になる
- 今後NextJSも試してみたい
- ローカルのvscode, cursorからssh接続できたりもするみたい（自分はエラーで接続できなかった）
- postgresqlのマネージ、オブジェクトストレージの利用、他ユーザーの開発環境への招待などいろいろできることは多そう
- 実際にデプロイするとどうなるんだろう