を構築し、ファームスタックアプリケーションを展開しましょう
あなたは、反応、角度、またはVueと完全なスタックアプリケーションを書いたことがありますか?このチュートリアルでは、ファームスタックをカバーします.FastAPIを構築するためのPythonフレームワークは、よく、高速、APIです.このプロジェクトは、do - doリストです.このチュートリアルは、一般的にVueやAngleのような他のフレームワークにも適用できますが、私は反応を使用します.
プロジェクトを始めるのはとても簡単です.私は、私のCLIの作成ファームアプリケーションを使用して1つ、手動で2つの方法が表示されます.どちらも非常に簡単ですが、すべての自分自身を設定しない場合は、CLIを使用することができます.私はあなたの最初のプロジェクトのために手動でアプリを設定することをお勧めします.
手動設定から始めましょう.
私たちが2つのgit - repos(craによって自動的に初期化されるもの)を初期化している理由は、サブモジュールを利用することです.私はこのセットアップを1つの大きなリポジトリにするのが好きです.このチュートリアルではサブモジュールで展開する方法を紹介しますが、あなたがそれを調べるなら、それらを使用せずに展開する方法を見つけることができます.
Pipenvを使っているなら、PIP依存性をインストールするのはとても簡単です.単にバックエンドフォルダに移動して入力します.
これはとても簡単です.CLIを通してほとんどのものを設定しているので、まだgitサブモジュールを設定する必要があります.
では、これらのサブモジュールを設定しましょう.
つの新しいリモートrepos、フロントエンドのための1つ、バックエンドのための1つ、および完全なアプリケーションの1つを確認します.
フロントエンドとバックエンドローカルreposで、コマンドを実行します.
我々は2012年に開始されます
ここでuvicornコマンドを実行します.
簡単に休憩してデータベースを作るために移動しましょう.あなたはこのアプリを展開する予定はない場合は、単にローカルMongoDBのデータベースを使用することができますが、私は私のアプリを展開するので、私は彼らのクラウドホスティングサービスを使用している.移動するMongoDB Atlas , そして、あなたが以前にこれを使用したならば、新しい口座をセットするか、新しいプロジェクトをつくってください.プロジェクトが作成されたら、“データベースを追加”ボタンを使用して、無料でクラスタを追加することができます.クラスタに名前を付け、それを作成します.それが行われると、“ブラウズコレクション”ボタンを押すと、“todoDatabase”と“todos”という名前の新しいデータベースとコレクションを挿入します.それが今のために必要なことです.
私たちはデータをデータベースにプッシュするために2つのことをする必要があります
さあ、データベースに接続しましょう.
そのためには、最初にdotenvを使ってURIをファイルから取得します.
すべての操作で画面が表示されます.
それらのいずれかで“それを試してみる”が、おそらく追加ToDoのいずれかを開始し、その後、操作を実行することができます.現在の応答を無視し、MonitDBデータベースをビューコレクションセクションで確認します.新しい項目を見る必要がありますが、応答に戻ることができない場合は、ページを開いている場合は、データベースを更新する必要があります.他の操作も試してみるべきですが、もしうまくいけば、フロントエンドで作業を始めることができます.
あなたがどのように反応するかを知っているならば、そして、あなたがAxiosを通してHTTPリクエストを送る方法を知っているならば、私はこのセクションをスキップすることを勧めます.
使っている[email protected]
[email protected] (ノードのバージョンに応じて別のバージョンのノードSASSとSASS LOADを使うことができます. [email protected] ナノイド アクシオス それは私が実際に使用しているライブラリの基本的には、私のテンプレートは、同様に反応ルーターを追加します
すてきなフォルダ構造を設定することから始めましょう.
今我々は我々のアプリを開始することができます.
インデックスを残しましょう.TSXだけで、アプリのためにまっすぐ行く.これは次のようになります.
これは私が行ったスタイルシートです
それでは、コンポーネントを操作しましょう.
完成したアプリは次のようになります.
Toolistは次のようになります.
これが最後です
Herokuを使用してバックエンドを展開し、Githubページをフロントエンドに配置します.私はHerokuと遭遇した唯一の本当の欠点は、それがアイドル状態である場合、バックエンドはもはやアイドル状態になっているときに再起動する必要がありますので、アプリケーションの使用の間に休憩後に長いロード時間を経験することがあります.Githubページは私が問題を抱えたことがない何かです.
あなたはすでに1つを持っていない場合、Herokuに新しいアカウントを作成し、新しいアプリを作成します.Githubを使って展開するのが最も簡単ですが、Heroku Cliを使うなら、より多くのコントロールを得ます.関係なく、これらはあなたが従う必要がある基本的な手順です.
単に新しいファイルを作成する
その後に戻る
Githubにプッシュし、展開し、それを行ってみましょう.それが働くならば、あなたは以前に見た同じルートページを見ることができなければなりません.
アプリの設定に移動し、設定のVarsを明らかにし、
依存関係をインストールしなければならないので、これは少し複雑です
エディット
このソースコードはここでgithubにあります.
https://github.com/jackmaster110/farm-stack-tut
プロジェクト設定
プロジェクトを始めるのはとても簡単です.私は、私のCLIの作成ファームアプリケーションを使用して1つ、手動で2つの方法が表示されます.どちらも非常に簡単ですが、すべての自分自身を設定しない場合は、CLIを使用することができます.私はあなたの最初のプロジェクトのために手動でアプリを設定することをお勧めします.
マニュアル設定
手動設定から始めましょう.
$ mkdir farm-stack-tut
$ cd farm-stack-tut
$ mkdir backend
$ code .
$ git init
$ yarn create react-app frontend --template typescript
$ cd backend
$ git init
$ touch requirements.txt main.py model.py database.py
さあ、必要条件を開きましょう.以下の依存関係を指定します.fastapi == 0.65.1
uvicorn == 0.14.0
motor == 2.4.0
gunicorn == 20.1.0
pymongo[srv] == 3.12.0
我々は、我々が我々のMongoDBアトラスデータベースとGunicornに接続するためにASGIサーバー、モーターとPirmongo[SRV]を走らせることのためにUvicornを必要とします我々がアプリケーションを配備するとき.私たちが2つのgit - repos(craによって自動的に初期化されるもの)を初期化している理由は、サブモジュールを利用することです.私はこのセットアップを1つの大きなリポジトリにするのが好きです.このチュートリアルではサブモジュールで展開する方法を紹介しますが、あなたがそれを調べるなら、それらを使用せずに展開する方法を見つけることができます.
依存関係のインストール
Pipenvを使っているなら、PIP依存性をインストールするのはとても簡単です.単にバックエンドフォルダに移動して入力します.
$ pipenv install -r requirements.txt
テンプレートの設定
これはとても簡単です.CLIを通してほとんどのものを設定しているので、まだgitサブモジュールを設定する必要があります.
$ yarn create farm-app --name=farm-stack-tut
あなたはとにかく名前のポップアップを見るかもしれません、私はそれを修理することに取り組んでいます、しかし、同じ名前を入力するならば、それはうまくいくはずです.Gitセットアップ
では、これらのサブモジュールを設定しましょう.
つの新しいリモートrepos、フロントエンドのための1つ、バックエンドのための1つ、および完全なアプリケーションの1つを確認します.
フロントエンドとバックエンドローカルreposで、コマンドを実行します.
$ git remote add origin <url>
$ git add *
$ git commit -m "first commit"
$ git branch -M main
$ git push -u origin main
メインのrepoでは、これらのコマンドを一度押してください.$ git submodule add <frontend-url> frontend
$ git submodule add <backend-url> backend
その後、変更して、主なリモートrepoに変更をプッシュします.バックエンドAPIの作成
我々は2012年に開始されます
main.py
, このコードを始めるにはfrom fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
origins = ["*"] # This will eventually be changed to only the origins you will use once it's deployed, to secure the app a bit more.
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
@app.get('/')
def get_root():
return {"Ping": "Pong"}
これは、最も基本的な可能なAPIであり、我々は適切にすべてを設定していることを確認するためのテストとして機能します.ここでuvicornコマンドを実行します.
$ uvicorn main:app --reload
あなたがhttp://localhost:8000
, 返されるメッセージを{ ping ": "pong "とします.あなたがそうしたならば、我々はバックエンドの残りを構築し始められることができます.モンゴドル
簡単に休憩してデータベースを作るために移動しましょう.あなたはこのアプリを展開する予定はない場合は、単にローカルMongoDBのデータベースを使用することができますが、私は私のアプリを展開するので、私は彼らのクラウドホスティングサービスを使用している.移動するMongoDB Atlas , そして、あなたが以前にこれを使用したならば、新しい口座をセットするか、新しいプロジェクトをつくってください.プロジェクトが作成されたら、“データベースを追加”ボタンを使用して、無料でクラスタを追加することができます.クラスタに名前を付け、それを作成します.それが行われると、“ブラウズコレクション”ボタンを押すと、“todoDatabase”と“todos”という名前の新しいデータベースとコレクションを挿入します.それが今のために必要なことです.
当社のモデルを作成し、当社のデータベースに接続
私たちはデータをデータベースにプッシュするために2つのことをする必要があります
model.py
. 私たちは3文字列、ナノイド、タイトル、および記述を含みます.そして、それが終わるかどうかチェックするブール値に加えます.モデルは次のようになります.from pydantic import BaseModel
class Todo(BaseModel):
nanoid: str
title: str
desc: str
checked: bool
私たちがしなければならない次のことは、実際に私たちのデータベースに接続しています.これはモーターとピモゴで十分に簡単ですが、アプリケーションを安全にするためには、データベースURIの環境変数を使います.$ pipenv install python-dotenv
バックエンドのルートで作成します.ENVファイルは、データベースURI(これはMongoDBアトラスのConnectをクリックすることで見つけることができます)でこれを置きます.DATABASE_URI = "<URI>"
技術的には、Herokuが我々が展開するとき、環境変数を挿入するのを許すので、我々のローカル・マシンで我々のアプリケーションを働かせ続けることを目的とするだけです、しかし、それはあなたの機密データを隠しておく良い行いです.あなたがすでにいないならば、Aを作ります.gitignore
ファイルを置く.env
インサイド.さあ、データベースに接続しましょう.
そのためには、最初にdotenvを使ってURIをファイルから取得します.
from model import *
import motor.motor_asyncio
from dotenv import dotenv_values
import os
config = dotenv_values(".env")
DATABASE_URI = config.get("DATABASE_URI")
if os.getenv("DATABASE_URI"): DATABASE_URI = os.getenv("DATABASE_URI") #ensures that if we have a system environment variable, it uses that instead
client = motor.motor_asyncio.AsyncIOMotorClient(DATABASE_URI)
今、我々はデータベースとコレクションの変数を作成し、コレクションのデータを変更する機能の束を作ることができます.database = client.TodoDatabase
collection = database.todos
async def fetch_all_todos():
todos = []
cursor = collection.find()
async for doc in cursor:
todos.append(Todo(**doc))
return todos
async def fetch_one_todo(nanoid):
doc = await collection.find_one({"nanoid": nanoid}, {"_id": 0})
return doc
async def create_todo(todo):
doc = todo.dict()
await collection.insert_one(doc)
result = await fetch_one_todo(todo.nanoid)
return result
async def change_todo(nanoid, title, desc, checked):
await collection.update_one({"nanoid": nanoid}, {"$set": {"title": title, "desc": desc, "checked": checked}})
result = await fetch_one_todo(nanoid)
return result
async def remove_todo(nanoid):
await collection.delete_one({"nanoid": nanoid})
return True
これらは我々が必要とするすべての機能ですが、自分自身を追加する自由を感じる.いくつかのHTTP操作を取得しましょうmain.py
:@app.get("/api/get-todo/{nanoid}", response_model=Todo)
async def get_one_todo(nanoid):
todo = await fetch_one_todo(nanoid)
if not todo: raise HTTPException(404)
return todo
@app.get("/api/get-todo")
async def get_todos():
todos = await fetch_all_todos()
if not todos: raise HTTPException(404)
return todos
@app.post("/api/add-todo", response_model=Todo)
async def add_todo(todo: Todo):
result = await create_todo(todo)
if not result: raise HTTPException(400)
return result
@app.put("/api/update-todo/{nanoid}", response_model=Todo)
async def update_todo(todo: Todo):
result = await change_todo(nanoid, title, desc, checked)
if not result: raise HTTPException(400)
return result
@app.delete("/api/delete-todo/{nanoid}")
async def delete_todo(nanoid):
result = await remove_todo(nanoid)
if not result: raise HTTPException(400)
return result
では、次のように動作しますhttp:localhost:8000/docs
そして、それらを試してみる.すべての操作で画面が表示されます.
それらのいずれかで“それを試してみる”が、おそらく追加ToDoのいずれかを開始し、その後、操作を実行することができます.現在の応答を無視し、MonitDBデータベースをビューコレクションセクションで確認します.新しい項目を見る必要がありますが、応答に戻ることができない場合は、ページを開いている場合は、データベースを更新する必要があります.他の操作も試してみるべきですが、もしうまくいけば、フロントエンドで作業を始めることができます.
フロントエンド
あなたがどのように反応するかを知っているならば、そして、あなたがAxiosを通してHTTPリクエストを送る方法を知っているならば、私はこのセクションをスキップすることを勧めます.
ライブラリ
使っている[email protected]
[email protected] (ノードのバージョンに応じて別のバージョンのノードSASSとSASS LOADを使うことができます.
アプリ
すてきなフォルダ構造を設定することから始めましょう.
今我々は我々のアプリを開始することができます.
インデックスを残しましょう.TSXだけで、アプリのためにまっすぐ行く.これは次のようになります.
import React from "react";
import TodoList from "./components/TodoList";
function App() {
return (
<div className="app-container">
<header className="app-header">
<h1>To-Do List</h1>
</header>
<div className="content">
<TodoList />
</div>
</div>
);
}
export default App;
スタイリングをする前に、必要な3つのコンポーネントを設定しましょうTodoList.tsx
, Todo.tsx
, and AddTodo.tsx
. 今のところ、基本的に同じように見えるはずですが、これはこのクラスのように、どうやっているのかによってクラス名のdivだけです.import React from "react";
function Todo() {
return(
<div className="todo-container">
</div>
);
}
export default Todo;
私たちは、これらのコンポーネントを我々のアプリのいくつかのスタイルを定義してみましょう、私はSASSの代わりにSCSSを使用するが、これは簡単にSASS(またはCSSにいくつかの余分な仕事をしたい場合)に適応する必要があります.これは私が行ったスタイルシートです
index.scss
:$primary: #146286;
$secondary: #641486;
$accent: #3066b8;
.app-header {
background-color: $primary;
color: white;
padding: 5px;
border-radius: 10px;
margin-bottom: 5px;
}
.content {
.todo-list-container {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(5, 1fr);
grid-gap: 10px;
.todo-container {
display: flex;
flex-direction: column;
justify-content: space-evenly;
border-radius: 6px;
padding: 10px 6px;
background-color: $secondary;
color: white;
h1 {
font-size: 20px;
}
span {
font-size: 14px;
}
footer {
display: flex;
flex-direction: row-reverse;
}
}
}
}
これは我々がする必要がある唯一のスタイリングでなければなりません、しかし、あなたが望むならば、あなたは若干の余分をすることができます.それでは、コンポーネントを操作しましょう.
完成したアプリは次のようになります.
import { nanoid } from "nanoid";
import React, { useState } from "react";
import { TodoType } from "./components/Todo";
import TodoList from "./components/TodoList";
function App() {
const [todoList, setTodoList] = useState<TodoType[]>([]);
const [title, setTitle] = useState<string>("");
const [desc, setDesc] = useState<string>("");
const changeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.currentTarget.value);
};
const changeDesc = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setDesc(event.currentTarget.value);
}
const changeChecked = (event: React.MouseEvent<HTMLInputElement>, id: string) => {
let temp = [...todoList];
temp.forEach((item) => {
if (item.nanoid === id) {
item.checked = !item.checked;
}
});
setTodoList(temp);
};
const addTodo = (event: React.MouseEvent<HTMLButtonElement>) => {
let newTodo: TodoType = {
nanoid: nanoid(),
title: title,
desc: desc,
checked: false
};
setTodoList([...todoList, newTodo]);
}
return (
<div className="app-container">
<header className="app-header">
<h1>To-Do List</h1>
</header>
<div className="content">
<TodoList submit={addTodo} changeDesc={changeDesc} changeTitle={changeTitle} list={todoList} changeChecked={changeChecked} />
</div>
</div>
);
}
export default App;
これはいくつかの非常に基本的な機能を反応フックを介して木の小道具を渡すために実行されます.Toolistは次のようになります.
import React from "react";
import AddTodo from "./AddTodo";
import Todo, { TodoType } from "./Todo";
interface TodoListProps {
list: TodoType[]
changeChecked: (event: React.MouseEvent<HTMLInputElement>, nanoid: string) => void;
changeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void;
changeDesc: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
submit: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
function TodoList(props: TodoListProps) {
return(
<div className="todo-list-container">
{props.list.map((item) => {
return(
<Todo nanoid={item.nanoid} title={item.title} desc={item.desc} checked={item.checked} changeChecked={props.changeChecked} />
);
})}
<AddTodo changeTitle={props.changeTitle} changeDesc={props.changeDesc} submit={props.submit} />
</div>
);
}
export default TodoList;
todoは次のようになります.import React from "react";
export type TodoType = {
nanoid: string;
title: string;
desc: string;
checked: boolean;
}
interface TodoProps extends TodoType {
changeChecked: (event: React.MouseEvent<HTMLInputElement>, nanoid: string) => void;
}
function Todo(props: TodoProps) {
return(
<div className="todo-container">
<h1>{props.title}</h1>
<span>{props.desc}</span>
<footer>
<input type="checkbox" checked={props.checked} onClick={(e) => props.changeChecked(e, props.nanoid)} />
</footer>
</div>
);
}
export default Todo;
最後に、addtodoは次のようになります.import React from "react";
interface AddTodoProps {
submit: (event: React.MouseEvent<HTMLButtonElement>) => void;
changeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void;
changeDesc: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
}
function AddTodo(props: AddTodoProps) {
return(
<div className="todo-container add-todo-container">
<input type="text" className="title" placeholder="Title..." onChange={props.changeTitle} />
<textarea className="desc" placeholder="Description..." onChange={props.changeDesc}>
</textarea>
<button className="submit" onClick={props.submit}>Add Todo</button>
</div>
);
}
export default AddTodo;
今すぐ使用する時間ですuseEffect()
そして、Axiosはデータベース内のすべてのデータを保存します.これが最後です
App.tsx
:import axios from "axios";
import { nanoid } from "nanoid";
import React, { useEffect, useState } from "react";
import { TodoType } from "./components/Todo";
import TodoList from "./components/TodoList";
function App() {
const [todoList, setTodoList] = useState<TodoType[]>([]);
const [title, setTitle] = useState<string>("");
const [desc, setDesc] = useState<string>("");
useEffect(() => {
axios
.get(process.env.REACT_APP_BACKEND_URL + "/api/get-todo")
.then((res) => {
setTodoList(res.data);
});
}, []);
const changeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.currentTarget.value);
};
const changeDesc = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setDesc(event.currentTarget.value);
};
const changeChecked = (
event: React.MouseEvent<HTMLInputElement>,
id: string
) => {
let temp = [...todoList];
let tempIndex = 0;
temp.forEach((item, i) => {
if (item.nanoid === id) {
item.checked = !item.checked;
tempIndex = i;
}
});
setTodoList(temp);
let item = todoList[tempIndex];
axios.put(
process.env.REACT_APP_BACKEND_URL +
`/api/update-todo/${item.nanoid}`,
{ nanoid: item.nanoid, title: item.title, desc: item.desc, checked: item.checked}
);
};
const addTodo = (event: React.MouseEvent<HTMLButtonElement>) => {
let newTodo: TodoType = {
nanoid: nanoid(),
title: title,
desc: desc,
checked: false,
};
setTodoList([...todoList, newTodo]);
axios.post(
process.env.REACT_APP_BACKEND_URL + "/api/add-todo",
JSON.stringify(newTodo)
);
};
return (
<div className="app-container">
<header className="app-header">
<h1>To-Do List</h1>
</header>
<div className="content">
<TodoList
submit={addTodo}
changeDesc={changeDesc}
changeTitle={changeTitle}
list={todoList}
changeChecked={changeChecked}
/>
</div>
</div>
);
}
export default App;
それが完了したら、我々はアプリケーションを展開する準備ができます.展開
Herokuを使用してバックエンドを展開し、Githubページをフロントエンドに配置します.私はHerokuと遭遇した唯一の本当の欠点は、それがアイドル状態である場合、バックエンドはもはやアイドル状態になっているときに再起動する必要がありますので、アプリケーションの使用の間に休憩後に長いロード時間を経験することがあります.Githubページは私が問題を抱えたことがない何かです.
バックエンド展開
あなたはすでに1つを持っていない場合、Herokuに新しいアカウントを作成し、新しいアプリを作成します.Githubを使って展開するのが最も簡単ですが、Heroku Cliを使うなら、より多くのコントロールを得ます.関係なく、これらはあなたが従う必要がある基本的な手順です.
単に新しいファイルを作成する
Procfile
バックエンドのルートでこれを入れます.web: gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
また、必ずpython-dotenv == 0.19.0
あなたにrequirements.txt
ファイルを再インストールし、すべてのブートを確実にする依存関係を再インストールします.その後に戻る
main.py
, を置換し、"*"
を指定します"https://<username>.github.io"
.Githubにプッシュし、展開し、それを行ってみましょう.それが働くならば、あなたは以前に見た同じルートページを見ることができなければなりません.
アプリの設定に移動し、設定のVarsを明らかにし、
DATABASE_URI
で設定します.フロントエンド展開
依存関係をインストールしなければならないので、これは少し複雑です
package.json
, しかし、それはまだかなりまっすぐ前方です.エディット
.env
'sバックエンドURLはHerokuアプリURLで、コミットしてプッシュします.$ yarn add --dev gh-pages
その後、開くことができますpackage.json
, そして、"scripts"
:"predeploy": "yarn build",
"deploy": "REACT_APP_BACKEND_URL=<backend-url> gh-pages -d build"
また、"homepage": "https://<username>.github.io/<project-name>-frontend/"
Githubでは、バックエンドURLと同じ環境変数として機能する秘密を追加し、同じ名前にしてください.$ yarn start
^C
$ yarn deploy
すべてうまくいけば、100 %の作業アプリを持っている必要があります.このソースコードはここでgithubにあります.
https://github.com/jackmaster110/farm-stack-tut
Reference
この問題について(を構築し、ファームスタックアプリケーションを展開しましょう), 我々は、より多くの情報をここで見つけました https://dev.to/sammyshear/let-s-build-and-deploy-a-farm-stack-app-epoテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol