This two-part article covers how we can build an Issue tracking application using React for the frontend, and Shuttle and Actix Web for the backend. It also uses Clerk for authentication in the frontend and protecting the Rest APIs in the backend.
This article covers the Rust backend, and the second part covers the React frontend.
Here is the source code of the complete project if you want to follow along, and a link to the demo.
To start this project we will use the Clerk template from Shuttle's example repository that has a barebone setup of Shuttle, Actix Web, Vite (React), and Clerk.
The backend uses the clerk-rs community crate to create a connection between the backend application and Clerk project. This crate has many different APIs for different use cases. In the template there is an endpoint to fetch all user records using the clerk-rs
user API. We can easily verify a user's identity when trying to access protected routes using the ClerkMiddleware
from the crate.
We will use Postgres for the database which will run in a docker container so make sure you also have docker installed in your local machine.
Let's get started
First, we need to clone the template from the Shuttle's example repository using Shuttle CLI:
You will get a few prompts to create a new project using this template. Make sure you provide a unique name because when you deploy this project on Shuttle this project name will become the sub-domain. And once a name is taken it cannot be used for any other projects
After that, cd
into the project directory.
Setup a new application in Clerk
Head on to Clerk's official website, sign in or sign up if you don't have an account, and create a new project in the dashboard. Give a name to your project and select the providers using which users can sign-in or sign-up in your application.
After that, you will get a Publishable Key and a Secret key which we will use later in our application.
Build the backend
Now we can start updating the existing backend code to write and build the Rest APIs for our application.
cd
into the backend, and rename the Secrets.example.toml
file to Secrets.toml
:
Copy the Secret key from the Clerk's dashboard and add it to the Secrets.toml
file:
⚠️ Do not commit this file, so make sure you have added this file in the .gitignore
.
Now, we need to write a new migration in a schema.sql
file which will create a new issues
table in our Postgres database.
Create schema.sql
and add the schema:
We can migrate this schema using SQLX. So add the SQLX crate to your dependency:
To connect to a shared Postgres database managed by Shuttle you need to add the crate with features specific to Postgres and SQLX to your dependencies:
Connect to the shared Postgres DB
Let's edit the main.rs
file.
First, we need to bring SQLX into the scope:
Add the pool field to the AppState
struct to share the Postgres pool instance in our whole application:
Next, pass the #[shuttle_shared_db::Postgres] pool: PgPool
argument to your shuttle_runtime::main
function and connect to Postgres using SQLX connect pool PgPool
.
The above function call will create the Postgres pool, and then SQLX will migrate the schema we have created before. The Clerk configuration is already defined in the template for us which uses the CLERK_SECRET_KEY
we got from the Clerk project we have created earlier.
The Clerk middleware protects all the paths under /api
, so that only signed in users can access them.
Try running the application using Shuttle CLI:
🗨️ Make sure docker is running in your local machine because after you run the above command Shuttle will pull the Postgres docker image and start a docker container.
The backend application can now query the Postgres database using SQLX. Let's create some CRUD endpoints for our application.
Issue structs
To define the fields of an issue we will create an Issue
struct that is used to define the response and request payload of our Read, Update, and Delete requests, and a NewIssue
struct for the Create requests:
Extract the Clerk JWT claim from a request
In some of the endpoints we need to check for the user's authorization. For that we will look for a user id which can be found in the JWT claim sub
property:
CRUD endpoints
Add the create issue endpoint which can query our database to insert a new issue into the table like this:
This endpoint will retrieve all the issues from the database:
This endpoint function will extract the issue_id
slug from the path using the Path extractor from Actix Web and query our database where the issue id
is equal to the issue_id
slug of the path.
To update an issue by id we will use slug from the path which will contain the issue_id
to query our database and update the record if it exists in the database. Before updating the record we can query if the record exists in the database or not:
This endpoint is for deleting the issue by id
from the database:
User endpoints
The template provides a get_user
handler function and a UserModel
struct.
We will edit the handler function to use the get_jwt_claim
function:
We need one last endpoint which can fetch the user details by their user_id
. This endpoint will be used to fetch issue author metadata from Clerk:
Now, let's add all these endpoints to our main
function under the /api
scope which is wrapped by Clerk middleware for protecting our endpoint routes and only allowing authenticated users to access these routes. After adding all the endpoints the main
function you should have something like this.
Building the frontend and deploying
One important thing to notice is that this piece of code
is going the serve the static pages of the frontend application located in the ./frontend/dist
directory whenever we build our Vite application, and this final output is optimized for production.
Follow along to part 2 where we build the frontend and deploy.