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.