--- tags: React, FastAPI, MongoDB --- # React & FastAPI & MongoDB ## pre-requirement - MongoDB - NPM - Python 10 ## Environment ### MongoDB [MongoDB]([MongoDB](https://www.mongodb.com/)) `mongod --version` `mongodsh` : mongodb shell (for commands) `show dbs` : checkout all dbs ### React `npx create-react-app [app-name]` `npm start`: start server at localhost:3000 `npm install axios bootstrap` ### FastAPI A fast(both performance and develop) tool for building APIs with built-in swagger doc. *install in virtual env* `fastapi`: FastAPI `uvicorn`: ASGI web server for python `motor`: coroutine-based API for non-blocking access to MongoDB from Tornado or asyncio. ## Build ### Backend #### CORSMiddleware `CORS`: Cross Origin Resourse Sharing Request should be in same domain for safety reason. An application should open accessibility for certain domains that are different from itself. ```python app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=origin_list, allow_credentials=True, allow_methods=['*'], allow_headers=['*'] ) ``` #### A simple get ```python @app.get("/") def read_root(): return {"Ping":"Pong"} ``` #### A simple CRUD of todos ```python @app.get("/api/todo") def get_todos(): return [todos] @app.get("/api/todo/{id}") async def get_todo_by_id(id): return [todo] # create @app.post("/api/todo") async def post_todo(todo): return [todo] @app.put("/api/todo/{id}") async def put_todo(todo): return [todo] @app.delete("/api/todo/{id}") async def delete_todo(todo): return [todo] ``` #### Build todo model `pydantic`: A tool to validate and setup data types through type annotations. It enforces type hints at runtime and provides user friendly errors when data is invalid. [pydantic docs](https://pydantic-docs.helpmanual.io/) ```python from pydantic import BaseModel class Todo(BaseModel): title: str description: str ``` #### Database connection [motor docs](https://pydantic-docs.helpmanual.io/) ```python from motor import motor.motor_asyncio client = motor.motor_asyncio.AsyncIOMotorClient("mongodb://localhost:27017") database = client.TodoList collection = database.todo ``` #### Database query query format follows [mongodb operations](https://www.mongodb.com/docs/manual/crud/) ```python # database.py async def fetch_one_todo(title): document = await.collection.find_one({"title": title}) return document async def update_todo(title, desc): await.collection.update_one({"title": title}, { "$set": {"description": desc} }) document = await.collection.find_one({"title": title}) return document ``` ```python # main.py from database import update_todo @app.post("/api/todo/{title}", response_model=Todo) async def put_todo(todo: Todo): response = await update_todo(todo.dict()) if response: return response raise HTTPException(400, "Something went wrong/ bad request") ``` ### Frontend #### JSX - Javascript XML. - Allows us to write HTML in React. - Makes it easier to write and add HTML in React. #### Root component Only one root component ```jsx function App() { return ( <div className="container"> <div className="App" style={{ "width": "400px" }} > <h1>Task Manager</h1> </div> </div> ) } export default App; ``` #### TodoList Component - Depart the **todo list** and make it as a component. `useState`: Tracks state in a function component. The first value is current state, the second value is the function which used to update the state. ```jsx import React, {useState, useEffect} from 'react'; function App() { const [todoList, setTodoList] = useState([{}]) //todoList's initial = [{}] ... <TodoListView todoList={todoList} /> } ``` - Inside the TodoList View - Depart every **todo item** and make it as a component as well. `props` stands for properties. ```jsx function TodoListView(props) { return ( <div> <ul> {props.todoList.map( todo => <TodoItem todo={todo} />)} </ul> </div> ) } export default TodoListView ``` #### useEffect + axios to get todos `useEffect`: `useEffect(<function>, <dependency>:optional)` Allows component performs side effects such as fetching data, timers, updating the DOM directly. ```jsx useEffect(() => axios.get('http://localhost:8000/api/todo') .then(res => { setTodoList(res.data) }) ); ``` ## Run - frontend `npm start` - backend `pipenv shell` `uvicorn app:main --reload`