Chapter 8: Building a CRUD app with Streamlit
This chapter introduces CRUD as the common create, read, update, and delete pattern and previews building a Streamlit CRUD app with persistent storage and user authentication.
After this chapter you will be able to
- CRUD operation model. Explain CRUD as the four operations Create, Read, Update, and Delete, and describe how they recur in many kinds of software.
- Haiku Haven purpose. Describe Haiku Haven as a website for users to write, share, refine, and manage their own haikus.
- CRUD haiku operations. Explain that Haiku Haven supports creating haikus, reading previously created haikus, updating an existing haiku, and deleting one the user no longer wants.
- Haiku Haven core workflow. Describe Haiku Haven as a website where users create accounts, log in, and create, view, update, and delete their own haikus.
- Haiku Haven page flows. Describe the distinct user flows and pages envisioned for Haiku Haven: account creation, login and logout, the logged-in haiku experience, and haiku authoring.
- Authenticated haiku management. Explain that a user sets a password during signup, uses it to authenticate on the login page, and then reaches the My haikus experience to create, view, edit, or delete haikus; authoring happens on the haiku editor page.
- Three-component web architecture. Describe Haiku Haven’s frontend, backend, and database architecture, including the primary responsibility of each component.
- Frontend-to-backend action flow. Explain that users interact with frontend widgets and that each major action, such as account creation or haiku updating, calls a corresponding backend function.
- Database persistence and retrieval. Explain that the database permanently stores user and haiku information in tables and makes the stored information easy to retrieve.
- Need for persistent storage. Explain why Haiku Haven needs to store data outside the app itself, including what users must be able to do after closing the browser, logging out, and returning later.
- Relational database structure. Describe how a relational database organizes data into tables of rows and columns, and explain what rows, columns, and schemas represent.
- Persistent database storage. Distinguish database persistence from pandas dataframe storage by explaining that dataframes live in memory while a program runs, whereas database data remains after the program finishes.
- Data modeling workflow. Describe the steps for modeling an app’s data: identify entities, define their relationships, list their attributes, and convert them into a relational database schema.
- Haiku Haven entities and attributes. Describe Haiku Haven’s two entities and their attributes: users have usernames and passwords, while haikus have text, an author, a numeric haiku ID, and a creation time.
- User-to-haiku key relationship. Explain that one user can write many haikus but each haiku has one user author, making this a one-to-many relationship; describe how the haikus.author foreign key refers to the users.username primary key.
- PostgreSQL transactional role. Explain why the chapter uses PostgreSQL for Haiku Haven and distinguish its transactional use case from BigQuery’s analytical data-warehouse use case.
- Restricted Haiku Haven database. Trace the setup of a Haiku Haven-specific PostgreSQL role and database: log in as the administrative postgres user, create a narrowly scoped user, grant it database-creation ability, reconnect as that user, create haikudb, and connect to it.
- SQL purpose and CRUD commands. Explain SQL as the language used to define, change, and query database tables, and identify CREATE TABLE, INSERT INTO, SELECT, UPDATE, DELETE FROM, and DROP TABLE as the basic commands demonstrated.
- Haiku Haven table schema. Describe how CREATE TABLE defines the users and haikus schemas, including their columns, data types, primary keys, and the foreign-key relationship from haikus.author to users.username.
- Row insertion and selection. Describe how INSERT INTO supplies values for named columns and how SELECT retrieves all or chosen columns, optionally filtering rows with WHERE, AND, and LIKE patterns such as %fox%.
- Streamlit SQL connection setup. Describe how st.connection connects the app to PostgreSQL, including the required SQLAlchemy and psycopg2-binary packages and the named SQL connection type.
- Reusable Database query interface. Explain how the Database class stores a Streamlit connection and exposes execute_query(query, params, write) so app code can run both read and write SQL without repeating connection logic.
- Safe transactional query execution. Trace how execute_query opens a SQLAlchemy session, executes text(query) with params, commits successful writes, returns fetched rows, and rolls back a failed write before re-raising the error.
- Secrets-based connection configuration. Describe how .streamlit/secrets.toml configures connections.haikuconn with a PostgreSQL URL containing the server address, port, credentials, and database name, while keeping that file out of Git.
- User account purpose. Explain why Haiku Haven needs user accounts: users need accounts to hold the haikus they create.
- Service-based backend organization. Explain how Haiku Haven divides backend actions into service classes organized by responsibility, with UserService handling user-related actions and HaikuService handling haiku-related actions.
- Hub coordinator role. Describe the Hub class as the frontend's backend contact point and, in the service-based design, as a coordinator of focused services rather than a class that implements every action itself.
- Secure password hashing. Explain why Haiku Haven stores a password_hash rather than a plaintext password, and describe how the User class hashes a submitted password and authenticates a later password attempt with bcrypt.
- UserService account operations. Trace how UserService retrieves an existing user and creates a new one, including its parameterized SQL queries, duplicate-username check, and conversion of returned database tuples into User objects.
- Hub-mediated signup flow. Describe how Hub gives the Streamlit frontend access to UserService and trace the signup page flow from password confirmation through create_user to a success or error message.
- Multi-page app structure. Describe how a Streamlit multi-page app separates each page into its own Python script while the entrypoint file acts as a router that chooses and loads the current page. Explain how the former signup flow is moved from main.py into frontend/signup.py.
- Page definitions and navigation. Explain how st.Page defines a page from its script path, title, and optional icon, and how a dictionary of Page objects is supplied to st.navigation. Describe that st.navigation returns the user-selected page, or the first page by default, and that calling .run() loads it.
- Shared Hub session state. Explain that st.session_state is shared across pages in a multi-page app, so main.py saves the Hub instance there and signup.py retrieves hub from session state instead of creating or receiving it locally.
- UserService authentication flow. Explain how get_authenticated_user uses a supplied username and password to find a user through get_user and authenticate that user. State that it returns the User object on success and None when either the username is absent or the password is wrong.
- Session-state login page. Describe how the login page calls UserService when the Log In button is clicked and interprets a returned User object as success versus None as failure. Explain that a successful login stores logged_in and the User object under st.session_state, then shows a success message; failure shows an error message.
- Context-sensitive page navigation. Describe how Haiku Haven shows different navigation choices according to the logged_in session-state value: signup and login when logged out, and home and logout when logged in. Explain that placing home first in the list passed to st.navigation makes it the default logged-in page.
- Authentication-triggered reruns. Trace the automatic redirection flow for login and logout: changing logged_in affects what main.py selects on the next rerun, and st.rerun makes that transition immediate. Describe that login leads to the logged-in home page, while logout reverses the state and returns the user to the logged-out login/signup flow.
- Haiku CRUD capabilities. State that the central app capability introduced here is allowing authenticated users to create, read, update, and delete haikus.
- Haiku dataclass structure. Describe the Haiku dataclass and explain how its fields correspond to the columns in the haikus database table.
- Haiku creation service. Explain how HaikuService creates a haiku: its create_haiku SQL supplies the author and text, while the database generates haiku_id and created_at and returns the completed record through RETURNING.
- Modal haiku editor. Describe how the haiku_editor function uses st.dialog to present a modal editor and st.text_area to accept multiline haiku text from the user.
- Haiku save workflow. Trace the save action from clicking Save Haiku through calling HaikuService.create_haiku to store the entered text in the database, followed by a success or failure message.
- Haiku service read-update-delete methods. Describe the three HaikuService methods added for the remaining CRUD operations: get_haikus_by_author reads a user’s haikus, update_haiku changes a haiku identified by haiku_id, and delete_haiku removes one identified by haiku_id.
- Haiku list display design. Explain how the frontend displays the current user’s haikus: it retrieves them by author, loops through the results, and passes each Haiku object to display_haiku, while showing an informational message when the list is empty.
- Editing and deleting existing haikus. Trace how edit and delete actions work in the UI: the existing haiku editor is reused with an optional Haiku argument, prepopulates the text for edits, and calls update_haiku with haiku_id; Delete calls delete_haiku, and successful changes trigger st.rerun so the list refreshes.
- PostgreSQL production requirement. Explain the additional deployment requirement for Haiku Haven: unlike the earlier app, it needs a running PostgreSQL server to host its database.
- Managed database provisioning. Explain why Haiku Haven needs an external managed PostgreSQL server in production and describe the Neon setup choices: creating an account, choosing a project and database name, and optionally selecting a cloud provider and location.
- Production schema recreation. Describe how to recreate Haiku Haven's users and haikus tables in Neon by opening Neon’s SQL Editor and executing the same CREATE TABLE commands used locally, without changes.
- Community Cloud dependencies. Describe why Haiku Haven needs a requirements.txt for its Community Cloud deployment, and state that it includes the needed third-party modules, primarily psycopg2-binary and bcrypt.
- Production database secrets. Explain how to configure Haiku Haven's secrets after deployment: copy the contents of secrets.toml into Community Cloud's Secrets setting and replace its connection string with the connection string copied from Neon.
What it builds on
- Persisting variables across reruns (chapter 4) · The idea that Streamlit session state can persist variables across app reruns.
- Deploying changes to Streamlit Community Cloud (chapter 5) · Using requirements.txt to manage an app's Python dependencies.
- Implementing and troubleshooting the app (chapter 4) · The idea that Streamlit widgets enable user actions in an app.
- st.session_state (chapter 4) · The distinction between temporary st.session_state values and data that survives beyond a session.
- Reading and displaying a dataframe (chapter 6) · The idea of tabular data displayed as a pandas dataframe
- Updating the dashboard to load data from BigQuery (chapter 7) · Using SQL to fetch sales-data rows from Google BigQuery
- Sourcing data from a data warehouse (chapter 7) · The earlier use of BigQuery as a data source for the dashboard.
- Setting up your development environment (chapter 2) · The need to configure a local development environment.
Section by section
Introduction
This chapter introduces CRUD as the common create, read, update, and delete pattern and previews building a Streamlit CRUD app with persistent storage and user authentication.
- CRUD operation model: Explain CRUD as the four operations Create, Read, Update, and Delete, and describe how they recur in many kinds of software.
Why it matters: CRUD is the basic application pattern that the Haiku Haven app implements throughout this chapter. - Chapter implementation roadmap: Describe the chapter's planned CRUD app work: persistent relational-database storage, SQL CRUD operations, a multi-page Streamlit app, shared database connections, and user authentication.
Why it matters: This roadmap connects the individual implementation topics to the complete Streamlit application the chapter builds. - CRUD engineering significance: Explain why mastering CRUD matters for software design: although the operations are mundane, implementing them often requires solving non-trivial challenges.
Why it matters: Recognizing CRUD as foundational helps you see database, authentication, and app-structure work as central engineering skills rather than incidental details.
Key terms: CRUD, Create, Read, Update, Delete, relational database, persistent storage, SQL, multi-page Streamlit app, shared database connections
8.1 Haiku Haven: A CRUD app in Streamlit
Introduces Haiku Haven, a Streamlit app where users manage their own valid haikus through the four CRUD operations.
- Haiku Haven purpose: Describe Haiku Haven as a website for users to write, share, refine, and manage their own haikus.
Why it matters: This app provides the concrete example through which the chapter develops CRUD functionality. - Haiku structure: State that a haiku is a short three-line poem whose first and third lines have five syllables and whose second line has seven syllables.
Why it matters: Knowing the poem format explains what kind of content Haiku Haven's users create. - CRUD haiku operations: Explain that Haiku Haven supports creating haikus, reading previously created haikus, updating an existing haiku, and deleting one the user no longer wants.
Why it matters: These four actions define the CRUD behavior that the rest of the chapter builds.
Key terms: Haiku Haven, CRUD app, haiku, 5-7-5 syllable rule, CRUD, create, read, update, delete
8.1.1 Stating the concept and requirements
Defines Haiku Haven as a private, account-based haiku manager, specifies its essential CRUD requirements, and limits the chapter's scope to core workflows.
- Haiku Haven core workflow: Describe Haiku Haven as a website where users create accounts, log in, and create, view, update, and delete their own haikus.
Why it matters: This workflow defines the app that the rest of the chapter builds. - CRUD application pattern: Explain why Haiku Haven represents a general CRUD application pattern: replacing “haiku” with data such as images, tasks, or posts yields the core of many familiar apps.
Why it matters: Recognizing the pattern helps you transfer this app's design to many other data-management applications. - Deliberate scope boundaries: State that Haiku Haven focuses on private author-owned haikus and the core essentials, excluding public discovery, social interactions, pagination, and advanced security features.
Why it matters: Clear boundaries keep the implementation achievable while it teaches the essential workflows.
Key terms: Haiku Haven, username, password, CRUD, create, read, data, update, delete, out of scope
8.1.2 Visualizing the user experience
This section sketches Haiku Haven’s multi-page user experience for signup, authentication, and managing haikus.
- Haiku Haven page flows: Describe the distinct user flows and pages envisioned for Haiku Haven: account creation, login and logout, the logged-in haiku experience, and haiku authoring.
Why it matters: Mapping the pages and flows provides the user-experience blueprint for implementing the multi-page CRUD app. - Authenticated haiku management: Explain that a user sets a password during signup, uses it to authenticate on the login page, and then reaches the My haikus experience to create, view, edit, or delete haikus; authoring happens on the haiku editor page.
Why it matters: This connects authentication to the logged-in CRUD experience the app must provide.
Key terms: multi-page app, account creation flow, login, logout, CRUD, My haikus page, password, authentication, signup page, login page
8.1.3 Brainstorming the implementation
This section outlines Haiku Haven’s three-part architecture: a Streamlit frontend, a backend divided by responsibility, and a database for permanent storage and retrieval.
- Three-component web architecture: Describe Haiku Haven’s frontend, backend, and database architecture, including the primary responsibility of each component.
Why it matters: This separation provides the foundation for organizing a CRUD web application as it grows. - Frontend-to-backend action flow: Explain that users interact with frontend widgets and that each major action, such as account creation or haiku updating, calls a corresponding backend function.
Why it matters: This flow separates user interaction from the code that performs the app’s work. - Backend responsibility groups: Distinguish the two groups of backend functions: user-related actions such as account creation and authentication, and haiku-related actions for creating, reading, updating, and deleting haikus.
Why it matters: Grouping functions by what they manage makes the backend easier to organize and extend. - Database persistence and retrieval: Explain that the database permanently stores user and haiku information in tables and makes the stored information easy to retrieve.
Why it matters: Permanent, retrievable data is what allows the app to retain users and haikus beyond an individual interaction.
Key terms: frontend, backend, database, Figure 8.2, widgets, corresponding function, user actions, creating an account, authenticating a user, haiku-related actions
8.2 Setting up persistent storage
This section identifies persistent storage as necessary for Haiku Haven and chooses a relational database to keep users' haikus beyond an app session.
- Need for persistent storage: Explain why Haiku Haven needs to store data outside the app itself, including what users must be able to do after closing the browser, logging out, and returning later.
Why it matters: Persistent storage lets an app retain users' work instead of losing it when their session ends. - Relational database choice: State that this app uses a relational database as its approach to persistent data storage, while recognizing that other storage approaches exist.
Why it matters: The relational database is the storage approach that the following sections develop for Haiku Haven.
Key terms: persistent storage, store data outside of the app, Haiku Haven, relational database, data storage
8.2.1 Relational database concepts
This section introduces relational databases as persistent, table-based storage structured by schemas and accessed with SQL.
- Relational database structure: Describe how a relational database organizes data into tables of rows and columns, and explain what rows, columns, and schemas represent.
Why it matters: This vocabulary lets you reason about how an app’s persistent data is structured. - Persistent database storage: Distinguish database persistence from pandas dataframe storage by explaining that dataframes live in memory while a program runs, whereas database data remains after the program finishes.
Why it matters: Persistent storage is what allows an app to retain its data instead of losing it when execution ends. - SQL database access: State that Structured Query Language (SQL) is the language relational databases use to create, manage, and query tables, including fetching stored rows.
Why it matters: SQL is the language used to work with the tables that hold an app’s persistent data.
Key terms: relational database, table, row, record, column, field, pandas dataframes, in memory, persistent storage, database
8.2.2 Haiku Haven’s data model
This section models Haiku Haven’s users and haikus as a relational database schema with a one-to-many foreign-key relationship.
- Data modeling workflow: Describe the steps for modeling an app’s data: identify entities, define their relationships, list their attributes, and convert them into a relational database schema.
Why it matters: This workflow turns an app’s real-world concepts into a structure a database can store and query. - Haiku Haven entities and attributes: Describe Haiku Haven’s two entities and their attributes: users have usernames and passwords, while haikus have text, an author, a numeric haiku ID, and a creation time.
Why it matters: These fields define the information Haiku Haven needs in order to support accounts and stored haikus. - User-to-haiku key relationship: Explain that one user can write many haikus but each haiku has one user author, making this a one-to-many relationship; describe how the haikus.author foreign key refers to the users.username primary key.
Why it matters: Primary and foreign keys preserve the connection between each haiku and its author across separate database tables.
Key terms: entities, relationships, attributes, relational database schema, User, Haiku, username, password, text, author
8.2.3 PostgreSQL: A real relational database
This section introduces PostgreSQL as Haiku Haven’s transactional relational database and walks through installing it locally, creating a limited app user and database, and connecting through psql.
- PostgreSQL transactional role: Explain why the chapter uses PostgreSQL for Haiku Haven and distinguish its transactional use case from BigQuery’s analytical data-warehouse use case.
Why it matters: Choosing a database that handles frequent record changes and concurrent operations safely is essential for a CRUD app. - Local PostgreSQL installation: Describe how to install PostgreSQL locally and make the psql shell available from a terminal, including the alternative of invoking psql by its full path.
Why it matters: A local PostgreSQL server and shell let you create and inspect the database while developing Haiku Haven. - Restricted Haiku Haven database: Trace the setup of a Haiku Haven-specific PostgreSQL role and database: log in as the administrative postgres user, create a narrowly scoped user, grant it database-creation ability, reconnect as that user, create haikudb, and connect to it.
Why it matters: Running the app with a limited user rather than the administrative superuser reduces security risk while giving the app its own database.
Key terms: PostgreSQL, BigQuery, relational database, cloud-based data warehouse, transactional use cases, analytical use cases, PostgreSQL installer, psql, bin directory, environment variables
8.2.4 A crash course in SQL
This section introduces the SQL commands used to create Haiku Haven’s tables and perform basic create, read, update, and delete operations on their rows.
- SQL purpose and CRUD commands: Explain SQL as the language used to define, change, and query database tables, and identify CREATE TABLE, INSERT INTO, SELECT, UPDATE, DELETE FROM, and DROP TABLE as the basic commands demonstrated.
Why it matters: These commands are the vocabulary needed to make the app store and retrieve persistent data. - Haiku Haven table schema: Describe how CREATE TABLE defines the users and haikus schemas, including their columns, data types, primary keys, and the foreign-key relationship from haikus.author to users.username.
Why it matters: The schema enforces the structure and relationships that keep users and their haikus organized. - Row insertion and selection: Describe how INSERT INTO supplies values for named columns and how SELECT retrieves all or chosen columns, optionally filtering rows with WHERE, AND, and LIKE patterns such as %fox%.
Why it matters: Adding records and retrieving the right records are the create and read parts of Haiku Haven’s CRUD behavior. - Targeted updates and deletions: Explain that UPDATE and DELETE FROM act on every row matching their WHERE clause, use the string-concatenation operator || in the example update, and can unintentionally affect every row when WHERE is omitted.
Why it matters: A precise WHERE clause prevents a small edit or deletion from becoming a destructive database-wide change.
Key terms: SQL, Structured Query Language, CREATE TABLE, INSERT INTO, SELECT, UPDATE, VARCHAR(100), TEXT, PRIMARY KEY, SERIAL
8.2.5 Connecting PostgreSQL to the app
This section connects Haiku Haven to PostgreSQL by encapsulating Streamlit’s SQL connection in a reusable Database class, configuring credentials securely, and explaining how the connection is shared across users.
- Streamlit SQL connection setup: Describe how st.connection connects the app to PostgreSQL, including the required SQLAlchemy and psycopg2-binary packages and the named SQL connection type.
Why it matters: This gives the app a managed way to send SQL queries to its PostgreSQL database. - Reusable Database query interface: Explain how the Database class stores a Streamlit connection and exposes execute_query(query, params, write) so app code can run both read and write SQL without repeating connection logic.
Why it matters: A single database interface keeps database access reusable as the app adds users and haiku operations. - Safe transactional query execution: Trace how execute_query opens a SQLAlchemy session, executes text(query) with params, commits successful writes, returns fetched rows, and rolls back a failed write before re-raising the error.
Why it matters: This pattern both protects the database from incomplete changes and keeps user-provided values separate from SQL structure. - Secrets-based connection configuration: Describe how .streamlit/secrets.toml configures connections.haikuconn with a PostgreSQL URL containing the server address, port, credentials, and database name, while keeping that file out of Git.
Why it matters: The connection cannot reach the intended database without its server details, and credentials must not be committed to source control. - App entrypoint database usage: Describe how main.py imports and instantiates Database, runs a SELECT through database.execute_query, and displays the returned list of row tuples with st.write when launched from the app root with streamlit run main.py.
Why it matters: This verifies that the Streamlit app can retrieve persistent data before higher-level services are added. - Shared database connection resource: Distinguish per-user Streamlit app instances from the database connection resource shared across users, and explain that st.connection internally applies st.cache_resource for this purpose.
Why it matters: Sharing the connection avoids creating a separate database connection for every user session or browser tab.
Key terms: st.connection, type="sql", SQLAlchemy, psycopg2-binary, PostgreSQL adapter, Database class, __init__, self.conn, execute_query, query
8.3 Creating user accounts
This section introduces user accounts for Haiku Haven, secure password storage, and the upcoming discussion of code organization.
- User account purpose: Explain why Haiku Haven needs user accounts: users need accounts to hold the haikus they create.
Why it matters: Accounts provide a way to associate each user's created haikus with that user. - Secure password storage: State that account creation must store passwords securely, rather than treating passwords as ordinary application data.
Why it matters: Passwords require secure handling when an app creates user accounts. - Code organization transition: Describe that the section will first discuss code organization before wiring the app to create accounts.
Why it matters: Organizing the code is introduced as preparation for implementing account creation.
Key terms: Haiku Haven accounts, haikus, passwords, securely, code organization
8.3.1 Splitting the app into services
This section organizes Haiku Haven’s backend into focused service classes coordinated by a Hub rather than one growing monolithic backend class.
- Service-based backend organization: Explain how Haiku Haven divides backend actions into service classes organized by responsibility, with UserService handling user-related actions and HaikuService handling haiku-related actions.
Why it matters: Focused services keep related backend behavior together as the app grows. - Hub coordinator role: Describe the Hub class as the frontend's backend contact point and, in the service-based design, as a coordinator of focused services rather than a class that implements every action itself.
Why it matters: Keeping Hub as a coordinator prevents the central backend interface from becoming an unwieldy monolith. - Modularity and extensibility rationale: Justify the service-based design by explaining that focused, independent services make the app easier to extend and maintain, such as by adding a LimerickService without changing UserService or HaikuService.
Why it matters: Modular organization lets new features be added with fewer changes to existing code.
Key terms: UserService, HaikuService, service classes, Hub, frontend, backend, coordinator, monolithic approach, separation of concerns, modular
8.3.2 Creating the user service
This section builds secure user creation for Haiku Haven by hashing passwords, encapsulating database access in services, and wiring a Streamlit signup page.
- Secure password hashing: Explain why Haiku Haven stores a password_hash rather than a plaintext password, and describe how the User class hashes a submitted password and authenticates a later password attempt with bcrypt.
Why it matters: Password hashing prevents a database breach from directly exposing users’ passwords. - User model password methods: Describe the User dataclass fields and the roles of its hash_password static method and authenticate instance method.
Why it matters: Keeping hashing and password checking on the User model gives the rest of the app clear operations for handling credentials. - UserService account operations: Trace how UserService retrieves an existing user and creates a new one, including its parameterized SQL queries, duplicate-username check, and conversion of returned database tuples into User objects.
Why it matters: The service centralizes user database operations and prevents creation of duplicate usernames. - Hub-mediated signup flow: Describe how Hub gives the Streamlit frontend access to UserService and trace the signup page flow from password confirmation through create_user to a success or error message.
Why it matters: The Hub keeps frontend code from managing the database directly while the signup page provides the first usable account-creation flow.
Key terms: User, password_hash, cryptographic hash function, bcrypt, hash_password, authenticate, dataclass, username, @staticmethod, UserService
8.4 Setting up a multi-page login flow
This section introduces Haiku Haven’s planned multi-page user flow, with separate signup, login, and haiku-functionality pages.
- Haiku Haven page structure: Describe Haiku Haven as a multi-page Streamlit app whose user experience separates signup, login, and haiku-related functionality into distinct pages.
Why it matters: Separating these functions into pages provides the structure for the app’s account and haiku workflows.
Key terms: multi-page app, signup, login, haiku-related functionality
8.4.1 Multi-page apps in Streamlit
This section converts Haiku Haven into a multi-page Streamlit app by routing between login and signup pages while sharing the Hub through session state.
- Multi-page app structure: Describe how a Streamlit multi-page app separates each page into its own Python script while the entrypoint file acts as a router that chooses and loads the current page. Explain how the former signup flow is moved from main.py into frontend/signup.py.
Why it matters: This structure lets related flows such as login and signup function as one organized application. - Page definitions and navigation: Explain how st.Page defines a page from its script path, title, and optional icon, and how a dictionary of Page objects is supplied to st.navigation. Describe that st.navigation returns the user-selected page, or the first page by default, and that calling .run() loads it.
Why it matters: Page objects and navigation provide the concrete Streamlit mechanism that connects separate page scripts into one app. - Shared Hub session state: Explain that st.session_state is shared across pages in a multi-page app, so main.py saves the Hub instance there and signup.py retrieves hub from session state instead of creating or receiving it locally.
Why it matters: Shared session state lets separate pages use the same application objects during a user's session. - Material icon syntax: State that displayable Streamlit text can use the :material/<icon_name>: syntax to render a Material icon, such as :material/login:, and that icon names can be found in Google's Material Symbols library.
Why it matters: This syntax provides a simple way to add recognizable icons to page labels and other displayable text.
Key terms: multi-page apps, entrypoint file, router, main.py, frontend/signup.py, st.Page, st.navigation, Page objects, .run(), pages dictionary
8.4.2 Implementing login
This section adds UserService authentication and a Streamlit login page that records a successful login in session state.
- UserService authentication flow: Explain how get_authenticated_user uses a supplied username and password to find a user through get_user and authenticate that user. State that it returns the User object on success and None when either the username is absent or the password is wrong.
Why it matters: This backend method gives the login page one consistent way to validate credentials. - Session-state login page: Describe how the login page calls UserService when the Log In button is clicked and interprets a returned User object as success versus None as failure. Explain that a successful login stores logged_in and the User object under st.session_state, then shows a success message; failure shows an error message.
Why it matters: Session state preserves the app's simple logged-in status and current user across Streamlit reruns.
Key terms: UserService, get_authenticated_user, get_user, authenticate, User, None, login.py, Log In, st.session_state, logged_in
8.4.3 Navigating between pages
This section completes Haiku Haven’s signup, login, and logout navigation by linking pages, changing navigation by session state, and rerunning after authentication changes.
- Context-sensitive page navigation: Describe how Haiku Haven shows different navigation choices according to the logged_in session-state value: signup and login when logged out, and home and logout when logged in. Explain that placing home first in the list passed to st.navigation makes it the default logged-in page.
Why it matters: A navigation menu that reflects authentication state gives users the pages and actions appropriate to their current context. - Cross-page navigation links: Explain how st.page_link connects the login and signup pages, including that it receives a target st.Page object and display label. State that its target may also be a regular external URL.
Why it matters: Page links let users move directly between related parts of a multi-page flow instead of having to find them in navigation. - Authentication-triggered reruns: Trace the automatic redirection flow for login and logout: changing logged_in affects what main.py selects on the next rerun, and st.rerun makes that transition immediate. Describe that login leads to the logged-in home page, while logout reverses the state and returns the user to the logged-out login/signup flow.
Why it matters: Streamlit must rerun after an authentication-state change so the app immediately renders the correct navigation and page. - Native OIDC authentication: Describe Streamlit’s native authentication alternative: st.login delegates identity verification to an OIDC identity provider, user details are available through st.user, and st.logout ends the session. Explain why this avoids maintaining a users table and password hashes in the app and is therefore more secure.
Why it matters: Using an identity provider can provide secure authentication without an app handling users’ passwords itself.
Key terms: st.navigation, logged_in, st.session_state, pages dictionary, st.page_link, st.Page, login page, signup page, st.rerun, home.py
8.5 Creating, reading, updating, and deleting haikus
This section introduces the core Haiku Haven work of implementing haiku CRUD operations, beginning with creation through a HaikuService and corresponding frontend changes.
- Haiku CRUD capabilities: State that the central app capability introduced here is allowing authenticated users to create, read, update, and delete haikus.
Why it matters: CRUD operations are the core user-facing behavior that turns Haiku Haven into an interactive content app. - Haiku implementation sequence: Describe the planned implementation order: first encapsulate haiku creation in a HaikuService class, then make the appropriate frontend changes, before covering the remaining CRUD operations.
Why it matters: This sequence separates the haiku behavior from the interface that exposes it to users.
Key terms: create, read, update, and delete, CRUD, haikus, HaikuService, frontend, haiku creation
8.5.1 Defining a HaikuService class
This section defines the Haiku data model and service layer for creating haikus, then exposes that service to the frontend through the application hub.
- Haiku dataclass structure: Describe the Haiku dataclass and explain how its fields correspond to the columns in the haikus database table.
Why it matters: Matching the application model to the database schema gives the service a consistent representation of each haiku. - Haiku creation service: Explain how HaikuService creates a haiku: its create_haiku SQL supplies the author and text, while the database generates haiku_id and created_at and returns the completed record through RETURNING.
Why it matters: The division of responsibility between the service and database lets the app create complete, persistent haiku records without manually generating database-managed values. - Hub service exposure: State that an instance of HaikuService is added to hub.py so the frontend can access the haiku creation function.
Why it matters: Registering the service in the hub connects the new backend capability to the Streamlit interface.
Key terms: Haiku dataclass, haikus table, haiku.py, backend/ folder, HaikuService, create_haiku, SQL query, author, text, haiku_id
8.5.2 Enabling users to create haikus
This section adds a modal haiku editor that saves a logged-in user's text through HaikuService and reruns the app after a successful save.
- Modal haiku editor: Describe how the haiku_editor function uses st.dialog to present a modal editor and st.text_area to accept multiline haiku text from the user.
Why it matters: The dialog gives users a focused interface for entering a new haiku without leaving the home page. - Haiku save workflow: Trace the save action from clicking Save Haiku through calling HaikuService.create_haiku to store the entered text in the database, followed by a success or failure message.
Why it matters: This connects a Streamlit interaction to persistent storage and makes the result visible to the user. - Post-save app rerun: Explain that a successful save triggers st.rerun, which reruns the entire app and closes the dialog because the button that originally opened it is no longer clicked.
Why it matters: Understanding the rerun explains why the editor closes after saving rather than needing an explicit close action. - Home page editor trigger: Describe how home.py includes an Add Haiku button that opens the haiku_editor dialog, using a Material icon in place of the word Add in the button label.
Why it matters: The trigger connects the logged-in home page to the new-haiku creation workflow.
Key terms: haiku_editor, st.dialog, modal dialog, st.text_area, Save Haiku, HaikuService, create_haiku, database, success/failure message, st.rerun
8.5.3 The other CRUD operations: Read, Update, Delete
This section completes Haiku Haven’s CRUD functionality by listing a user’s haikus and enabling them to edit or delete each one.
- Haiku service read-update-delete methods: Describe the three HaikuService methods added for the remaining CRUD operations: get_haikus_by_author reads a user’s haikus, update_haiku changes a haiku identified by haiku_id, and delete_haiku removes one identified by haiku_id.
Why it matters: These backend operations provide the database actions that the haiku-management interface calls. - Haiku list display design: Explain how the frontend displays the current user’s haikus: it retrieves them by author, loops through the results, and passes each Haiku object to display_haiku, while showing an informational message when the list is empty.
Why it matters: A useful CRUD interface must let users see the records that they can manage. - Haiku card content and widget identity: Describe what display_haiku presents for each haiku and why: it shows created_at, text, and edit and delete controls, while omitting the internal haiku_id and redundant author; each repeated button needs a widget key so Streamlit can distinguish it from buttons for other haikus.
Why it matters: Choosing meaningful fields and unique widget identities makes a repeated-record interface understandable and functional. - Editing and deleting existing haikus: Trace how edit and delete actions work in the UI: the existing haiku editor is reused with an optional Haiku argument, prepopulates the text for edits, and calls update_haiku with haiku_id; Delete calls delete_haiku, and successful changes trigger st.rerun so the list refreshes.
Why it matters: Reusing the editor and rerunning after successful database changes keeps the interface synchronized with persisted haikus.
Key terms: HaikuService, get_haikus_by_author, update_haiku, delete_haiku, haiku_id, UPDATE...SET, display_haiku, Haiku, home.py, st.info
8.6 Deploying Haiku Haven
This section introduces deploying Haiku Haven to Community Cloud and identifies its additional need for a running PostgreSQL database server.
- Haiku Haven production deployment: Describe the move from a locally working Haiku Haven app to a production deployment on Streamlit Community Cloud, noting that it follows the deployment process introduced in chapter 5.
Why it matters: A local Streamlit app must be deployed before users can access it as a production web application. - PostgreSQL production requirement: Explain the additional deployment requirement for Haiku Haven: unlike the earlier app, it needs a running PostgreSQL server to host its database.
Why it matters: The deployed app cannot store and retrieve its database data unless it can connect to a running PostgreSQL server.
Key terms: Haiku Haven, Community Cloud, productionize, PostgreSQL, PostgreSQL server, database, running server
8.6.1 Setting up a managed PostgreSQL server in production
This section sets up a Neon managed PostgreSQL database for the deployed Haiku Haven app and recreates its local schema in production.
- Managed database provisioning: Explain why Haiku Haven needs an external managed PostgreSQL server in production and describe the Neon setup choices: creating an account, choosing a project and database name, and optionally selecting a cloud provider and location.
Why it matters: A Community Cloud deployment cannot install and run the local PostgreSQL server used during development. - Production connection string: Describe the Neon production connection string as the database connection information supplied after setup, including the generated username and password, and state that it should be stored safely.
Why it matters: The deployed app needs this connection information to reach its production database without exposing credentials. - Production schema recreation: Describe how to recreate Haiku Haven's users and haikus tables in Neon by opening Neon’s SQL Editor and executing the same CREATE TABLE commands used locally, without changes.
Why it matters: A new production database needs the same tables as the local app before it can store users and haikus.
Key terms: Streamlit Community Cloud, managed PostgreSQL service, Neon, project name, database name, connection string, haikudb_owner, username, password, SQL Editor
8.6.2 Deploying to Community Cloud
This subsection deploys Haiku Haven to Streamlit Community Cloud by declaring dependencies and configuring production database secrets.
- Community Cloud dependencies: Describe why Haiku Haven needs a requirements.txt for its Community Cloud deployment, and state that it includes the needed third-party modules, primarily psycopg2-binary and bcrypt.
Why it matters: Community Cloud uses this file to install the Python packages your deployed app needs to run. - Production database secrets: Explain how to configure Haiku Haven's secrets after deployment: copy the contents of secrets.toml into Community Cloud's Secrets setting and replace its connection string with the connection string copied from Neon.
Why it matters: The deployed app needs the production database connection details in Community Cloud rather than the local connection configuration.
Key terms: requirements.txt, Streamlit Community Cloud, psycopg2-binary, bcrypt, secrets.toml, Secrets setting, connection string, Neon, Community Cloud
8.7 Summary
This summary recaps the CRUD, relational-database, security, Streamlit navigation, and production-deployment foundations used to build the Haiku Haven app.
Where this is used later
- Go forth and develop! (chapter 12) · The Haiku Haven CRUD app
- Creating a Dockerfile (chapter 12) · Creating a Dockerfile likewise requires declaring how an app's dependencies are installed.
- Deployment considerations (chapter 12) · Experience deploying the Haiku Haven app to Community Cloud.
Concepts explained in this chapter
- PostgreSQLPostgreSQL is an open-source object-relational database management system that supports SQL, transactions, and extensible data types.
- connection stringA connection string is a structured value containing the address, port, credentials, and database name needed to connect to a data service.
- databaseA database is an organized system for storing, managing, and retrieving structured or unstructured data.
- st.connectionst.connection is a Streamlit API function that creates and manages connections to external data services such as databases.
- CRUDCRUD is a software pattern covering the creation, reading, updating, and deletion of persistent records.
- SQL injectionSQL injection is a security vulnerability in which attacker-controlled input alters a database statement to execute unintended SQL commands.
- SQLAlchemySQLAlchemy is a Python database toolkit and object-relational mapping library that provides database connectivity, SQL execution, and transaction management.
- st.cache_resourcest.cache_resource is a Streamlit decorator that caches shared resources, such as database connections, across users and app sessions.
- database tableA table is a relational data structure consisting of rows and columns that stores records about related entities.
- schemaA database schema is the formal definition of a database's tables, fields, relationships, and constraints.
- psqlpsql is PostgreSQL's interactive command-line client for issuing SQL statements and database-administration commands.
- foreign keyA foreign key constraint requires values in one table's column to correspond to existing values in a referenced table's key column.
- primary keyA primary key is a column or set of columns whose combined values uniquely identify each row in a data table.
- st.navigationst.navigation is a Streamlit API function that creates navigation from page definitions and returns the page selected for execution.
- SQLA search query is a textual request specifying the items or information to retrieve from a search service.
- password hashingPassword hashing is the process of transforming passwords into one-way cryptographic representations for safer credential storage.
- bcryptbcrypt is a password-hashing library and algorithm implementation designed to make password guessing computationally expensive.
- cryptographic hashA cryptographic hash function transforms input into a fixed-looking digest such that recovering the original input is computationally difficult.
- columnA database column is a named field in a table that stores one attribute of each row according to a defined data type.
- data typeA data type specifies the kind of value that a database column can store and the operations applicable to that value.
- recordA database row is a single record in a table containing values for that table's columns.
- login pageA login page is a web-application page that collects user credentials and initiates authentication.
- CREATE TABLE statementCREATE TABLE is a SQL statement that creates a table with specified columns, data types, and constraints.
- database updateUPDATE is a SQL statement that changes column values in rows matching an optional condition.
- deletionDELETE FROM is a SQL statement that removes rows from a table, optionally restricted by a condition.
- insertINSERT INTO is a SQL statement that adds one or more rows to a table.
- selectSELECT FROM is a SQL query form that retrieves rows and selected fields from one or more tables.
- where clauseA WHERE clause is a SQL query component that restricts affected or returned rows according to a condition.
- authenticationUser authentication is the process of verifying a person's identity before granting access to an account or application resources.
- st.page_linkst.page_link is a Streamlit API function that renders a link for navigating between pages in a multipage application.
- identity providerAn identity provider is a service that authenticates users and supplies identity information to relying applications.
- openid connectOpenID Connect is an authentication protocol built on OAuth 2.0 that lets applications verify user identity through an identity provider.
- st.loginst.login is a Streamlit API function that initiates authentication through a configured identity provider.
- st.logoutst.logout is a Streamlit API function that ends the current authenticated session and returns the user to the app's main page.
- st.userst.user is Streamlit's object for accessing the authenticated user's identity information and login status.
- multi-page appA multi-page app is an application structure in which separate pages are defined independently and a central entrypoint selects and runs the current page.
- st.Pagest.Page is a Streamlit API object that defines a page script together with metadata such as its title and icon for multipage navigation.
- data modelA data model is a structured representation of an application's entities, attributes, relationships, and storage organization.
- edgeA graph edge is a connection between two graph nodes that represents a relationship or execution transition.
- separation of concernsSeparation of concerns is a software design principle in which distinct components focus on distinct responsibilities and interact through controlled interfaces.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
