В этом руководстве вы узнаете, как создать и развернуть приложение для ведения заметок. С помощью этого приложения вы можете создать свою задачу, обновить ее и удалить задачу.
Я и мой друг Кушал изучили и создали это приложение.
Ниже приведены технологии, которые мы используем в этом руководстве
1) Mongo Atlas. MongoDB Atlas - это полностью управляемая облачная база данных, разработанная теми же людьми, которые создают MongoDB. Atlas берет на себя всю сложность развертывания, управления и восстановления ваших развертываний в выбранном вами провайдере облачных услуг (AWS, Azure и GCP).
2) Moongoose. Mongoose - это библиотека MongoDB и моделирования объектных данных (ODM) на основе узлов. Он отслеживает отношения данных, выполняет проверку схемы и преобразует объекты кода в представления MongoDB. 3) Express Js
4) Reactjs. React - это библиотека JavaScript для создания декларативных, эффективных и настраиваемых пользовательских интерфейсов.
5) ExpressJ - Express - это минималистичная и гибкая платформа для веб-приложений Node. js с открытым исходным кодом, предназначенная для разработки веб-сайтов, веб-приложений, & API намного проще.
6) Digital Ocean - это уникальный провайдер облачного хостинга, который предлагает услуги облачных вычислений для бизнес-структур, чтобы они могли масштабироваться за счет развертывания приложений DigitalOcean, которые работают параллельно на нескольких облачных серверах без ущерба для производительности!
Если вас больше интересует полный код, вы можете увидеть его по следующему URL-адресу
Https://github.com/karkranikhil/todo-app
URL развернутого приложения
Https://todo-app-v9tsq.ondigitalocean.app/
I) Настройка проекта
Для вас мы настроили базовый код, чтобы вы могли сразу перейти к созданию кода, а не к настройке проекта. В коде базового проекта мы уже упоминали пакеты, которые собираемся использовать в этом проекте, внутри package.json.
Сначала клонируйте проект из следующего
Https://github.com/karkranikhil/todo-app/tree/project-setup
Выполните следующие команды
git clone https://github.com/karkranikhil/todo-app/tree/project-setup npm install npm run client
Как только вы сможете запустить все команды, вы увидите, что новое окно браузера открыто, и вы сможете увидеть следующий результат.
Хорошо Готово, ваш проект успешно настроен !!
II) Давайте настроим экспресс-сервер
В папке server нашего проекта создайте новый файл index.js.
Добавьте в файл следующий код
// require express const express = require("express"); const path = require("path"); var cors = require('cors') // create express app const app = express(); // use middleware on express app app.use(cors()); app.use(express.urlencoded()) app.use(express.json()) // define port to run express app const port = process.env.PORT || 5000; // Add endpoint app.get('/', (req, res) => { res.send("Welcome to our Todo App"); }); // Listen to server app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });
В приведенном выше коде мы импортируем необходимый пакет, например express, cors и path. Все эти пакеты уже установлены с помощью команды npm install
. После этого мы создаем приложение Express. После создания нашего приложения мы упомянули несколько промежуточных программ, использующих метод app.use. Все запросы API проходят через промежуточное программное обеспечение. После этого мы определили порт, на котором будет работать наш сервер.
используя app.get, мы создаем наш первый API, который будет прослушивать метод GET и возвращать на экране сообщение «Добро пожаловать в наше приложение Todo».
В конце мы вызываем порт прослушивания app
и передаем номер port
(5000) и обратный вызов, который печатает msg на консоли после запуска нашего сервера.
Давайте запустим наш сервер, используя следующую команду
node server/index.js
После успешного запуска вашего сервера перейдите по следующему URL-адресу
вы увидите следующий результат
Итак, мы успешно создали наш первый экспресс API.
III) Настройка нашего MongoDB Atlas
Создайте учетную запись MongoDB Atlas
MongoDB Atlas - это платформа MongoDB Database-as-a-Service, что означает, что они настраивают и размещают базу данных для вас. Тогда ваша единственная ответственность будет заключаться в том, чтобы заполнить вашу базу данных тем, что имеет значение: данными.
- Перейдите сюда, чтобы зарегистрировать новую учетную запись MongoDB Atlas.
- Заполните регистрационную форму своими данными и нажмите Зарегистрироваться.
Создать новый кластер
- На следующей странице введите название своей организации, название проекта, выберите JavaScript в качестве предпочтительного языка программирования и нажмите зеленую кнопку Продолжить.
- Нажмите кнопку Создать кластер в разделе "Общие кластеры". Это должен быть единственный бесплатный вариант.
- В раскрывающемся списке Поставщик облака и регион оставьте значение по умолчанию (обычно AWS).
- В раскрывающемся списке Уровень кластера оставьте значение по умолчанию
M0 Sandbox (Shared RAM, 512 MB Storage)
. - В раскрывающемся списке Имя кластера вы можете дать своему кластеру имя или оставить его по умолчанию
Cluster 0
. - Нажмите зеленую кнопку Создать кластер внизу экрана.
- Теперь вы должны увидеть сообщение
Your cluster is being created. New clusters take between 1-3 minutes to provision.
Подождите, пока кластер не будет создан, прежде чем переходить к следующему шагу.
Создайте нового пользователя для базы данных
- В левой части экрана нажмите Доступ к базе данных.
- Нажмите зеленую кнопку Добавить нового пользователя базы данных.
- В модальном окне введите новое имя пользователя и пароль.
- В разделе Права пользователя базы данных оставьте вариант по умолчанию Чтение и запись в любую базу данных.
- Нажмите кнопку Добавить пользователя, чтобы создать нового пользователя.
Разрешить доступ со всех IP-адресов
- В левой части экрана нажмите Доступ к сети.
- Нажмите зеленую кнопку Добавить IP-адрес.
- В модальном окне нажмите кнопку РАЗРЕШИТЬ ДОСТУП ОТ ЛЮБОГО ОТДЕЛА. Вы должны увидеть
0.0.0.0/0
в поле записи списка доступа. - Нажмите зеленую кнопку Подтвердить.
Подключитесь к вашему кластеру
- Нажмите зеленую кнопку Приступить к работе в левом нижнем углу экрана, и теперь вы увидите последний шаг: Подключитесь к кластеру, нажмите на него.
- В левой части экрана нажмите Кластеры.
- Нажмите кнопку Подключить для своего кластера.
- В модальном всплывающем окне нажмите Подключить приложение.
- Вы должны увидеть URI, который вы будете использовать для подключения к своей базе данных, примерно так:
mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<db-name>?retryWrites=true&w=majority
. - Нажмите кнопку Копировать, чтобы скопировать свой URI в буфер обмена.
Обратите внимание, что поля <username>
, <cluster-name>
и <db-name>
скопированного вами URI уже заполнены. Все, что вам нужно сделать, это заменить поле <password>
тем, которое вы создали на предыдущем шаге.
IV. Подключение MongoDB к нашему экспресс-приложению
Под сервером в папке создайте новый файл с именем config.js и вставьте следующий код
// Export mongoose const mongoose = require(“mongoose”); //Assign MongoDB connection string to Uri and declare options settings const uri = `mongodb+srv://${USERNAME}:${PASSWORD}@todocluster.4vnhp.mongodb.net/myFirstDatabase?retryWrites=true&w=majority` // Declare a variable named option and assign optional settings const options = { useNewUrlParser: true, useUnifiedTopology: true }; // Connect MongoDB Atlas using mongoose connect method mongoose.connect(uri, options).then(() => { console.log(“Database connection established!”); },err => { { console.log(“Error connecting Database instance due to:”, err); } });
В приведенном выше коде сначала мы импортируем библиотеку мангуста, а затем добавляем URL-адрес, который мы скопировали из нашего атласа MongoDB.
Примечание ** замените USERNAME и PASSWORD своими учетными данными
Monoogese предоставляет нам метод подключения, который помогает нам подключаться к нашей базе данных mongo. если возникает какая-либо ошибка, она печатается в консоли.
Нет, наша конфигурация готова, теперь мы должны вызвать это на нашем сервере index.js
Внутри index.js добавьте следующий жирным шрифтом код
app.use(cors()); app.use(express.urlencoded()) app.use(express.json()) // Import DB Connection require(“./config.js”);
давайте снова запустим сервер с той же командой
node server/index.js
Вы увидите сообщение «Соединение с базой данных установлено!»
на консоли, как показано ниже
V. Создание API для нашей задачи создание, чтение, обновление, удаление
Давайте создадим несколько новых файлов controller.js, model.js, routes.js
в папке сервера, как показано ниже.
Внутри Model.js
файла добавьте следующий код.
// Import mongoose const mongoose = require(“mongoose”); // Declare schema and assign Schema class const Schema = mongoose.Schema; // Create Schema Instance and add schema propertise const TodoSchema = new Schema({ text: { type:String, required:true }, status:{ type:Boolean, required:true }, createdOn: { type:Date, default:Date.now } }); // create and export model module.exports = mongoose.model(“todoModel”, TodoSchema);
В model.js мы создаем схему для нашей базы данных. Если мы посмотрим на приведенный выше код, мы используем три поля
а) текст - для хранения данных
б) статус - для хранения статуса
в) createdOn - для хранения даты создания задачи.
Внутри routes.js
файла добавьте следующий код
// create App function module.exports = function(app) { var todoList = require(‘./controller’); // todoList Routes // get and post request for /todos endpoints app .route(“/todos”) .get(todoList.listAllTodos) .post(todoList.createNewTodo); // put and delete request for /todos endpoints app .route(“/todo/:id”) .put(todoList.updateTodo) .delete(todoList.deleteTodo); };
routes.js
сообщает нам, какой метод вызывать внутри контроллера на основе метода и конечной точки.
Внутри controller.js
файла добавьте следующий код
// import Todo Model const Todo = require(“./model”); // DEFINE CONTROLLER FUNCTIONS // listAllTodos function — To list all todos exports.listAllTodos = (req, res) => { Todo.find({}, (err, todo) => { if (err) { res.status(500).send(err); } res.status(200).json(todo); }); }; // createNewTodo function — To create new todo exports.createNewTodo = (req, res) => { let newTodo = new Todo (req.body); newTodo.save((err, todo) => { if (err) { res.status(500).send(err); } res.status(201).json(todo); }); }; // updateTodo function — To update todo status by id exports.updateTodo = (req, res) => { Todo.findOneAndUpdate({ _id:req.params.id }, req.body, { new:true }, (err, todo) => { if (err) { res.status(500).send(err); } res.status(200).json(todo); }); }; // deleteTodo function — To delete todo by id exports.deleteTodo = async ( req, res) => { await Todo.deleteOne({ _id:req.params.id }, (err) => { if (err) { return res.status(404).send(err); } res.status(200).json({ message:”Todo successfully deleted”}); }); };
Внутри контроллера мы написали методы, которые вызываются на основе конечной точки API и метода, определенного в routes.js
В конце концов, мы должны подключить наш файл маршрута внутри index.js
Добавьте следующий выделенный код в index.js
// Import DB Connection require(“./config.js”); // Import API route var routes = require(‘./routes’); //importing route routes(app);
Пришло время протестировать наши API.
VI. Тестирование API с помощью почтальона
Перед тестированием давайте снова запустим сервер с помощью следующей команды
node server/index.js
Давайте откроем почтальон. Вы можете использовать и инструмент тестирования API
а) Тестирование базовой базовой конечной точки
http://localhost:5000
он вернет следующий результат
б) Добавление задачи в БД
Для добавления мы должны использовать следующий URL-адрес с методом POST
localhost:5000/todos
И как только он будет добавлен, вы увидите следующий результат
в) Обновление задачи в БД
Для обновления задачи нам нужен идентификатор задачи, которую мы хотим обновить, и в нашем случае мы будем использовать ранее созданный идентификатор задачи.
мы должны использовать следующий URL-адрес с методом PUT
localhost:5000/todo/60eecba5dd8f6b50db4deb52
И как только задача будет обновлена, вы увидите следующий результат
г) Получение задач из БД
Для получения задачи мы должны использовать следующий URL-адрес с методом GET
localhost:5000/todos
И как только задачи будут загружены, вы увидите следующий результат. Он вернет все задачи, доступные внутри БД
д) Удаление задачи из БД
Для удаления задачи нам нужен идентификатор задачи, которую мы хотим удалить, и в нашем случае мы будем использовать ранее созданный идентификатор задачи.
мы должны использовать следующий URL-адрес с методом DELETE
localhost:5000/todo/60eecba5dd8f6b50db4deb52
И как только задача будет удалена, вы увидите следующий результат.
Ура, все наши API работают !!
VII. Настройка пользовательского интерфейса и интеграция API
Откройте свой проект и создайте следующую папку и файлы
а) Создайте новую папку под названием api в папке src и добавьте в нее два файла httpClient.js and todoApi.js
б) Создайте новую папку под названием components в папке src и в ней создайте три новые папки Todo, TodoItem, and TodoList.
- В папке
Todo
добавьте два файлаTodo.css and Todo.js
- В папке
TodoItem
добавьте два файлаTodoItem.css and TodoItem.js
- В папке
TodoList
добавьте два файлаTodoList.css and TodoList.js
c) Создайте новую папку под названием redux в папке src и в ней создайте два новых файла actions.js and reducers.js
г) Добавьте store.js
файл в папку src.
Теперь ваша структура папок будет выглядеть, как показано ниже.
Давайте добавим следующий код в соответствующий файл
httpClient.js
Этот файл используется для вызова HTTP-запроса. Мы используем пакет axios для вызова API
import axios from ‘axios’ //Create a Http Client using Axios. Further modifications in this layer can be done later like Authorization. const post = (url = ‘’, data = ‘’, config = {}) => { return axios.post(url, data, config) } const get = (url) => { return axios(url) } const put = (url = ‘’, data = ‘’, config = {}) => { return axios.put(url, data, config) } //Cannot contain a delete method — Cause delete is a keyword. const del = (url = ‘’, config = {}) => { return axios.delete(url, config) } //Encapsulating in a JSON object const HttpClient = { post, get, put, delete: del } export {HttpClient}
todoApi.js
Этот файл используется для вызова методов, определенных в httpClient.
мы настроили наш API и его путь для его вызова.
import {HttpClient} from ‘./httpClient’; // This is the API. The backend root URL can be set from here. console.log(process.env.REACT_APP_PROD_URL); const port = process.env.PORT || 5000; const API = process.env.REACT_APP_PROD_URL || ‘https://todo-app-v9tsq.ondigitalocean.app' || `http://localhost:${port}` //Setting the todos URI const TODO_API = `${API}/todos` // The CRUD Operations of the Todo Resource. //Create const createTodo = todo => { return HttpClient.post(TODO_API, todo) } //Read const getTodos = () => { return HttpClient.get(TODO_API) } //Update const updateTodo = todo => { return HttpClient.put(`${API}/todo/${todo._id}`, todo) } //Delete const removeTodo = todo => { return HttpClient.delete(`${API}/todo/${todo._id}`) } //Encapsulating in a JSON object const TodoApi = {createTodo, getTodos, updateTodo, removeTodo} export {TodoApi}
todoApi.js
Этот файл используется для вызова методов, определенных в httpClient.
мы настроили наш API и его путь для его вызова.
Мы использовали Redux для обучения, поэтому в этом проекте мы использовали redux
store.js
Этот файл используется для создания хранилища redux, которое является глобальным хранилищем.
import {createStore, combineReducers, applyMiddleware} from ‘redux’; import thunkMiddleware from ‘redux-thunk’ import { createLogger } from ‘redux-logger’ import { todosReducer } from ‘./redux/reducers’; const reducers = { todosReducer, }; const rootReducer = combineReducers(reducers); // Create the redux logging middleware const loggerMiddleware = createLogger() export const configureStore= (preloadedState) => createStore( rootReducer, preloadedState, applyMiddleware( thunkMiddleware, loggerMiddleware ) );
actions.js
действие - это объект, содержащий полезную информацию. Они являются единственным источником информации для магазина Redux, который необходимо обновлять.
import { TodoApi } from ‘../api/todoApi’ //Create export const CREATE_TODO = ‘CREATE_TODO’ export const CREATE_TODO_SUCCESS = ‘CREATE_TODO_SUCCESS’ export const CREATE_TODO_ERROR = ‘CREATE_TODO_ERROR’ //Read export const GET_TODOS = ‘GET_TODOS’ export const GET_TODOS_SUCCESS = ‘GET_TODOS_SUCCESS’ export const GET_TODOS_ERROR = ‘GET_TODOS_ERROR’ //Update export const UPDATE_TODO = ‘UPDATE_TODO’ export const UPDATE_TODO_SUCCESS = ‘UPDATE_ODO_SUCCESS’ export const UPDATE_TODO_ERROR = ‘UPDATE_TODO_ERROR’ //Remove export const REMOVE_TODO = ‘REMOVE_TODO’; export const REMOVE_TODO_SUCCESS = ‘REMOVE_TODO_SUCCESS’ export const REMOVE_TODO_ERROR = ‘REMOVE_TODO_ERROR’ //Create //The dispatch and getstate function is provided by the Redux-Thunk middleware, we can dispatch actions with it. export function CreateTodo(todo){ return (dispatch, getState) => { return TodoApi.createTodo(todo).then(res => { dispatch(CreateTodoSuccess(res.data)) }) } } export function CreateTodoSuccess(todo){ return { type:CREATE_TODO_SUCCESS, todo } } //Read export function GetTodos(){ return (dispatch, getState) => { return TodoApi.getTodos().then(res => { dispatch(GetTodoSuccess(res.data)) }) } } export function GetTodoSuccess(todos){ return { type:GET_TODOS_SUCCESS, todos } } //Remove export function RemoveTodo(todo) { return (dispatch, getState) => { TodoApi.removeTodo(todo).then(res => { dispatch(RemoveTodoSuccess(todo)) }) } } export function RemoveTodoSuccess(todo) { return { type: REMOVE_TODO_SUCCESS, todo, _id: todo._id } } //update export function UpdateTodo(todo){ return (dispatch) => { return TodoApi.updateTodo(todo).then(res => { dispatch(UpdateTodoSuccess(res.data)) }) } } export function UpdateTodoSuccess(todo){ return { type:UPDATE_TODO_SUCCESS, todo } }
redurs.js
Редукторы обновляют магазин в зависимости от значения действия.
import * as TodoActions from “./actions”; export const todosReducer = (state = [], action) => { const { type } = action; switch(type) { // Create case TodoActions.CREATE_TODO_SUCCESS: { return [ …state, action.todo ]; } //Read case TodoActions.GET_TODOS_SUCCESS: { return action.todos || []; } //update case TodoActions.UPDATE_TODO_SUCCESS: { return state.map(item=>item._id === action.todo._id ? {…action.todo}:{…item}) } //Remove case TodoActions.REMOVE_TODO: { return state.map(s => todo(s, action)) } case TodoActions.REMOVE_TODO_SUCCESS: { return state.filter(todo => todo._id !== action._id); } default: return state; } }
Todo.js
import React from ‘react’; import { connect } from ‘react-redux’; import ‘./Todo.css’; import { CreateTodo } from ‘../../redux/actions’; const Todo = ({todoList, onCreatePressed}) => { const [newText, setNewText] = React.useState(“”); return ( <div className=”new-todo”> <input type=”text” className=”new-todo-input” placeholder=”Task title” value={newText} onChange={e => setNewText(e.target.value)}/> <div className=”button-container”> <button onClick={()=> { const isDuplicate = todoList.some(todo => todo.text === newText) if (!isDuplicate){ onCreatePressed({“text”: newText, “status”: true}); setNewText(‘’); } }} className=”add-button”> Create Task </button> </div> </div> ); } const mapStateToProps = state => ({ todoList: state.todosReducer }); const mapDispatchToProps = dispatch => ({ onCreatePressed: text => dispatch(CreateTodo(text)), }); export default connect(mapStateToProps, mapDispatchToProps)(Todo);;
Todo.css
.new-todo { border: 2px solid #cccccc; margin: 0 auto; display: flex; align-items: center; max-width: 700px; } .new-todo-input { border: none; background: #2b2e3e; padding: 1rem; border-radius: 5px; color: #fff; width: 100%; flex: 1; margin: 10px; outline: none; } .new-todo-inputedit { border: auto; outline: auto; } .new-todo .button-container { padding: 5px; } .add-button { border: none; background-color: #9135f6; color: white; border-radius: 5px; font-size: 1rem; padding: 1rem; width: 100%; cursor:pointer; }
TodoList.js
import React from ‘react’; import { connect } from ‘react-redux’; import TodoItem from ‘../TodoItem/TodoItem’; import ‘./TodoList.css’; import { RemoveTodo } from ‘../../redux/actions’; const TodoList = ({todos = [], onRemovePressed}) => ( <div className=”todo-wrapper”> {todos.map(todo => <TodoItem todo={todo} onRemovePressed={onRemovePressed} key={todo?.text}/>)} </div> ); const mapStateToProps = state => ({ todos: state.todosReducer }); const mapDispatchToProps = dispatch => ({ onRemovePressed: text => dispatch(RemoveTodo(text)), }); export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
todoList.css
.todo-wrapper { display: flex; align-items: center; justify-content: center; padding-top: 2rem; flex-wrap: wrap; }
todoItem.js
import React, {useState} from ‘react’; import { useDispatch } from ‘react-redux’ import ‘./TodoItem.css’; import { UpdateTodo } from ‘../../redux/actions’; const TodoItem = ({todo, onRemovePressed}) => { const dispatch = useDispatch() const [isEdit, setIsEdit] = useState(null) const [updatedText, setUpdatedText]= useState(todo?.text) const editUpdateHandler = () =>{ if(isEdit){ dispatch(UpdateTodo({…todo, “text”:updatedText})) } else { setIsEdit(true) } } return ( <div className=”todo-item”> {isEdit ? <input type=”text” className=”new-todo-input new-todo-inputedit” placeholder=”Task title” value={updatedText} onChange={e => setUpdatedText(e.target.value)}/> :<p>{todo.text}</p>} <div className=”button-container”> {/* <button className=”mark-completed-button”> Mark as Completed </button> */} <p>{new Date(todo.createdOn).toLocaleDateString()}</p> <button className=”button update-button” onClick={() => editUpdateHandler()}> {isEdit ? “update” : “Edit”} </button> <button className=”button remove-button” onClick={() => onRemovePressed(todo)}> Delete </button> </div> </div> )} export default TodoItem;
todoItem.css
.todo-item { margin: 1rem; background: #2b2e3e; border-radius: 16px; box-shadow: 0px 0px 3px #d1b8ef; display: flex; padding:0.5rem; justify-content: space-between; align-items: center; min-height: 200px; width: 200px; display: flex; flex-direction: column; width: 260px; } .todo-item .button-container { display: flex; align-items: center; } .button { border: none; color: white; padding: 10px; cursor:pointer; border-radius: 10px; margin-left:1rem; } .remove-button{ background:#ff6e6e; } .update-button{ background:#f19732; } .mark-completed-button { border: none; background-color: cyan; color: black; padding: 10px; margin: 5px; }
App.js - добавить все компоненты в app.js
import { hot } from ‘react-hot-loader’; import React from ‘react’; import “./App.css”; import TodoList from ‘./components/TodoList/TodoList’ import Todo from ‘./components/Todo/Todo’; const App = () => ( <div className=”App”> <h1>Organize your work</h1> <Todo /> <TodoList /> </div> ); export default hot(module)(App);
index.js
нам нужно обновить index.js, чтобы интегрировать redux в приложение
import React from ‘react’; import ReactDom from ‘react-dom’; import App from ‘./App’; import { Provider } from ‘react-redux’; import { configureStore } from “./store”; import * as TodoActions from ‘./redux/actions’ const store = configureStore() store.dispatch(TodoActions.GetTodos()) ReactDom.render( <Provider store={store}> <App /> </Provider>, document.getElementById(‘root’), );
Давайте протестируем приложение, выполнив следующую команду
npm run dev
после успешного запуска вы увидите следующий вывод
VIII. Создайте пакет и обслуживайте интерфейс с экспресс-сервера
Чтобы собрать пакет нашего интерфейса, выполните следующую команду
npm build:client
это создаст папку dist внутри нашего проекта, как показано ниже
Перейдите на server / index.js и добавьте следующую выделенную строку
// Import DB Connection require(“./config.js”); //serving frontend using middlewares app.use(express.static(path.join(__dirname, “..”, “dist”))); app.use(express.static(“public”));
Теперь давайте запустим приложение, используя следующую команду
npm run dev
Вы увидите тот же результат, но на этот раз приложение работает на порту 5000.
IX - Развертывание приложения в цифровом океане
создать аккаунт на D igital Ocean
После создания перейдите в приложение и создайте новое приложение.
Выберите источник вашего приложения. В моем случае мой код находится в GitHub, поэтому я выбираю github
Выберите имя репозитория и сохраните мастер ветки
Установите свою конфигурацию
Назовите свои веб-службы
Нажмите запустить базовое приложение
Теперь приступайте к развертыванию, и после развертывания вы получите развернутый URL-адрес
Откройте URL-адрес, и ваше приложение ВВЕРХ и работает
использованная литература
Https://github.com/karkranikhil/todo-app/tree/master
Https://cloud.digitalocean.com/
Спасибо Кушалу Джайну за помощь и вклад в эту статью.