If you have worked with React or Next.js, you already know how to build user interfaces. You can create forms, display data, handle button clicks, and navigate between pages.
But where does the data come from?
Imagine you're building a job portal. Users should be able to view available jobs, companies should be able to publish job openings, and registered users should be able to apply for jobs.
Your frontend alone cannot safely handle all these responsibilities. You need a backend.
We'll learn by implementing each feature rather than studying backend concepts in isolation.
Let's start!
A backend is the part of an application that runs on a server and handles tasks such as:
Receiving requests from the frontend.
Validating incoming data.
Applying business rules.
Reading and writing data in a database.
Authenticating users and checking permissions.
Sending responses back to the frontend.
For example, when a user opens a job portal, the frontend might request a list of available jobs. The backend processes that request, retrieves the relevant data, and returns it to the frontend.
A simplified architecture looks like this:
┌─────────────────────┐
│ React Frontend │
│ │
│ Job Listings UI │
└──────────┬──────────┘
│
│ HTTP Request
▼
┌─────────────────────┐
│ Flask Backend │
│ │
│ Routing & Logic │
│ Validation │
└──────────┬──────────┘
│
│ SQL Query
▼
┌─────────────────────┐
│ PostgreSQL │
│ │
│ Jobs & Companies │
└─────────────────────┘The frontend sends an HTTP request to the backend. Flask processes the request, communicates with the database when necessary, and returns an HTTP response.
Important: Flask doesn't automatically provide a database, authentication system, or API endpoints. We'll add these capabilities as our project grows.
API stands for Application Programming Interface. In our application, an API provides a defined way for the frontend and backend to communicate.
Suppose our frontend needs to display all available jobs. We might create an endpoint like this:
GET /api/jobsThe frontend sends a request to this endpoint, and the backend returns JSON:
[
{
"id": 1,
"title": "Frontend Developer",
"company": "Tech Solutions"
},
{
"id": 2,
"title": "Backend Developer",
"company": "Digital Labs"
}
]The frontend can then use this response to render job cards.
For this series, we'll primarily build REST-style APIs that use HTTP methods and URLs to represent operations on resources.
Here are some common HTTP methods we'll use:
Method | Purpose | Example |
|---|---|---|
GET | Retrieve data | Get all jobs |
POST | Create a resource | Create a job |
PUT | Replace an existing resource | Replace a job |
PATCH | Partially update a resource | Update a job title |
DELETE | Delete a resource | Delete a job |
We'll implement these operations in later articles. For now, we only need to understand how to create an endpoint and return a response.
Flask is a lightweight Python web framework that helps us build web applications and APIs.
We'll use Flask because it allows us to start with a small application and gradually introduce the tools needed for a more complete backend.
Our technology choices for this series are:
Flask: Handles HTTP requests and routes.
PostgreSQL: Stores application data.
SQLAlchemy: Lets us interact with the database using Python objects and queries.
Flask-Migrate: Manages database schema changes.
Flask-JWT-Extended: Helps implement JWT-based authentication.
pytest: Tests our API endpoints.
We won't install everything immediately. We'll introduce each dependency when the project needs it.
Before writing our first endpoint, let's create a project directory.
You should have Python 3 installed. We'll use a virtual environment so that the dependencies for this project remain separate from other Python projects.
Open your terminal and run:
mkdir jobhub-backend
cd jobhub-backend
python3 -m venv venv
source venv/bin/activateOn Windows, activate the virtual environment using:
venv\Scripts\Activate.ps1Next, install Flask:
pip install FlaskCreate a requirements.txt file to record the dependency:
pip freeze > requirements.txtThis file will help us reproduce the project's environment later.
Create a file named app.py.
Our initial project structure is deliberately simple:
jobhub-backend/
├── venv/
├── app.py
└── requirements.txtAs the application grows, we'll introduce separate folders for routes, models, configuration, and other responsibilities.
For now, let's focus on making our first API work.
Open app.py and add the following code:
from flask import Flask, jsonify
app = Flask(__name__)
@app.get("/api/health")
def health_check():
return jsonify({
"status": "success",
"message": "JobHub API is running"
}), 200
if __name__ == "__main__":
app.run(debug=True)
Let's understand the important parts.
app = Flask(__name__)
This creates our Flask application. Flask uses it to register routes and handle incoming requests.
@app.get("/api/health")
This decorator registers a route that accepts GET requests at /api/health. When a client requests this URL using GET, Flask executes the associated health_check() function.
jsonify()
This converts our Python dictionary into a JSON HTTP response.
app . run(debug=True)
This starts Flask's development server. Debug mode is useful while learning because it provides helpful debugging information and reloads the server when code changes. Debug mode is intended for local development, not production.
Notice that we haven't written any Python program from scratch to handle HTTP requests. Flask handles the underlying web-server integration and routing for us.
Start the application from your project directory:
python app.pyFlask will start a local development server, usually at:
http://127.0.0.1:5000Keep the terminal running.
Now open your browser and visit:
http://127.0.0.1:5000/api/healthYou should receive a response similar to this:
{
"message": "JobHub API is running",
"status": "success"
}Congratulations! You've created and run your first Flask API.
You can also test the same endpoint from another terminal:
curl -i http://127.0.0.1:5000/api/healthThe -i option displays the HTTP response headers along with the response body.
You should see a response containing:
HTTP/1.1 200 OK
Content-Type: application/jsonThe response body contains the JSON returned by your Flask route.
You can also send a GET request through Postman by entering the same URL and selecting the GET method.
Testing APIs independently of the frontend is a useful habit. It helps you determine whether a problem is in the backend or in the frontend's request-handling code.
Let's make the application a little more useful.
Add the following route below health_check() in app.py:
@app.get("/api/jobs")
def get_jobs():
jobs = [
{
"id": 1,
"title": "Frontend Developer",
"company": "Tech Solutions"
},
{
"id": 2,
"title": "Backend Developer",
"company": "Digital Labs"
}
]
return jsonify(jobs), 200Save the file. If you are running Flask in debug mode, the development server should reload automatically.
Visit:
http://127.0.0.1:5000/api/jobsYou should now receive a JSON array containing two jobs.
We have created two endpoints:
Endpoint | Purpose |
|---|---|
GET /api/health | Checks whether the API is responding |
GET /api/jobs | Returns a list of sample jobs |
However, there's an important limitation.
Our jobs are stored in a Python list. Every time the application restarts, the list is recreated from the code. We cannot reliably manage job records this way, and there is no way to create, update, or delete persistent records.
In the next article, we'll connect PostgreSQL and introduce SQLAlchemy so that our application can work with actual database records.
Problem 1: ModuleNotFoundError: No module named 'flask'
Flask might not be installed in your active environment.
Activate your virtual environment and install Flask:
pip install FlaskProblem 2: Port 5000 is already in use
Another application might already be using that port. You can change the development port:
app.run(debug=True, port=5001)Then visit http://127.0.0.1:5001/api/health.
Problem 3: The browser shows 404 Not Found
Check that the URL matches the registered route exactly. Our endpoint is /api/health, not /health.
Problem 4: The browser shows 405 Method Not Allowed
Our route accepts GET requests. If you send a POST request to it, Flask will reject the method. We'll learn to create POST endpoints in a later part.
Before moving to the next article, try these tasks yourself.
Create a new endpoint: GET /api/about.
Return JSON containing the application name, version, and a short description.
Create a GET /api/jobs/1 endpoint that returns the details of one job.
Return a 404 Not Found response when the requested job ID does not exist.
Test all three endpoints using Postman or curl.
For the single-job endpoint, you can start with the same Python list we used earlier. We haven't introduced a database yet, so using sample data is fine.
Hint: You can access a URL parameter with a route such as @app.get("/api/jobs/<int:job_id>").
Try implementing the challenge before looking for a complete solution. The goal is to become comfortable with registering routes, handling request parameters, and returning JSON responses.
In Part 2: Connecting Flask to PostgreSQL with SQLAlchemy, we'll move beyond sample data and start building the actual backend foundation.
We'll configure environment variables, install the PostgreSQL driver, connect our Flask application to the database, and introduce a more maintainable project structure using an application factory and Blueprints.
By the end of Part 2, we'll have a Flask application connected to PostgreSQL and ready for database models and CRUD operations.
See you in Part 2!
Key takeaway: A backend API receives requests, executes application logic, and returns responses. With Flask, we've created our first working endpoints. Next, we'll connect the application to a database so that it can work with persistent data.