├── .gitignore ├── frontend ├── src │ ├── App.css │ ├── index.js │ ├── Components │ │ └── ArticleList.js │ └── App.js ├── public │ └── index.html ├── package.json └── README.md ├── backend ├── migrations │ ├── README │ ├── script.py.mako │ ├── alembic.ini │ └── env.py ├── run ├── database.db ├── requirements.txt ├── routes.py ├── models.py ├── manage.py └── app.py ├── flask+react.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /backend/venv 2 | /frontend/node_modules 3 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | min-height: 100vh; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /backend/migrations/README: -------------------------------------------------------------------------------- 1 | Single-database configuration for Flask. 2 | -------------------------------------------------------------------------------- /backend/run: -------------------------------------------------------------------------------- 1 | FLASK_APP=routes.py FLASK_DEBUG=1 FLASK_ENV=development flask run -------------------------------------------------------------------------------- /flask+react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahondiek/Connecting-React-Frontend-to-a-Flask-Backend/HEAD/flask+react.png -------------------------------------------------------------------------------- /backend/database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahondiek/Connecting-React-Frontend-to-a-Flask-Backend/HEAD/backend/database.db -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root') 10 | ); 11 | 12 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==1.6.5 2 | click==8.0.1 3 | Flask==2.0.1 4 | Flask-Cors==3.0.10 5 | flask-marshmallow==0.14.0 6 | Flask-Migrate==3.1.0 7 | Flask-SQLAlchemy==2.5.1 8 | greenlet==1.1.0 9 | gunicorn==20.1.0 10 | itsdangerous==2.0.1 11 | Jinja2==3.0.1 12 | Mako==1.1.4 13 | MarkupSafe==2.0.1 14 | marshmallow==3.13.0 15 | marshmallow-sqlalchemy==0.26.1 16 | mysqlclient==2.0.3 17 | python-dateutil==2.8.2 18 | python-editor==1.0.4 19 | six==1.16.0 20 | SQLAlchemy==1.4.22 21 | Werkzeug==2.0.1 22 | -------------------------------------------------------------------------------- /backend/routes.py: -------------------------------------------------------------------------------- 1 | from flask import current_app,jsonify,request 2 | from app import create_app,db 3 | from models import Articles,articles_schema 4 | 5 | # Create an application instance 6 | app = create_app() 7 | 8 | # Define a route to fetch the avaialable articles 9 | 10 | @app.route("/articles", methods=["GET"], strict_slashes=False) 11 | def articles(): 12 | 13 | articles = Articles.query.all() 14 | results = articles_schema.dump(articles) 15 | 16 | return jsonify(results) 17 | 18 | 19 | if __name__ == "__main__": 20 | app.run(debug=True) -------------------------------------------------------------------------------- /backend/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /frontend/src/Components/ArticleList.js: -------------------------------------------------------------------------------- 1 | 2 | const ArticleList = (props) => { 3 | 4 | return ( 5 |
6 | {/* Display the article details if article is not None */} 7 | {props.articles && props.articles.map(article =>{ 8 | return ( 9 | 10 |
11 |

{ article.title}

12 |

{ article.body }

13 |

{ article.date }

14 |
15 |
16 | ) 17 | 18 | })} 19 |
20 | ) 21 | } 22 | 23 | export default ArticleList; -------------------------------------------------------------------------------- /backend/models.py: -------------------------------------------------------------------------------- 1 | from app import db,ma 2 | from datetime import datetime 3 | 4 | 5 | class Articles(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | title = db.Column(db.String(100),nullable=False) 8 | body = db.Column(db.Text, nullable=False) 9 | date = db.Column(db.DateTime(), default=datetime.utcnow) 10 | 11 | 12 | def __repr__(self): 13 | return "" % self.title 14 | 15 | # Generate marshmallow Schemas from your models 16 | class ArticlesShema(ma.Schema): 17 | class Meta: 18 | # Fields to expose 19 | fields = ("id","title", "body", "date") 20 | 21 | 22 | article_schema = ArticlesShema() 23 | articles_schema = ArticlesShema(many=True) -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | def deploy(): 2 | """Run deployment tasks.""" 3 | from app import create_app,db 4 | from flask_migrate import upgrade,migrate,init,stamp 5 | from models import Articles 6 | 7 | app = create_app() 8 | app.app_context().push() 9 | 10 | # create database and tables 11 | db.create_all() 12 | 13 | # migrate database to latest revision 14 | stamp() 15 | migrate() 16 | upgrade() 17 | 18 | deploy() 19 | 20 | # Error: Working outside of application context. 21 | 22 | # This typically means that you attempted to use functionality that needed 23 | # to interface with the current application object in some way. To solve 24 | # this, set up an application context with app.app_context(). See the 25 | # documentation for more information. -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React and Flask 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /backend/app.py: -------------------------------------------------------------------------------- 1 | # Import the required libraries 2 | from flask import Flask 3 | from flask_sqlalchemy import SQLAlchemy 4 | from flask_migrate import Migrate 5 | from flask_marshmallow import Marshmallow 6 | from flask_cors import CORS 7 | 8 | 9 | # Create various application instances 10 | # Order matters: Initialize SQLAlchemy before Marshmallow 11 | db = SQLAlchemy() 12 | migrate = Migrate() 13 | ma = Marshmallow() 14 | cors = CORS() 15 | 16 | 17 | def create_app(): 18 | """Application-factory pattern""" 19 | app = Flask(__name__) 20 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db" 21 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 22 | 23 | # Initialize extensions 24 | # To use the application instances above, instantiate with an application: 25 | db.init_app(app) 26 | migrate.init_app(app, db) 27 | ma.init_app(app) 28 | cors.init_app(app) 29 | 30 | return app 31 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "4.0.3", 12 | "web-vitals": "^1.1.2" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState,useEffect } from 'react' 2 | import './App.css'; 3 | import ArticleList from './Components/ArticleList' 4 | 5 | function App() { 6 | const [articles, setArticles] = useState([]); 7 | 8 | // Modify the current state by setting the new data to 9 | // the response from the backend 10 | useEffect(()=>{ 11 | fetch('http://localhost:5000/articles',{ 12 | 'methods':'GET', 13 | headers : { 14 | 'Content-Type':'application/json' 15 | } 16 | }) 17 | .then(response => response.json()) 18 | .then(response => setArticles(response)) 19 | .catch(error => console.log(error)) 20 | 21 | },[]) 22 | 23 | return ( 24 |
25 |
26 |
27 |

Connecting a React Frontend to a Flask Backend.

28 |
29 |
30 | 31 | 34 | 35 |
36 | ); 37 | } 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /backend/migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic,flask_migrate 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [logger_flask_migrate] 38 | level = INFO 39 | handlers = 40 | qualname = flask_migrate 41 | 42 | [handler_console] 43 | class = StreamHandler 44 | args = (sys.stderr,) 45 | level = NOTSET 46 | formatter = generic 47 | 48 | [formatter_generic] 49 | format = %(levelname)-5.5s [%(name)s] %(message)s 50 | datefmt = %H:%M:%S 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ONDIEK ELIJAH OCHIENG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Requirements ,Usage and Installation 2 | ## Backend - Flask 3 | ### Installation 4 | 5 | ### 1 .Clone the git repo and create an environment 6 | 7 | Depending on your operating system,make a virtual environment to avoid messing with your machine's primary dependencies 8 | 9 | **Windows** 10 | 11 | ```bash 12 | git clone https://github.com/Dev-Elie/Connecting-React-Frontend-to-a-Flask-Backend.git 13 | cd Connecting-React-Frontend-to-a-Flask-Backend/backend 14 | py -3 -m venv venv 15 | ``` 16 | 17 | **macOS/Linux** 18 | 19 | ```bash 20 | git clone https://github.com/Dev-Elie/Connecting-React-Frontend-to-a-Flask-Backend.git 21 | cd Connecting-React-Frontend-to-a-Flask-Backend/backend 22 | python3 -m venv venv 23 | ``` 24 | 25 | ### 2 .Activate the environment 26 | 27 | **Windows** 28 | 29 | ```venv\Scripts\activate``` 30 | 31 | **macOS/Linux** 32 | 33 | ```. venv/bin/activate``` 34 | or 35 | ```source venv/bin/activate``` 36 | 37 | ### 3 .Install the requirements 38 | 39 | Applies for windows/macOS/Linux 40 | 41 | ```pip install -r requirements.txt``` 42 | ### 4 .Migrate/Create a database - Optional during initial set up 43 | 44 | Applies for windows/macOS/Linux 45 | 46 | ```python manage.py``` 47 | 48 | ### 5. Run the application 49 | 50 | **For linux and macOS** 51 | Make the run file executable by running the code 52 | 53 | ```chmod 777 run``` 54 | 55 | Then start the application by executing the run file 56 | 57 | ```./run``` 58 | 59 | **On windows** 60 | ``` 61 | set FLASK_APP=routes 62 | flask run 63 | ``` 64 | OR 65 | `python routes.py` 66 | 67 | ## Frontend - React 68 | ### Installation 69 | 70 | You just need to install the packages listed on package.json, on the frontend folder. 71 | 72 | ``` 73 | cd Connecting-React-Frontend-to-a-Flask-Backend/frontend 74 | npm install 75 | ``` 76 | 77 | `npm start` 78 | 79 | ## Updating pip packages 80 | 81 | In an active virtual environment install updates using the command: 82 | 83 | `pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U` 84 | 85 | Update the reqirements.txt file. 86 | 87 | `pip freeze > requirements.txt` 88 | 89 | ![](https://github.com/Dev-Elie/Connecting-React-Frontend-to-a-Flask-Backend/blob/main/flask%2Breact.png) 90 |

Follow me on Twitter

91 |

dev_elie

92 | -------------------------------------------------------------------------------- /backend/migrations/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | import logging 4 | from logging.config import fileConfig 5 | 6 | from flask import current_app 7 | 8 | from alembic import context 9 | 10 | # this is the Alembic Config object, which provides 11 | # access to the values within the .ini file in use. 12 | config = context.config 13 | 14 | # Interpret the config file for Python logging. 15 | # This line sets up loggers basically. 16 | fileConfig(config.config_file_name) 17 | logger = logging.getLogger('alembic.env') 18 | 19 | # add your model's MetaData object here 20 | # for 'autogenerate' support 21 | # from myapp import mymodel 22 | # target_metadata = mymodel.Base.metadata 23 | config.set_main_option( 24 | 'sqlalchemy.url', 25 | str(current_app.extensions['migrate'].db.get_engine().url).replace( 26 | '%', '%%')) 27 | target_metadata = current_app.extensions['migrate'].db.metadata 28 | 29 | # other values from the config, defined by the needs of env.py, 30 | # can be acquired: 31 | # my_important_option = config.get_main_option("my_important_option") 32 | # ... etc. 33 | 34 | 35 | def run_migrations_offline(): 36 | """Run migrations in 'offline' mode. 37 | 38 | This configures the context with just a URL 39 | and not an Engine, though an Engine is acceptable 40 | here as well. By skipping the Engine creation 41 | we don't even need a DBAPI to be available. 42 | 43 | Calls to context.execute() here emit the given string to the 44 | script output. 45 | 46 | """ 47 | url = config.get_main_option("sqlalchemy.url") 48 | context.configure( 49 | url=url, target_metadata=target_metadata, literal_binds=True 50 | ) 51 | 52 | with context.begin_transaction(): 53 | context.run_migrations() 54 | 55 | 56 | def run_migrations_online(): 57 | """Run migrations in 'online' mode. 58 | 59 | In this scenario we need to create an Engine 60 | and associate a connection with the context. 61 | 62 | """ 63 | 64 | # this callback is used to prevent an auto-migration from being generated 65 | # when there are no changes to the schema 66 | # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html 67 | def process_revision_directives(context, revision, directives): 68 | if getattr(config.cmd_opts, 'autogenerate', False): 69 | script = directives[0] 70 | if script.upgrade_ops.is_empty(): 71 | directives[:] = [] 72 | logger.info('No changes in schema detected.') 73 | 74 | connectable = current_app.extensions['migrate'].db.get_engine() 75 | 76 | with connectable.connect() as connection: 77 | context.configure( 78 | connection=connection, 79 | target_metadata=target_metadata, 80 | process_revision_directives=process_revision_directives, 81 | **current_app.extensions['migrate'].configure_args 82 | ) 83 | 84 | with context.begin_transaction(): 85 | context.run_migrations() 86 | 87 | 88 | if context.is_offline_mode(): 89 | run_migrations_offline() 90 | else: 91 | run_migrations_online() 92 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | --------------------------------------------------------------------------------