DEV Community

Cover image for Build Your Own Forum with FastAPI: Step 1 - A Minimal Forum
Leapcell
Leapcell

Posted on

Build Your Own Forum with FastAPI: Step 1 - A Minimal Forum

There are already many forum products on the market, but are you still frustrated because none of them meet your unique needs? If so, why not build a forum from scratch yourself?

Don't be intimidated by giant forum SaaS platforms like Discourse; building a forum isn't as difficult as it seems.

In the upcoming series of articles, we will guide you step-by-step from scratch to build a fully functional, modern forum website using the popular Python web framework, FastAPI.

This tutorial series is tailored for beginners. By the end of this first article, you will have a runnable mini-forum, just like the one shown below:

Without further ado, let's get started:

Step 1: Environment Setup

First, let's prepare the development environment.

Create a Project Directory and Virtual Environment

Create a dedicated folder for the project and create a virtual environment within it.

For simplicity, we'll use Python's built-in venv module. There are many other great virtual environment tools, such as poetry, which you can explore on your own.

# Create and enter the project directory
mkdir fastapi-forum
cd fastapi-forum

# Create the virtual environment
python3 -m venv venv

# Activate the virtual environment
# Windows:
# venv\Scripts\activate
# macOS / Linux:
source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

After successful activation, you will see (venv) prepended to your command line prompt.

Install FastAPI and Uvicorn

Next, we'll install the core libraries needed for this article: fastapi and uvicorn.

  • fastapi: The framework itself.
  • uvicorn: An ASGI server used to run our FastAPI application.
pip install fastapi "uvicorn[standard]"
Enter fullscreen mode Exit fullscreen mode

"uvicorn[standard]" installs uvicorn along with some recommended dependencies for optimal performance.

Step 2: "Hello, Forum!" Get the FastAPI App Running

With the environment ready, let's write our first line of FastAPI code. Create a file named main.py in the fastapi-forum directory and add the following content:

main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello, Forum!"}
Enter fullscreen mode Exit fullscreen mode

What does this code do?

  1. It creates an instance of FastAPI and names it app.
  2. @app.get("/") is a decorator. It tells FastAPI that the function read_root below will handle GET requests coming to the path /.
  3. When a request hits the root path, the function returns a Python dictionary, which FastAPI automatically converts into a JSON response.

Next, run the application from your terminal:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode
  • main: Refers to the main.py file.
  • app: Refers to the FastAPI instance app you created in main.py.
  • --reload: This is a very useful parameter that makes the server restart automatically after code changes.

Now, open your browser and navigate to http://127.0.0.1:8000. You will see:

{ "message": "Hello, Forum!" }
Enter fullscreen mode Exit fullscreen mode

Congratulations! Your first FastAPI application is now running successfully!

Step 3: Define the Core Data Structure

Our forum is for publishing posts, so we need a clear data structure to define what a "post" looks like. FastAPI recommends using Pydantic for data definition and validation. It's built into FastAPI and is very powerful.

Let's define a Post model in main.py.

main.py (Updated)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

# 1. Define the data model
class Post(BaseModel):
    id: int
    title: str
    content: str

app = FastAPI()

# 2. Use a list as an in-memory database
# Note: This is for demonstration only. Data will be lost upon server restart!
db: List[Post] = [
    Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."),
    Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."),
]

@app.get("/")
def read_root():
    return {"message": "Welcome to my forum!"}

# Subsequent APIs will be added here...
Enter fullscreen mode Exit fullscreen mode

We created a Post class that inherits from pydantic.BaseModel. We defined that a post should have three fields: id (integer), title (string), and content (string).

We also created a list named db and pre-populated it with two Post objects to simulate our database.

Step 4: Implement the Core APIs

A basic forum needs two features: viewing posts and creating posts. Let's implement the corresponding API endpoints.

1. Get All Posts

We need an endpoint that returns all the posts in our db list. Add the following code to main.py:

main.py (Continued)

... # Previous code remains unchanged

# 3. API to get the list of posts
@app.get("/api/posts", response_model=List[Post])
def get_posts():
    return db
Enter fullscreen mode Exit fullscreen mode
  • @app.get("/api/posts"): We define a new path /api/posts that handles GET requests.
  • response_model=List[Post]: This tells FastAPI that the response body for this endpoint will be a list of Post objects. FastAPI uses this for data validation, conversion, and clear documentation in the API docs.

2. Create a New Post

Next is the endpoint for creating a new post.

First, install python-multipart to support handling form data:

pip install python-multipart
Enter fullscreen mode Exit fullscreen mode

main.py (Continued)

# ... Previous code remains unchanged

# 4. API to create a new post
@app.post("/api/posts", response_model=Post)
def create_post(title: str = Form(...), content: str = Form(...)):
    new_id = len(db) + 1
    new_post = Post(
        id=new_id,
        title=title,
        content=content
    )
    db.append(new_post)
    return new_post
Enter fullscreen mode Exit fullscreen mode

The parameter format title: str = Form(...) tells FastAPI to extract the data from form fields.

Why are the input/output formats form fields? Because we will later create an HTML page where users can submit post content through a form.

Step 5: A Simple Interactive Page

A pure API can't really be called a forum. We can use FastAPI's HTMLResponse to quickly create a usable HTML page, allowing users to create and view posts directly in their browser.

We will create a new path GET /posts to display this page.

main.py (Final Complete Version)

from fastapi import FastAPI, Form
from fastapi.responses import HTMLResponse, RedirectResponse
from pydantic import BaseModel
from typing import List

# --- Data Models ---
class Post(BaseModel):
    id: int
    title: str
    content: str

# --- In-Memory Database ---
db: List[Post] = [
    Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."),
    Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."),
]

app = FastAPI()

# --- HTML Template ---
def generate_html_response():
    posts_html = ""
    for post in reversed(db):  # Show new posts at the top
        posts_html += f"""
        <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;">
            <h3>{post.title} (ID: {post.id})</h3>
            <p>{post.content}</p>
        </div>
        """

    html_content = f"""
    <html>
        <head>
            <title>My FastAPI Forum</title>
            <style>
                body {{ font-family: sans-serif; margin: 2em; }}
                input, textarea {{ width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }}
                button {{ padding: 10px 15px; background-color: #007BFF; color: white; border: none; cursor: pointer; }}
                button:hover {{ background-color: #0056b3; }}
            </style>
        </head>
        <body>
            <h1>Welcome to My Forum</h1>
            <h2>Create a New Post</h2>
            <form action="/api/posts" method="post">
                <input type="text" name="title" placeholder="Post Title" required><br>
                <textarea name="content" rows="4" placeholder="Post Content" required></textarea><br>
                <button type="submit">Post</button>
            </form>
            <hr>
            <h2>Post List</h2>
            {posts_html}
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

# --- Routes ---

@app.get("/", response_class=RedirectResponse)
def read_root():
    # Redirect the root path to our view page
    return "/posts"

# Route to display the page
@app.get("/posts", response_class=HTMLResponse)
async def view_posts():
    return generate_html_response()

@app.post("/api/posts")
def create_post(title: str = Form(...), content: str = Form(...)):
    new_id = len(db) + 1
    new_post = Post(
        id=new_id,
        title=title,
        content=content
    )
    db.append(new_post)
    # Redirect back to the main page after posting for a refresh effect
    return RedirectResponse(url="/posts", status_code=303)
Enter fullscreen mode Exit fullscreen mode

We use the generate_html_response function to dynamically generate a complete HTML page containing the form and the post list. While primitive, it's perfectly sufficient for this stage.

GET /posts will directly return the generated HTML page, allowing users to see the forum interface when they visit /posts. Additionally, the root path / is redirected to /posts, so users visiting the root URL are automatically taken to the forum page.

You might notice that GET /api/posts has been removed. This is because we no longer need a separate API to get the post list; the page reads the data directly from the in-memory db.

We have also modified the logic for POST /api/posts. Instead of returning a Post object, it now redirects back to the /posts page so that users can see the updated post list immediately after submission.

Run the Forum

Now, make sure your uvicorn server is still running. Refresh or revisit http://127.0.0.1:8000, and you will see the forum page with existing posts and a form to create new ones.

ImageP1

After entering content into the form and submitting it, you will see your new post appear in the post list.

ImageP2

Deploying the Project Online

A forum is meant to be used by everyone, so just running it locally isn't enough. Next, we can deploy it online.

A simple deployment option is to use Leapcell. It's a web app hosting platform that can host projects in various languages and frameworks, including FastAPI, of course.

Leapcell

Follow the steps below:

  1. Register an account on the website.
  2. Commit your project to GitHub. You can refer to GitHub's official documentation for the steps. Leapcell will pull the code from your GitHub repository later.
  3. Click "Create Service" on the Leapcell page. LeapcellImageP1
  4. After choosing your FastAPI repo, you'll see Leapcell has auto-populated the necessary configurations. LeapcellImageP2
  5. Click "Submit" at the bottom to deploy. The deployment will complete quickly and return you to the deployment homepage. Here we can see that Leapcell has provided a domain. This is the online address of your blog. LeapcellImageP3

Conclusion

In just a few short steps, we have built a forum prototype with the most essential features from scratch using FastAPI.

This forum is still very incomplete. For example, as soon as the server restarts, all the new posts disappear. This is obviously unacceptable.

In the next article, we will introduce a real database to our forum to enable persistent data storage.


Follow us on X: @LeapcellHQ


Read on our blog

Related Posts:

Top comments (0)