Creating Your First Project

Learn how to use Thin Backend to build SPAs


Thin Backend is a Blazing Fast, Universal Web App Backend for Making Realtime Single Page Apps.

1. Project Setup

This guide will lead you through creating a small todo application.

Start by creating a new project in Thin Backend:

  1. Open Thin Backend. (Haven't created an account yet? Create one here)
  2. Click + New Project
  3. Enter Todo App into the name field
  4. and finally click Create Project

After clicking the Create Project button you will be redirected to the freshly created project:

2. Adding a Frontend

You now already have a working database and API running, ready to be used by a single page app. Next we're setting up a React and TypeScript frontend to connect with our new Thin Backend project.

  1. Click on Frontend in the left sidebar:

  2. Open a terminal and run the command shown on the page:

  3. Congrats! The react frontend is now running at localhost:3000 and connected to Thin πŸŽ‰

3. Trying out the Frontend

Now that it's running, we can already try out our empty react app:

  1. Open the app at localhost:3000:

    As we're not logged into our Todo App yet, this will show a login screen.

  2. Before we can log in to our app, we first need to create an account.

    Click Sign Up and follow the instructions:

    After completing the sign up you will be logged in automatically.

  3. Once the login process is completed, Thin Backend will show us the app:

    Pretty empty :) But that's expected. We will add some functionality to our app very soon.

    On the top right corner you can see the email of the logged in user.

    You can use the navigation in the top right corner also for logging out. Click the β–Ό next to the email and then click on Logout. If you try out the logout functionality, make sure to log in again before continuing with the Guide.

4. Hello World

It's time to make the react app less empty by implementing our todo app.

To test that everything is working fine, let's make the app display a Hello World:

  1. Open the app.tsx with your code editor.

    Ignore any TypeScript errors for the moment, we'll get to that soon.

  2. Add a Hello World message to the App() component, like this:

    function App() {
        return <div className="container">
            <div>Hello World</div>
  3. Open the browser at localhost:3000. It should look like this:

    If everything looks alright, we're now ready for developing on our todo app locally.

5. Creating Tables

For our todo app project, let's first build a way to manage tasks.

For working with tasks, we first need to create a tasks table inside our database. A single task has

  • a title,
  • a timestamp when it was created,
  • a user_id,
  • and of course an id field for the task itself.

Thin Backend uses UUIDs instead of the typical numerical ids.

This is what your tasks table might look like:

id :: UUIDtitle :: Textcreated_at :: Timestampuser_id :: UUId
8d040c2d-0199-4695-ac13-c301970cff1dLearn Haskell2022-01-03 18:14:511d89c722-7269-46d3-b71b-ec6742867363
ad938116-a2ac-44ce-9201-cc66b8a4bb51Learn IHP2022-01-03 18:20:151d89c722-7269-46d3-b71b-ec6742867363

To work with tasks in our application, we now have to define this data schema.

Open the Schema Designer by clicking the SCHEMA button in Thin Backend:

  1. Right click into the Tables pane and click Add Table

  2. Enter the table name tasks and click on Create Table:

In the right pane, you can see the columns of the newly created table. The id column has been automatically created for us.

  1. Right-click into the Columns pane and select Add Column:

  2. Use this modal to create the title:

  3. Now let's add the created_at timestamp column. You can see that Thin Backend automatically suggest this created_at column below the table definition:

    Click on the suggested created_at column to add it to the tasks table:

    Now it will look like this:

  4. The only column left now is the user_id.

    The schema designer is also suggesting to add this for us. Click it to add the user_id column:

    After the user_id column has been added, you'll also see a new index and a policy appear. Thin Backend often uses convention over configuration to provide good defaults for your app:

    • The index on the user_id column is added because it's common to run database queries like Give me all tasks for a specific user.
    • The policy is defined to only grant a user access to their own tasks:
      • The user_id field has to be the same as the current user's id, otherwise access is prohibited.
      • A user can only add tasks if the user_id is set to their own id.

  5. After that, your schema should look like this:

Running Migrations

Next, we need to make sure that our database schema with our tasks table is imported into the Thin Backend PostgreSQL database. For that we'll create and run a database migration.

  1. Click the Migrate DB button at the bottom of the page

  2. Thin Backend already prepared a few SQL statements to migrate our DB. Typically we don't need to make any changes here.

    Once your app is running in production, you might want to review and modify the auto generated SQL statements to make sure that everything goes as planned.

    Click Create Migration after you've quickly reviewed the SQL.

  3. The migration has been created:

    Click Run to apply the migration to the app database:

The tasks table has been created now. Let’s quickly connect to our database and see that everything is correct:

  1. Click on the DATA button in the left navigation.
  2. In the DATA editor, click on the tasks table.
  3. You should see an empty table:

    If there was some issue with the migration, the tasks would not be in the table list at the left.

Now our database is ready to be consumed by our react app.

6. Coding the App

Setting up TypeScript

If you've already opened the project's app.tsx in VSCode, you'll likely have seen an error like this:

The red marked errors happen because we haven't installed the TypeScript type definitions for the project yet. Thin Backend automatically generates TypeScript type definitions based on the database schema.

To install them:

  1. Open Thin Backend
  2. Click SCHEMA β†’ Type Definitions
  3. Run the displayed npm install command in your local project

After running that command, your editor should not report any errors anymore.

Listings Tasks

In our todo app, we want to list tasks, allow the user to add new tasks, edit them and later delete them when completed.

Let's start by adding a component for showing all of the current users tasks.

  1. Open app.tsx in your code editor
  2. Add a component Tasks like this:

    function Tasks() {
        const tasks = useQuery(query('tasks').orderBy('createdAt'));
        if (tasks === null) {
            return <div>Loading ...</div>
        return <div>
            { => <Task task={task} key={} />)}

    You can see that we're using a custom useQuery(..) react hook to get the results of the query('tasks').orderBy('createdAt') query.

    If you type out the above code, you'll get nice autocompletions by VSCode based on your project's schema:

  3. Write a Task component like this:

    interface TaskProps {
        // You might might need to an manually add an import
        // if not using VSCode: `import { Task } from 'thin-backend';`
        task: Task;
    function Task({ task }: TaskProps) {
        return <div>{task.title}</div>
  4. Finally we need to mount the <Tasks /> from inside our App component:

    function App() {
        return <div className="container">

Right now the app should still be empty as we don't have any tasks yet. Let's add an Add Task button:

Creating Tasks

  1. Inside app.tsx add a component AddTaskButton like this:

    function AddTaskButton() {
        function handleClick() {
            createRecord('tasks', {
                title: window.prompt('Title:') || '',
                userId: getCurrentUserId()
        return <button className="btn btn-primary" onClick={handleClick}>Add Task</button>

    We're calling Thin Backend's createRecord('tasks', someTask) function to create a new task inside the tasks table.

  2. Finally we need to mount the new <AddTaskButton /> in our App component:

    function App() {
        return <div className="container">
            <AddTaskButton />

Now open the browser and click the Add Task button:

After you entered a title, the new task should instantly appear in the tasks list:

You might wonder: How is the Tasks component notified about new tasks? The const tasks = useQuery(..) call we used to retrieve tasks sets up a subscription on the database and is automatically notified whenever the query result has changed.

Editing Tasks

Next we're going to make tasks editable. On double click on a task, we'll show a prompt to enter a new title:

  1. Change the Task component to look like this:

    function Task({ task }: TaskProps) {
        function edit() {
            updateRecord('tasks',, {
                title: window.prompt('New title', task.title) || task.title
        return <div onDoubleClick={edit}>{task.title}</div>

    This will call Thin Backend's updateRecord('someTable', someId, somePatch) function and update the title field to the newly entered value.

Double click on a task to and try to change the task's title.

Deleting Tasks

To complete our todo app, we'll add delete buttons to tasks, so that completed tasks can be removed again.

  1. Inside app.tsx add a component DeleteTaskButton like this:

    function DeleteTaskButton({ task }: TaskProps) {
        function handleClick() {
        return <button
                className="btn ml-5 p-0 btn-link text-danger"

    We're using Thin Backend's deleteRecord() function here to delete the task when the delete button is clicked.

  2. Let's render the delete button right next to the task itself inside the Task component:

    function Task({ task }: TaskProps) {
        function edit() {
            updateRecord('tasks',, {
                title: window.prompt('New title', task.title) || task.title
        return <div onDoubleClick={edit}>
            {/* ADD THE LINE BELOW */}
            <DeleteTaskButton task={task}/>

The result should look like this now:

Click the delete button and tasks will instantly disappear:

8. Summary

You've now implemented the basics of todo management. Congrats!

Along the way you've learned the basic operations of Thin Backend:

  • Fetching data with useQuery and the query builder:
    const tasks = useQuery(query('tasks').orderBy('createdAt'))

  • Inserting into a database table:
    createRecord('some_table', someRecord)

  • Patching a database record:
    updateRecord('some_table', someRecordId somePatchObject)

  • Deleting a database record:
    deleteRecord('some_table', someRecordId)

You now understand enough of Thin Backend to be dangerous. The best way to continue your journey is to start building things. Take a look at the other Guides to learn more about all the functionality provided by Thin Backend.

Check out the Database Guide for a full list of all database operations you can do with Thin.

Next: Database Guide β†’


If you need any help or input, feel free to ask in the Thin Community Forum.