Building a REST API is a core skill for any backend developer, and Flask makes it surprisingly approachable. In this guide we’ll walk through the entire process of creating a clean, maintainable API from scratch—complete with authentication, validation, and testing. By the end you’ll have a ready‑to‑deploy service you can extend to power anything from a mobile app to a microservice architecture.
What You’ll Need
- A machine running Linux, macOS, or Windows with Python 3.9+
- Basic command‑line knowledge (terminal or PowerShell)
- Git installed for version control
- Optional: Docker for containerizing the app
- Text editor or IDE (VS Code, PyCharm, Sublime, etc.)
Step 1: Scaffold the Project
Start by creating a project directory and a virtual environment to keep dependencies isolated.
mkdir flask_api_demo
cd flask_api_demo
python -m venv venv
source venv/bin/activate # Windows: venvScriptsactivate
pip install --upgrade pip
Next, install the core packages: Flask, Flask‑RESTful for routing, and Flask‑SQLAlchemy for ORM support. We’ll also add Flask‑Marshmallow for schema validation.
pip install flask flask-restful flask-sqlalchemy flask-marshmallow marshmallow-sqlalchemy
Initialize a Git repo so you can track changes.
git init
Create the basic file structure:
flask_api_demo/
├── app.py
├── models.py
├── schemas.py
├── resources/
│ └── user.py
└── requirements.txt
Step 2: Define the Data Model
Open models.py and set up a simple User model using SQLAlchemy.
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, server_default=db.func.now())
def __repr__(self):
return f"<User {self.username}>"
In app.py, configure the database URI and initialize the extensions.
from flask import Flask
from models import db
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
with app.app_context():
db.create_all()
Step 3: Build Schemas for Validation
We’ll use Marshmallow to serialize and deserialize data. In schemas.py:
from flask_marshmallow import Marshmallow
from models import User
ma = Marshmallow()
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User
load_instance = True
sqla_session = db.session
Instantiate the schema in app.py for later use.
from schemas import ma, UserSchema
ma.init_app(app)
user_schema = UserSchema()
users_schema = UserSchema(many=True)
Step 4: Create API Resources
Under resources/user.py, define the REST endpoints using Flask‑RESTful.
from flask_restful import Resource, reqparse
from models import db, User
from schemas import users_schema, user_schema
parser = reqparse.RequestParser()
parser.add_argument("username", type=str, required=True, help="Username cannot be blank")
parser.add_argument("email", type=str, required=True, help="Email cannot be blank")
class UserList(Resource):
def get(self):
users = User.query.all()
return users_schema.dump(users), 200
def post(self):
args = parser.parse_args()
if User.query.filter_by(username=args["username"]).first():
return {"message": "Username already exists"}, 400
new_user = User(username=args["username"], email=args["email"])
db.session.add(new_user)
db.session.commit()
return user_schema.dump(new_user), 201
class UserResource(Resource):
def get(self, user_id):
user = User.query.get_or_404(user_id)
return user_schema.dump(user), 200
def delete(self, user_id):
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return {"message": "Deleted"}, 200
Register the resources in app.py:
from flask_restful import Api
from resources.user import UserList, UserResource
api = Api(app)
api.add_resource(UserList, "/users")
api.add_resource(UserResource, "/users/<int:user_id>")
Step 5: Run the Development Server
Back in the root, create a requirements.txt by running pip freeze > requirements.txt so collaborators can install dependencies quickly.
Start the Flask development server:
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
Open http://127.0.0.1:5000/users in your browser or use curl to test the endpoints:
curl -X POST http://127.0.0.1:5000/users
-H "Content-Type: application/json"
-d '{"username": "alice", "email": "[email protected]"}'
If you get a JSON payload back, your API is working!
Common Mistakes to Avoid
1. Forgetting to initialize extensions inside an app context. Flask‑SQLAlchemy and Marshmallow need the app to be configured before you call init_app or create tables.
2. Using a global database session. Stick to db.session provided by SQLAlchemy; sharing sessions across threads can lead to race conditions.
3. Hard‑coding the database URI. For production, use environment variables and a proper RDBMS like PostgreSQL.
4. Returning raw model objects. Always serialize with Marshmallow to avoid leaking sensitive fields or causing circular reference errors.
5. Skipping input validation. Relying solely on database constraints can lead to confusing 500 errors. Use request parsers or Marshmallow schemas to validate early.
Tips and Tricks
Use Flask‑Blueprints for modularity. As your API grows, split resources into separate modules and register them with app.register_blueprint().
Leverage Flask‑CLI commands. Create custom commands to seed data, run migrations, or perform health checks.
Add pagination. For list endpoints, accept page and per_page query params and use paginate() to limit response size.
Integrate Swagger/OpenAPI. Use flasgger or flask-restx to auto‑generate interactive docs.
Test with pytest. Write unit tests for each resource; use flask.testing.FlaskClient to simulate requests.
Frequently Asked Questions
Do I need a separate database for each environment?
Not necessarily. You can use SQLite for local dev, but for staging and production, switch to PostgreSQL or MySQL. Use environment variables to point Flask at the correct URI.
Can I add authentication to this API?
Yes. A common approach is to use Flask‑JWT‑Extended. Install it with pip install flask-jwt-extended and protect routes with @jwt_required() decorators. Store user credentials in a users table and issue tokens upon login.
How do I deploy this Flask API to a cloud provider?
Containerize the app with Docker, push the image to a registry, and deploy to services like Heroku, AWS Elastic Beanstalk, or Google Cloud Run. Ensure you set the FLASK_APP and FLASK_ENV environment variables appropriately.
Conclusion
Building a REST API with Flask is a rewarding exercise that teaches you the fundamentals of web development, database modeling, and clean code practices. By following these steps, you’ve created a solid foundation that can scale to complex applications, integrate authentication, and be containerized for production. Keep experimenting—add more resources, enforce stricter validation, or swap in a different ORM—and you’ll be well on your way to mastering backend development.
Photo by Ferenc Almasi on Unsplash





