diff --git a/sidebarTutorials.js b/sidebarTutorials.js
index 0cce63ee2..6a0ad1b74 100644
--- a/sidebarTutorials.js
+++ b/sidebarTutorials.js
@@ -28,6 +28,7 @@ module.exports = {
'external-resources',
'webpack',
'using-launchdarkly-and-okteto-to-automate-modern-feature-flag-management',
+ 'develop-and-deploy-a-flask-and-reactjs-app-in-okteto',
],
},
{
diff --git a/src/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto.mdx b/src/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto.mdx
new file mode 100644
index 000000000..9cfc5f25d
--- /dev/null
+++ b/src/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto.mdx
@@ -0,0 +1,834 @@
+---
+title: Developing A Flask and ReactJS Application in Okteto
+description: Learn how to build an application with Flask and ReactJS directly in Okteto cloud.
+---
+
+import Image from '@theme/Image';
+
+> Muhammed Ali is a Software Developer with a passion for technical writing and open source contribution. His area of expertise is Full-stack web development and DevOps.
+
+**[Flask](https://flask.palletsprojects.com/en/2.0.x/)** is an open source web microframework built with Python. It is straightforward, easy to understand, and is used to build web applications and also APIs.
+
+In this article, you'll be building a TODO list application directly in Okteto using the [Okteto Development Environment](https://okteto.com/docs/reference/development-environment/) with the Flask framework and ReactJS library.
+
+Remote development with Okteto doesn't require an active docker session. Instead, we provide you a persistent volume, instant file synchronization, and an accessible public endpoint to view your application changes as you develop your application locally.
+
+**[ReactJS](https://reactjs.org/)** is an open-source front-end JavaScript library used to build user interfaces.
+
+**[Okteto](https://okteto.com/)** is an open-source tool that runs locally to synchronize application code changes to a running pod in a local or remote Kubernetes cluster. With Okteto you're able to see a deployment version of your app as you're developing it.
+
+Instead of working locally, you'll use Okteto's development environment. Although your application can be built locally in your machine, using Okteto's development environment enables you to develop your application in a production ready environment.
+
+## Prerequisites
+
+In order to follow this article, the following is required:
+
+- Basic understanding of Python and the [Flask](https://flask.palletsprojects.com/en/2.0.x/) framework
+- Basic understanding of [ReactJS](https://reactjs.org/)
+- Basic understanding of [Docker](https://docker.com/)
+- [NPM](https://npmjs.org/) installed
+
+## Build Flask API
+
+The first step is to create a project directory to house our todo list application:
+
+```console
+$ mkdir flask-react-app
+$ cd flask-react-app
+```
+
+Next, create the folder for the API application and create a route file:
+
+```console
+$ mkdir api && cd api
+$ touch todo_list_api.py
+```
+
+Next, create a `requirements.txt` file where your application dependencies will be listed. In this case, you only have one, which is `Flask`:
+
+```txt
+Flask==1.1.2
+Flask-SQLAlchemy==2.5.0
+```
+
+[Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/en/2.x/) will be used to handle the database in which the todo data will be stored.
+
+Next, you're going to create a `Dockerfile`. This Dockerfile will contain the instructions for building your application's image:
+
+```console
+$ touch Dockerfile
+```
+
+Add the following to it:
+
+```Dockerfile
+FROM python:3.9
+
+WORKDIR /api
+
+ENV FLASK_ENV=development
+
+COPY ./requirements.txt .
+
+RUN pip install -r requirements.txt
+
+COPY . .
+
+CMD [ "python", "todo_list_api.py" ]
+```
+
+Your file structure should now look like this:
+
+```
+├── flask-react-app
+│ ├── api
+│ │ ├── todo_list_api.py
+│ | ├── Dockerfile
+│ | ├── requirements.txt
+
+```
+
+## Scaffolding the frontend application
+
+For the frontend part of this application, you'll be using [Create-React-App](https://reactjs.org/docs/create-a-new-react-app.html/) to scaffold a new application.
+
+In the project directory, `flask-react-app`, run the command to create a frontend application in React:
+
+```console
+$ npx create-react-app frontend
+$ cd frontend
+```
+
+The Okteto development environment listens to the port `8080`. Add a proxy and change the port value in `start` under the `scripts` section in your `package.json` file:
+
+```json
+"proxy": "http://api:5000",
+"scripts": {
+ "start": "PORT=8080 react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+},
+```
+
+Install additional dependencies for the application:
+
+```console
+$ npm i react-router react-router-dom web-vitals
+```
+
+Next, you're going to create a `Dockerfile` for your frontend application:
+
+```console
+$ touch Dockerfile
+```
+
+Add the following to your Dockerfile:
+
+```Dockerfile
+
+FROM node:14.17.3
+
+WORKDIR /usr/src/app
+
+COPY ./package.json ./
+
+COPY ./package-lock.json ./
+
+RUN npm install
+
+COPY . .
+
+EXPOSE 8080
+
+ENTRYPOINT [ "npm" ]
+
+CMD ["start"]
+```
+
+Create a `docker-compose.yml` file so you can run both services at once.
+
+In the `docker-compose.yml` manifest, you're going to define two services: `api` for the backend and `web` for the frontend. The `web` service is dependent on the `api` service.
+
+Add the following to your `docker-compose.yml` manifest:
+
+```yaml
+services:
+ api:
+ build: ./api
+ ports:
+ - "5000:5000"
+ volumes:
+ - ./api:/api
+ environment:
+ FLASK_ENV: development
+ web:
+ build: ./frontend
+ ports:
+ - "8080:8080"
+ volumes:
+ - ./frontend:/usr/src/app
+ depends_on:
+ - api
+
+```
+
+Now run the following commands to build and start your application:
+
+```console
+$ docker-compose build
+$ docker-compose up
+```
+
+Your React application is now running and can be accessed at [http://localhost:8080](http://localhost:8080)
+
+
+
+## Build application in Okteto
+
+First, you have to [install Okteto CLI](https://okteto.com/docs/getting-started/installation/) to access Okteto's development environment.
+
+Next, login into Okteto Cloud from your CLI:
+
+```console
+$ okteto login
+```
+
+You'll be prompted to your browser. If it's successful, you'll see something similar to this:
+
+
+
+Now let's deploy the application using the `docker-compose.yml` file you created earlier:
+
+```console
+$ okteto stack deploy
+```
+
+After executing that, your docker-compose file will be deployed and built on Okteto cloud.
+
+Go to [Okteto cloud](https://cloud.okteto.com/) and you'll see that your application has been deployed successfully:
+
+
+
+The endpoints for your application have been automatically generated.
+
+## Set up your Okteto development environment for Flask
+
+In your terminal, create an Okteto manifest in your `api` folder:
+
+```console
+$ okteto init
+```
+
+After running it, you'll be prompted to select the deployment you want to develop and select "use default".
+
+You'll see that `okteto.yml` and `.stignore` were created at the root of your project.
+
+```yaml
+#okteto.yml
+name: api
+autocreate: true
+image: okteto/python:3
+command: bash
+volumes:
+ - /root/.cache/pip
+sync:
+ - .:/usr/src/app
+forward:
+ - 8080:8080
+reverse:
+ - 9000:9000
+```
+
+You'll have to change the `forward` route to `5000` because the frontend will be using port `8080`:
+
+```yml
+forward:
+ - 5000:5000
+```
+
+### Description of the files created
+
+#### Okteto.yml
+
+`okteto.yml` tells Okteto CLI how your project's development environment should be run.
+
+- `name`: the name of your project.
+- `command`: handles the command that is used to run your project.
+- `sync`: this handles the folders that will be synchronized between your local machine and the development container.
+- `forward`: used to provide a list of ports to forward from your development container.
+- `reverse`: a list of ports to connect development container to your local machine.
+- `volumes`: paths in your development container to be mounted as persistent volumes.
+- `sync`: the folders that will be synchronized between your local machine and the development container.
+
+#### .stignore
+
+`.stignore` tells Okteto CLI which files not to synchronize to your development environment. If you're familiar with `.gitignore`, `.stignore` has a similar function.
+
+Next, activate your development container:
+
+```
+$ okteto up
+```
+
+```
+ i Using khabdrick @ cloud.okteto.com as context
+ ✓ Images successfully pulled
+ ✓ Files synchronized
+ Context: cloud_okteto_com
+ Namespace: khabdrick
+ Name: api
+ Forward: 5000 -> 5000
+ Reverse: 9000 <- 9000
+
+Welcome to your development container. Happy coding!
+```
+
+Now install the requirements:
+
+```
+pip install -r requirements.txt
+```
+
+Now your development environment is currently running on Okteto cloud.
+
+Remember the `todo_list_api.py` file you created earlier? Navigate to it and add the following code:
+
+```python
+from flask import Flask, jsonify, request, json
+from flask_sqlalchemy import SQLAlchemy
+
+#instantiate Flask functionality
+app = Flask(__name__)
+
+# set sqlalchemy URI in application config
+app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.db"
+app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
+# instance of SQL
+
+db = SQLAlchemy(app)
+
+
+class TodoList(db.Model):
+# This class will handle the models for the todo list
+ id = db.Column(db.Integer, primary_key=True)
+ detail = db.Column(db.Text, nullable=False)
+
+ def __str__(self):
+ return f"{self.id} {self.detail}"
+
+def todo_serializer(todo):
+# covert the object coming from the database into JSON
+ return{
+ "id":todo.id,
+ "detail":todo.detail
+ }
+
+@app.route('/api', methods=['GET'])
+def home():
+# Display all the todos in the database
+ return jsonify([*map(todo_serializer, TodoList.query.all())])
+
+@app.route('/api/todocreate', methods=['POST'])
+def todo_create():
+
+ request_data = json.loads(request.data)
+ todo = TodoList(detail=request_data['detail'])
+
+ db.session.add(todo)
+ db.session.commit()
+
+ return{'201':'todo created successfully'}
+
+@app.route('/api/')
+def detail_view(id):
+# Detail view of each todo
+ return jsonify([*map(todo_serializer, TodoList.query.filter_by(id=id))])
+
+@app.route('/api/', methods=['PUT'])
+def update_todo(id):
+# Updates the todo with the id
+ todo = TodoList.query.get(id)
+ detail = request.json['detail']
+ todo.detail = detail
+ db.session.commit()
+
+ return {"200":"Updated successfully"}
+
+@app.route('/api/', methods=['POST'])
+def delete_todo(id):
+# Deletes todo from the database based on id
+ request_data = json.loads(request.data)
+ TodoList.query.filter_by(id=request_data['id']).delete()
+ db.session.commit()
+ return {"204":"Updated successfully"}
+
+if __name__ == '__main__':
+ app.run(debug=True, host='0.0.0.0')
+```
+
+The code above contains the routes and functions that performs the CRUD operations in the todolist application.
+
+The `#` before the command symbolizes that you're working with the development environment provided by Okteto.
+
+To test your API, add a todo. To do that, start the `python` shell in your development container terminal.
+
+Next, run the following commands to create an `example.db` file in your project:
+
+```shell
+> from todo_list_api import db
+> db.create_all()
+```
+
+Next, run the following commands to add a todo list entry:
+
+```shell
+> from todo_list_api import TodoList
+> todo = TodoList(detail="Learn Okteto")
+```
+
+Save the todo entry:
+
+```shell
+> db.session.add(todo)
+> db.session.commit()
+```
+
+Exit the Python shell with `ctrl + d`
+
+Start your Flask server by running `python todo_list_api`. You'll be able to access your API from the URL for your API provided by Okteto.
+
+
+
+### Set up your Okteto development environment for React
+
+In another terminal window, navigate to your project's directory root and initialize an Okteto manifest:
+
+```console
+$ okteto init
+```
+
+Select the web deployment:
+
+```console
+Select the deployment you want to develop:
+ ▸ web
+ ▸ api
+ Use default values
+```
+
+You'll notice an `okteto.yml` and `.stignore` file have been created in the root of your project.
+
+Next, activate your development container:
+
+```console
+$ okteto up
+```
+
+```
+ ✓ Images successfully pulled
+ ✓ Files synchronized
+ Context: cloud_okteto_com
+ Namespace: khabdrick
+ Name: web
+ Forward: 8080 -> 8080
+ Reverse: 9000 <- 9000
+
+Welcome to your development container. Happy coding!
+```
+
+### Build the front end
+
+In `frontend/src/components` folder, create a file `form.js`. This file will house the `Form` component responsible for form handling:
+
+```jsx
+import React from "react"
+
+
+export const Form = ({input, onFormChange, onFormSubmit}) => {
+
+ const handleChange = (event)=>{
+ // handle what the form does when you type in it
+ onFormChange(event.target.value)
+ }
+
+ const handleSubmit = (event)=>{
+ // handle what the form does when it is submitted
+ event.preventDefault()
+ onFormSubmit()
+ }
+
+ return(
+ <>
+
+ >
+ )
+}
+```
+
+In the same folder, create a file `cards.js` to handle the rendering of todos.
+
+Add the following to it:
+
+```jsx
+import React from "react";
+import {Link} from "react-router-dom"
+
+export const Card = ({ listOfTodos }) => {
+ return (
+ <>
+ {listOfTodos.map(todo => {
+ return (
+
+
+
+
+
{todo.detail}
+
+ );
+ })}
+ >
+ );
+};
+```
+
+Nevermind the `listOfTodos` prop, you will create it next.
+
+In the same folder, create a new file`TodoList.js`. This file houses the components responsible for retrieving and sending todos from the API:
+
+```jsx
+import React, { useState, useEffect } from "react";
+import { Card } from "../components/card";
+import { Form } from "../components/form";
+
+export const TodoPage = () => {
+ const [todo, setTodo] = useState([]);
+ const [addTodo, setAddTodo] = useState("");
+
+ useEffect(() => {
+ // Fetch the list of todos API
+ fetch("/api")
+ .then((response) => {
+ if (response.ok) {
+ return response.json();
+ }
+ })
+ .then((data) => setTodo(data));
+ }, []);
+
+ const handleFormChange = (inputValue) => {
+ setAddTodo(inputValue);
+ };
+
+ const handleFormSubmit = () => {
+ // Creates todo suing the API
+ fetch("/api/todocreate", {
+ method: "POST",
+ body: JSON.stringify({
+ detail: addTodo,
+ }),
+ headers: {
+ "Content-type": "application/json; charset=UTF-8",
+ },
+ })
+ .then((response) => response.json())
+ .then((message) => {
+ console.log(message);
+ setAddTodo("");
+ getUpdate();
+ });
+ };
+
+ const getUpdate = () => {
+ // Automatically update the list as you submit the form
+ fetch("/api")
+ .then((response) => {
+ if (response.ok) {
+ return response.json();
+ }
+ })
+ .then((data) => setTodo(data));
+ };
+
+ return (
+ <>
+
+
+ >
+ );
+};
+```
+
+Next, create the detail page, this page will display each todo.
+
+First create a new file `detailView.js` in `src/components` folder, and add the following:
+
+```jsx
+import React, { useState, useEffect } from "react";
+import { Link, useParams } from "react-router-dom";
+import { useHistory } from "react-router-dom";
+
+export const Detail = () => {
+ const history = useHistory();
+ const [todo, setTodo] = useState([]);
+ const { id } = useParams();
+
+ useEffect(() => {
+ fetch(`/api/${id}`)
+ .then((response) => response.json())
+ .then((data) => setTodo(data));
+ }, [id]);
+
+.then((response) => response.json())
+ .then((message) => {
+ history.push("/"); //redirect to homepage
+
+ })
+
+};
+
+return (
+
+
+ {todo.map((data) => (
+
Detail: {data.detail}
+ ))}
+
+
+ Go Back to Todo List
+
+);
+};
+```
+
+Moving forward, let's update `App.js` so that we can test out what we have.
+
+```jsx
+import React from "react";
+import "./App.css";
+import { TodoPage } from "./components/TodoList";
+import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
+import { Detail } from "./components/detailView";
+
+function App() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
+```
+
+In your Okteto dashboard, select the second endpoint:
+
+
+
+You should see "Invalid Host header" on the browser. This is because you have not permitted Okteto to run your React application.
+
+To fix this, you'll create a `.env` and disable host check. In the frontend folder, run the command from your terminal:
+
+```console
+$ touch .env
+```
+
+Add the following to your `.env` file:
+
+```env
+DANGEROUSLY_DISABLE_HOST_CHECK=true
+```
+
+While doing all these updates, Okteto automatically synchronizes the changes with your application deployed on Okteto.
+
+Next, update your application style in `App.css`:
+
+```css
+*{
+ text-decoration: none;
+}
+
+body {
+ font-size: 1.6rem;
+ background-color: #efefef;
+ color: #324047
+}
+
+
+.App-header{
+ padding-left: 500px;
+}
+
+.form-class{
+ margin-top: 100px;
+}
+
+.links{
+ position: relative;
+ list-style-type: square;
+ padding-left: 1rem;
+ margin-bottom: 0.5rem;
+}
+```
+
+In your okteto development container, start your application:
+
+```console
+npm run start
+```
+
+You'll see that you can view and create todos.
+
+
+
+Next, you'll build the update and delete functionality. In the `src/components` folder create a new file `deletePage.js`.
+
+```jsx
+import React from "react"
+import {useHistory} from 'react-router-dom'
+
+export const Delete = ({id}) =>{
+ const history = useHistory()
+ const deleteTodo = () =>{
+ fetch(`/api/${id}`,{
+ method: 'POST',
+ body:JSON.stringify({
+ id:id
+ })
+ }).then(response => response.json())
+ .then(data => {
+ history.push('/')
+
+ })
+
+ }
+
+ return(
+ <>
+
+ >
+ )
+}
+```
+
+The code above is just fetching the delete function and applying it to the `delete` button.
+
+For `update` you'll have to make some changes to the detail view, and add a form to use for updating. Update your `detailView.js` file to look like below.
+
+```jsx
+import React, { useState, useEffect } from "react";
+import { Link, useParams } from "react-router-dom";
+import { Delete } from "../components/deletePage";
+import { Form } from "../components/form";
+import { useHistory } from "react-router-dom";
+
+export const Detail = () => {
+ const history = useHistory();
+ const [todo, setTodo] = useState([]);
+ const [updateTodo, setUpdateTodo] = useState("");
+ const { id } = useParams();
+
+ useEffect(() => {
+ fetch(`/api/${id}`)
+ .then((response) => response.json())
+ .then((data) => setTodo(data));
+ }, [id]);
+
+ const handleFormChange = (inputValue) => {
+ setUpdateTodo(inputValue);
+ };
+
+ const handleFormSubmit = () => {
+ fetch(`/api/${id}`, {
+ method: "PUT",
+ body: JSON.stringify({
+ detail: updateTodo,
+ }),
+ headers: {
+ "Content-type": "application/json; charset=UTF-8",
+ },
+ })
+ .then((response) => response.json())
+ .then((message) => {
+ history.push("/");
+
+ })
+
+ };
+
+ return (
+
+
+
+ {todo.map((data) => (
+
Detail: {data.detail}
+ ))}
+
+
Fill the above to update
+
+ Go Back to Todo List
+
+ );
+};
+
+```
+
+Now click on a particular todo, and you should see something like this:
+
+
+
+## Conclusion
+
+In this article, you learned how to build a Flask and React application by developing directly in Okteto's development environment. You also learned how to deploy docker-compose applications to Okteto. While you could develop locally, by developing directly in Okteto's development environment, you can avoid having to install Docker locally, and can develop your app in a production-like environment. You can find the code used in this article on [GitHub](https://github.com/okteto/flask-react-app/).
+
+Have any questions about Okteto? Come join the conversation on the #okteto channel in the [Kubernetes Slack workspace](http://slack.k8s.io).
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/api.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/api.png
new file mode 100644
index 000000000..c7dc39342
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/api.png differ
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/dashboard.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/dashboard.png
new file mode 100644
index 000000000..3d0ee65d8
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/dashboard.png differ
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/localhost.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/localhost.png
new file mode 100644
index 000000000..d9e029409
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/localhost.png differ
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/okteto-welcome.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/okteto-welcome.png
new file mode 100644
index 000000000..d0bd4252e
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/okteto-welcome.png differ
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todo.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todo.png
new file mode 100644
index 000000000..c44c6a43a
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todo.png differ
diff --git a/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todolist.png b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todolist.png
new file mode 100644
index 000000000..2e3f4790e
Binary files /dev/null and b/static/img/tutorials/develop-and-deploy-a-flask-and-reactjs-app-in-okteto/todolist.png differ