Using Mountaineer to develop a React app with Python

Mountaineer is a powerful framework that makes it easy to build web apps using both Python and React. It leverages your existing knowledge of Python and TypeScript, letting you use each language for what it does best.

In this guide, we’ll walk through how to set up a Mountaineer project and build a simple app that integrates both frontend and backend components seamlessly.

Setting Up the Mountaineer Development Environment

Before you start, you need to set up your environment correctly. Mountaineer integrates a React (Node.js) project into a Python environment, so version compatibility is important.

Required Updates:

  • Rust: Version 1.77 or later
  • Go: Version 1.22 or later
  • Node.js: Version 20 or later
  • Python: Version 3.11 or later

Once your environment is ready, you can create a new Mountaineer project by running:

pipx run create-mountaineer-app

This command sets up the project structure with all necessary dependencies for both Python and Node environments.

Example Command Output:

You’ll be prompted to provide some details like the project name, author, and whether you want to use tools like Poetry for dependency management. Here’s a sample interaction:

$ pipx run create-mountaineer-app
? Project name [my-project]: microblog
? Author [Your Name <youremail@example.com>]
? Use poetry for dependency management? [Yes] Yes
? Create stub MVC files? [Yes] No
? Use Tailwind CSS? [Yes] No
? Add editor configuration? [vscode] vscode

Creating project...
# [Creating and setting up files...]

This creates the project structure, including configurations for Python, React, Docker, and more.

Running the Server

Once the setup is complete, you can start the server with:

poetry run runserver

Visit http://127.0.0.1:5006/ to see your app in action. At first, the page will be blank, but this is where you’ll start building.

Adding a Controller and a View

In Mountaineer, a controller handles the backend logic, while a view renders the frontend.

Creating a Simple Controller

Let’s start by creating a home.py controller:

from mountaineer import ControllerBase

class HomeController(ControllerBase):
    url = "/"
    view_path = "/app/home/page.tsx"

    async def render(self) -> None:
        pass

his controller doesn’t return any data yet. We’ll register it in app.py:

from mountaineer.app import AppController
from intro_to_mountaineer.controllers.home import HomeController

controller = AppController(config=AppConfig())
controller.register(HomeController())

Adding a Simple View

Next, create the corresponding view in page.tsx under the /views/app/home/ directory:

import React from "react";

const Home = () => {
  return (
    <div>
      <h1>Home</h1>
      <p>Hello, world!</p>
    </div>
  );
};

export default Home;

This view displays a basic “Hello, world!” message.

Adding a Model

Mountaineer supports PostgreSQL, making it easy to manage your app’s data. Let’s create a BlogPost model in blogpost.py:

from mountaineer.database import SQLModel, Field
from uuid import UUID, uuid4
from datetime import datetime

class BlogPost(SQLModel, table=True):
    id: UUID = Field(default_factory=uuid4, primary_key=True)
    text: str
    date: str = datetime.now()

Add this model to your project by updating __init__.py:

from .blogpost import BlogPost

Setting Up the Database

Start the PostgreSQL database with Docker and create the table:

docker-compose up -d
poetry run createdb

This will automatically create the BlogPost table in your database.

Connecting the Frontend to the Backend

Let’s extend our app to handle adding new blog posts.

Updating the Controller

Add a method to handle new blog posts in home.py:

class HomeController(ControllerBase):
    url = "/"
    view_path = "/app/home/page.tsx"

    async def render(self) -> None:
        pass

    @sideeffect
    async def add_blogpost(self, payload: str, session: AsyncSession) -> None:
        new_blogpost = BlogPost(text=payload)
        session.add(new_blogpost)
        await session.commit()

Updating the View

Now, add a form to your view in page.tsx to submit new blog posts:

import React, { useState } from "react";

const Home = () => {
  const [newBlogpost, setNewBlogpost] = useState("");

  const handleSubmit = async () => {
    await serverState.add_blogpost({ payload: newBlogpost });
    setNewBlogpost("");
  };

  return (
    <div>
      <h1>Home</h1>
      <input 
        type="text" 
        value={newBlogpost} 
        onChange={(e) => setNewBlogpost(e.target.value)} 
      />
      <button onClick={handleSubmit}>Post</button>
    </div>
  );
};

export default Home;

This form sends data to the backend when the “Post” button is clicked, adding a new blog post.

Displaying Data from the Database

Finally, let’s display the blog posts stored in the database.

Updating the Controller

Modify the render method in home.py to fetch and display blog posts:

class HomeController(ControllerBase):
    url = "/"
    view_path = "/app/home/page.tsx"

    async def render(self, session: AsyncSession) -> HomeRender:
        posts = await session.execute(select(BlogPost))
        return HomeRender(posts=posts.scalars().all())

Updating the View

Update the view to display the fetched posts:

const ShowPosts = ({ serverState }) => {
  return (
    <div>
      {serverState.posts.map((post) => (
        <div key={post.id}>
          <div>{post.text}</div>
          <div>{post.date}</div>
        </div>
      ))}
    </div>
  );
};

This will show all blog posts stored in the database on your homepage.

Conclusion

Mountaineer makes it easy to integrate Python and React, allowing you to build full-stack applications efficiently. By following this guide, you now have a basic understanding of how to set up a Mountaineer project and connect your frontend and backend.

Leave a Reply

Your email address will not be published. Required fields are marked *