Changelog

Node.js / Next.js API Route Support

Previously it was not possible to query your Thin backend from Node.js applications. Node.js doesn't support WebSockets out of the box. Thin is using WebSockets are the primary communication layer to the Thin Backend, therefore Node.js based apps always failed to connect.

We've now internally added support for querying Thin via HTTP instead of just via WebSockets. The thin-backend package will automatically detect when a WebSocket is not available and then fall back to use a fetch(..) call. Of course this doesn't support live queries, but we don't want those on the node.js servers anyways.

Here's how a Thin query can look from a nextjs API route:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import { initThinBackend, query } from 'thin-backend'
import { DataSyncController } from 'thin-backend';

type Data = {
  name: string
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  initThinBackend({ host: process.env.NEXT_PUBLIC_BACKEND_URL });
  
  const task = await query('tasks').fetchOne();

  res.status(200).json({ task })
}

Serverside Auth:

The query('tasks').fetchOne() query will run as an anonymous / not logged in user. If you want to access the Thin api as a logged in user, you need to pass the JWT from the client to the next.js app.

On the client side you can retrieve the JWT like this:

import { DataSyncController } from 'thin-backend';

const jwt = DataSyncController.getJWT();

fetch("/api/my-nextjs-endpoint", { headers: { Authentication: jwt } })

On the serverside you need to override the getJWT function of Thin:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import { initThinBackend, query } from 'thin-backend'
import { DataSyncController } from 'thin-backend';

type Data = {
  name: string
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  initThinBackend({ host: process.env.NEXT_PUBLIC_BACKEND_URL });

  DataSyncController.getJWT = () => req.headers.Authentication;
  
  const task = await query('tasks').fetchOne();

  res.status(200).json({ task })
}

If you use Thin with Node.js, update to the latest version of the thin-backend package.

Vue Improvements

We've added some functions that already exist in the Thin React integration to the thin-backend-vue package:

  • useCurrentUserId
  • useIsLoggedIn
  • useCurrentUser

If you use Thin with Vue, update to the latest version of thin-backend-vue to use these new functions.

JWT

The JWT private and public keys are now visible in the project setting. This helps when integrating Thin with other API systems that need to verify the auth state.

Other Changes

  • Fixed query builder .where({ someField: someValue }) causing an error because it was not correctly encoded.
  • The ALL SQL Keyword is now correctly escaped when using it as an identifier in the Schema Designer.
  • Fixed race-condition in closing of DataSync subscription. It’s caused by a race condition when a internal DataSubscription is closed before it’s initialized, or closed after the server already closed the subscription. You might have sometimes seen the Non-exhaustive patterns in Just closeSignalMVar error message in the console. That error was caused by this issue.
  • The Components tab is now also visible when the onboarding is not yet completed.
  • Improved the Policy Creation modal to include more examples and links to the documentation.
  • The Column Edit modal now has a "Delete Column" button. Previously this was only reachable via the context menu.
  • The Schema Designer now prevents you from deleting the built-in users table. It also prevents you from deleting built-in columns on the users table.
  • Improved mobile design of the documentation.
  • Added a GIF to increase awareness that the Schema Designer makes heavy use of context menus.

Vercel Integration

The Thin + Vercel Integration is now available on the Vercel Marketplace. With the Vercel Integration you can connect your Vercel projects to a Thin Backend with just a few clicks:

The integration automatically sets the BACKEND_URL environment variable on your Vercel project. This environment variable is used by commonly used Thin project templates to connect to the backend server.

Try out the Thin Integration on the Vercel Marketplace.

Improved Sync for Update Operations

Previously live queries didn't remove database records when they did't fit the query conditions anymore after an update operation.

E.g. in the code below a task is not removed from the pendingTasks array, even when updateRecord('tasks', task.id, { isCompleted: true }) is executed:

function Tasks() {
    const pendingTasks = useQuery(query('tasks').where('isCompleted', false));
    
    const completeTask = task => {

        // In old versions this didn't remove the task from `pendingTasks`
        // even though there's condition `.where('isCompleted', false)`

        updateRecord('tasks', task.id, { isCompleted: true });
    };

    return pendingTasks?.map(task => <div>
        {task.title}

        <button onClick={() => completeTask(task)}>complete</button>
    </div>)
}

With the latest version of Thin, update operations are checked against the query conditions. Records are removed from the result set when they don't match anymore:

function Tasks() {
    const pendingTasks = useQuery(query('tasks').where('isCompleted', false));
    
    const completeTask = task => {

        // With the latest Thin version, this will
        // automatically remove the task from `pendingTasks`

        updateRecord('tasks', task.id, { isCompleted: true });
    };

    return pendingTasks?.map(task => <div>
        {task.title}

        <button onClick={() => completeTask(task)}>complete</button>
    </div>)
}

Improved Placement of New Records

Whenever a new database record is created using createRecord(..), Thin appends them to the result list of all useQuery(..) results. When the useQuery(..) is sorted by a createdAt column, newest records first, it will prepend the result instead:

// New Tasks appear at the end of the list
const tasks = useQuery(query('tasks'));

With the new Thin version, the order by direction is taken into account to decide whether to append or prepend a new database record:

// New tasks appears at the end
const completedTasksFirst = useQuery(query('tasks').orderByAsc('isCompleted'));

// New task appears at the start of the list
const completedTasksLast = useQuery(query('tasks').orderBy('isCompleted'));

It's now also possible to override the placement of new records:

import { NewRecordBehaviour } from 'thin-backend';

const completedTasksFirst = useQuery(query('tasks').orderBy('isCompleted'), {
    newRecordBehaviour: NewRecordBehaviour.APPEND_NEW_RECORD // or PREPEND_NEW_RECORD
});

Other Changes

  • Use CREATE OR REPLACE when updating a function in a migration. Previously the migration generator tried to delete a function, and then re-create it. The DELETE FUNCTION often fails because the function is still in use somewhere.

Self Hosting

You can now run Thin Backend on your own infrastructure. Whether you want to code on a plane or deploy Thin on your own AWS infrastructure, you can use our new docker image to deploy Thin in a few minutes.

The database schema and your migrations are stored in .sql files, so you can easily put them into your project's git repo.

You can find install instructions in the documentation.

Community

We've added a new Thin community forum. This will replace the Thin Slack channel going forward. The forum has the advantage that other people can later find solutions on Google.

Check it out at community.thin.dev.

Other Changes

  • Fixed error that happens when creating a table with a table name starting with public, e.g. public_messsages.
  • Fixed migration generator detecting a difference between two postgres functions due a different indentation, even though there's no semantic difference.
  • Fixed crash when a SET or SELECT statements appears in the database schema. This typically happend when importing an existing database schema into Thin.
  • Added a back link when clicking Login with Email or Sign up with Email, so you can get back to the GitHub login process

Redesigned Frontend Setup

We want to make it even easier to get started with Thin. So we've redesigned the process of connecting your first frontend to Thin.

The Frontend button in the navigation now always displays instructions on how to set up your react frontend with Thin. Previously clicking the Frontend button opened the connected Single Page App.

We've also simplified the setup process by removing all options that might not be useful for the first start. Other project templates like the Next.js Template are still accessible from the sidebar.

Reset Password

If you've signed up via email and forgot your password, you can now reset it yourself, by clicking Forgot Password on the login screen.

Other Changes

  • Updated design of the Vercel Integration Setup screen
  • The download urls for the typescript Types now have a different url. The old urls had caused escaping issues on windows terminals because of the & sign in the url. The new url format has no & anymore. For b.c. old urls are still working.
  • Removed the react starter (without TS) template, the react starter with TypeScript is always the better choice
  • Several improvements to the Auth React components
  • Fixed react app not loading when requireLogin is not set

New React-based Login

We've finished a new react component for dealing with logins and signups right from within your app. This will avoid all the redirects that happen through the login process, improve latency and allow for easier customization. You can find a demo here.

Right now this is only available for react users. We will port this to other frameworks in the future as well.

To use the new login:

npm update thin-backend

# Install the new react package
npm install thin-backend-react

Inside your app replace all:

// OLD:
import .. from 'thin-backend/react'

// NEW:
import .. from 'thin-backend-react'

Now when you're using <ThinBackend requireLogin>, the login will happen without a redirect, right from within your app.

You can render the login form manually like this:

import { LoginAndSignUp } from 'thin-backend-react/auth-ui';

<LoginAndSignUp />

Components Overview

To increase awareness about the Thin Components, we've added a first draft of the new Components section inside the app. We will extend this over the coming weeks.

Other Changes

  • Improved projects rendering on mobile
  • Added documentation for foreign key constraints to the Database Guide
  • Switched logo inside the app to SVG to have high resolution everywhere
  • Improved error messages for login with email and password
  • Added password reset
  • Added info graphic for backend url to the right sidebar of the documentation
  • Decreased font size for code on mobile devices so it fits on the screen
  • Simplified "Your First Project" Guide by running on localhost instead of Vercel
  • Show github stars on mobile website

Website Improvements

Based on feedback we've received last week, we've worked on improving the page load time of the Thin website.

Next to the performance improvements we've also added a few testimonials and replaced the demo video at the top with a code example.

Improved BACKEND_URL Setup

When setting up a Thin project for the first time using one of our project templates, it can happen that you miss to set the BACKEND_URL environment variable.

The error message that you'll receive in that case now contains a link to the new Troubleshooting Documentation that helps you to fix this issue.

Additionally the Backend URL is now visible in the header of the Schema Designer and other parts of the app. This makes it easier to find.

Other Changes

  • Fixed unique constraint with multiple columns always showing up as unmigrated changes
  • When you're logging in with GitHub the first time and then click cancel in the OAuth dialog, the error redirect was not handled. This is fixed now.
  • We're making good progress on a react component for login. This will avoid all the redirects that happen through the login process and allow for easier customization. It's not finished yet. You can find an early draft already on GitHub.

Support for Vue

This has been requested quite a few times. You can now use Thin with your Vue projects 🎉

<script setup lang="ts">
import { createRecord, query, updateRecord, type Task } from 'thin-backend';
import { useQuery } from 'thin-backend-vue';

const tasks = useQuery(query('tasks').orderBy('createdAt'));

function updateTask(task: Task) {
    updateRecord('tasks', task.id, { title: window.prompt('New title') || '' })
}

function addTask() {
    createRecord('tasks', {
        title: window.prompt('Title:') || ''
    });
}
</script>

<template>
    <div v-for="task in tasks" v-on:dblclick="updateTask(task)">
        {{task.title}}
    </div>

    <button v-on:click="addTask()">Add Task</button>
</template>

The general approach in Vue is very similiar to how we have it working with react. E.g. the react hook useQuery has a Vue pendant called useQuery.

Check out the basic documentation on GitHub

Pricing Update

Based on feedback we've updated the Thin Pricing to make it more intuitive:

  • Thin Prototype: Instead of 100 MB data traffic it's now 20k API Calls included
  • Thin Pro: It's now 1$ per 100k API calls instead of 2$ per GB of data traffic

Custom Logout Redirect

You can now specify the redirect url where a user is redirected after logout:

logout({
    redirect: "https://example.com/logout-completed"
})

Use Thin with Your Own Database

Based on user feedback we've added support for using your own Postgres database with Thin. This makes it easier to adopt Thin Backend with existing projects.

Right now this feature is hidden behind a feature flag and only available to selected users.

If you're curious, check out the docs here.

If you want to use Thin with your existing database, reach out and we'll help you get up and running.

Other Changes

  • Fixed the checkbox that appears when you create a column like project_id not being correctly styled
  • The TypeScript type definitions now have a definition for QueryBuilder.subscribe. This improves the developer experience for people working with Svelte
  • The modal to create a new column now shows the precision when selecting a float or double type
  • Fixed column rename not updating foreign keys and indexes in certain cases

Vercel Integration

We've improved the Vercel integration, so you can now create new projects with Vercel even faster than before.

There's now two ways to connect your Thin app with Vercel:

  1. New Projects: Start inside Thin, create a new Thin project and follow the frontend setup instructions. When you're prompted, selected that you want to use Vercel for hosting. This will redirect you to Vercel.

    This already worked previously, but you had to manually specify the BACKEND_URL before the first deployment in Vercel. With the new Integration, this is taken care of. So you don't need to specifiy anything.

  2. Existing Vercel Projects: Open the Thin Integration on the Vercel Integrations Marketplace, click "Add Integration" and follow the instructions on the screen.

Caching

The Thin JavaScript client now caches results of useQuery(..) calls in-memory. This improves the user-experience when using a routing library and going back in page history.

New useIsConnected() hook

The new useIsConnected() react hook returns true when the browser is online and connected to the Thin Backend server. When the client is offline, it returns false.

Other Changes

  • Fixed several issues in Optimistic Updates that have caused operations to fail because the message has been sent to early
  • Fixed an issue with missing newline normalization causing pending migration changes even when nothing has been changed in the Schema Designer

New Name: Thin

IHP Backend was always just a working title. We've now settled on a new name: Thin Backend.

The idea behind the name is simple: With Thin you have a thin backend layer, and a rich frontend layer in your app.

We've also moved on from the previous domains ihpbackend.digitallyinduced.com and thinbackend.app to thin.dev now. Short and fresh! Give it a try at thin.dev if you're curious.

ihp-backend -> thin-backend

With the new name we've also renamed the NPM package ihp-backend to thin-backend. If you have an existing thin app, go to the package.json and replace ihp-backend with thin-backend in there.

New App Subdomain: myapp.di1337.com -> myapp.thinbackend.app

The domain at which your app is reachable is appname.thinbackend.app now. While di1337.com is a cool domain, it's now time to have something more rememberable.

Reliability Improvements

The Thin infrastructure has been completely rebuild to make sure that everything always works. Previously, every Thin project was running inside a single docker container. This turned out to be pretty inefficient, expensive and error-prone. We've now refactored everything to run without docker and in a more efficient way. Since this refactoring everything has been running much more smooth, stable and fast.

New Hosting Region

We've moved the server hosting region from germany over to us-east-1 in AWS. This will deliver better average latency for everyone. We'll very soon add more regions to make things even faster.

Thin Backend is now a product offered by digitally induced, Inc., a corporation registered in Delaware, US, a wholly owned subsidiary of digitally induced GmbH, registered in Germany.

Thin Components

Build your apps faster with ready-to-use customizable components. With Thin Components you can e.g. add a simple CRUD table to your app with just a single line of code.

When rendered, this Crud component can look like this:

The components are open source and you can contribute to it on the thin GitHub repo :)

Additionally we also have a still-WIP landing page for the components.

Soon we'll add more components, like ComboBox/ or Upload/. In the future we might even add a <StripePayment /> component and much more to deal with high level functionality in your thin apps.

Optimistic Creates, Updates, Deletes

When you're too far away from an IHP Backend server (like e.g. using an IHP Backend app from the US) it previously felt a bit slow. This is because of the increased latency.

We've just added Optimistic Updates. Now when a database record is created using createRecord, it will instantly appear in your apps UI even though the server might have not responded yet. This will improve the user experience for any operations after the initial data fetch.

Optimistic Updates are enabled by default. They work with createRecord, updateRecord and deleteRecord operations.

Make sure to use the latest version of the ihp-backend npm package by running npm update to make use of optimistic updates.

ReScript Guide + Project Template

We've added a new ReScript Template. So if you're using ReScript, you can now start a new project using IHP Backend with a few clicks.

There's also a ReScript Example App. If you're curious how IHP Backend + ReScript looks in practise, check it out on GitHub.

You can now build search functionality like the above in few lines of code:

function ProductSearch() {
    const [searchQuery, setSearchQuery] = useState('');
    const onChange = useCallback(event => setSearchQuery(event.target.value), [ setSearchQuery ]);

    const products = useQuery(query('products').whereTextSearchStartsWith('textSearch', searchQuery));

    return <form>
        <input type="text" className="form-control" value={searchQuery} onChange={onChange}/>

        <div>
            Result: {products?.map(product => <div key={product.id}>{product.name}</div>)}
        </div>
    </form>
}

You can find more details on this in the new Search Guide.

Delete Button for Projects

If you don't need a project anymore, you can now delete it from the project settings:

Improved display of more complex policies in the Schema Designer

When you have policies for a specific action (like e.g. DELETE operations), they're now shown like this:

For the "Users can delete their messages" policy, it now says "DELETE allowed if".

New Example App

We've added a new example app to the start page. The example app is a simple chat app. You can try it out here.

You can find the source code on GitHub.

Other Changes

  • Added missing TypeScript type definition for loginWithRedirect
  • Fixed project settings sometimes not giving any feedback
  • Fixed editing a migration crashes if the text field is submitted empty
  • The redirect url after login is http://localhost:3000 by default now. Previously an app crashed when this was not configured before logging in the first time.
  • Fixed useQuery etc. not correctly restoring after the internet connection was lost for a moment.
  • Added support for postgres generated columns

Suggested Column: updated_at

You can now add updated_at timestamps to your tables with a single click:

When adding the updated_at from the suggested columns, IHP Backend will automatically wire up a database trigger that updates the timestamp whenever the row is updated. So you don't need to manually update the updated_at timestamp.

Easy Checking if Logged In

This week we did some improvements to make it easier to check whether a user is already logged in or not, and to display different content based on that.

Here's an example of using the new useIsLoggedIn() react hook to display a "Hello" message or a "Login required" depending on the login state:

import { useIsLoggedIn, useCurrentUser } from 'ihp-backend/react';

function Content() {
    const isLoggedIn = useIsLoggedIn();

    if (isLoggedIn === null) {
        return <div>Loading ...</div>
    }

    if (isLoggedIn) {
        return <LoggedInContent />
    } else {
        return <LoggedOutContent />
    }
}

function LoggedInContent() {
    const user = useCurrentUser();

    return <div>Hello {user.name}!</div>
}

function LoggedOutContent() {
    return <div>You are not logged in yet</div>
}

Check out the docs for more details on this.

New Feature Pages

We've added new pages to describe all different features and use cases of IHP Backend:

Login with Google

We've added support for OAuth Providers this week. You can now quickly enable "Login with Google" functionality in your project settings.

This is how the setup looks in the project settings:

Enabling google login automatically generates and runs a migration that adds a google_user_id column to your users table.

Learn how to enable "Login with Google" in the docs.

First Version of Pricing

As several people already asked for details on the future pricing plans we've now added a first iteration of the pricing.

The pricing might still change in the coming weeks as we're still collecting feedback on this. Reach out if you have any input on this :)

ReScript Types

Next to the TypeScript types, IHP Backend now also provides you with ReScript Types. Like with the existing TypeScript definitions, you only need to install the generated npm module. You can find the install instructions in the "Type Definitions" tab of your project.

Here's an example of a ReScript Todo App:

Next week we'll add a rescript template as well, so it's even easier to start your project with ReScript.

Other Changes

  • Fixed typos on the start page
  • Navigation on the start page now highlights active menu items
  • Fixed several resource leaks in the realtime APIs that caused long running apps to consume a lot memory on the client and server-side
  • Added internal resource limits to improve robustness of the realtime API
  • created_at columns now get an index by default when added to a table
  • Added validation that prevents creation of columns on the users table if no default value is provided. Custom columns on the users table always need a default value, otherwise your app's signup will crash.
  • Added API documentation for useQuerySingleResult (like useQuery, but only returns a single database record)
  • Fixed a bug causing the "Migrations" tab not active in the UI, even though you're on the migrations page
  • Added a feedback button to the app
  • Fixed a crash in the schema designer

New IHPBackend Component

With the new <IHPBackend/> Component it's easier to use IHP Backend in your project. It replaces the previous ensureIsUser().then pattern:

The new API also renders earlier. The previous ensureIsUser() pattern always needed to complete the login process before showing the react application. With the new component based approach loading spinners and other components will already be visible until the login is completed.

To use the new API, update to v0.3 of the ihp-backend package by running npm update in your project. The "old" way with ensureIsUser still works, but it's highly recommended to switch to the new API.

Check the API Reference for more details.

Auth Guide

We've added a new Guide describing common use cases around auth, like how to make a login and logout button.

You can find it in the docs.

Changelog

The weekly change log is now also available on the website.

npm run start => npm run dev

We've renamed the npm run start command to npm run dev in all project templates to be more consistent with how Next.js does things.

More Batch Operations

We've added updateRecords and deleteRecords so you can update and delete multiple database records in a single database operation:

Transactions

You can now wrap multiple database operations in a database transaction to make sure things don't get out of sync:

Learn more about transactions in the docs.

Other Changes

  • Simplified the documentation navigation: The Home tab has been removed, it's now Guides and API Reference only

  • Several code snippets in the docs now have a click to copy behaviour

  • Changed the logo size in the navigation bar

  • The logs view in the project settings now only shows up to 512 lines of logs.

  • We've added a list of supported database types to the Database Guide

  • Several mobile improvements to the start page

  • Fixed cache busting sometimes not working as expected

  • Internal Performance Improvements:

    Previously an operation like createRecord needed multiple SQL calls internally. Now they only need one call to the database. This saves 2ms of latency on every operation.

useQuery without Login

It's now possible to call useQuery and other query functions without requiring the user to be logged in. Previously trying to access the database while being logged out triggered an error.

Tables are of course still protected by policies. To e.g. allow read-only access to logged out users, you need to define a policy like this:

NPM Module

The location of the ihp-backend has moved from a custom .tar.gz file at https://ihpbackend.digitallyinduced.com/ihp-backend.tar.gz to a normal npm module. Additionally the "ihp-datasync" module is now re-exported from the ihp-backend package, so instead of two dependencies, you only need a single one now.

To keep using the latest version of the JS SDK, change your package.json like this:

New Demo Video

Our previous demo video was already a bit outdated as a lot of things have improved since early january. You can find the new demo video here.

Custom App Icons

You can now upload your own App Icon visible on the Login Screen of your App.

To change the Icon, open the project settings and click on Auth:

New Svelte Guide

If you're a Svelte Fan this is good news! You can check out the Svelte Guide in the Docs.

New Next.js Guide

We've previously already supported Next.js by using the Next.js template. Additionally we now have documentation on how to use IHP Backend with Next.js when not using that template project.

Check it out in the docs.

Improved Domains

Previously backend domains were in the format random string.di1337.com. When working with multiple projects in can be confusing what Backend URL points to what project again. To fix this new projects now have domains in the format project name.di1337.com.

Here's a before (left) and after (right):

Other Changes

  • The Startpage and Documentation is much more mobile-friendly now
  • We've added a live demo to the startpage
  • Fixed LIMIT and OFFSET queries like query("tables").limit(100) not working
  • The API Reference is now visible to logged out users as well
  • The API Reference now has a section about the lower-level DataSubscription API
  • The useCurrentUser function has moved to better support expo. Update you imports from import { useCurrentUser } from 'ihp-backend' to import { useCurrentUser } from 'ihp-backend/react'
  • Documentation Pages have og:meta tags now, so they look nicer when sharing on slack or twitter
  • We've made some more design changes to the projects overview screen
  • Improved error handling when a new record is added via the Data Editor, but a required field is not set. Previously this would lead to an internal server error. Now it shows a helpful error message.
  • The useCurrentUser function is now implemented in terms of useQuery, so it will automatically update when the user record has been changed (e.g. the user profile picture was changed). This also saves one HTTP request and lowers latency.
  • When adding a user_id column to a table in the Schema Designer, the default value is now set to ihp_user_id() by default. This means that code like createRecord('tasks', { title: 'Hello World', userId: getCurrentUserId() }) can now be written as createRecord('tasks', { title: 'Hello World' }).
  • The IHP Backend database server was moved into the same region as the main app server to improve latency.
  • Fixed a bug where null was not correctly encoded in a query like query("table").filterWhere("someCol", null)
  • Realtime functions like useQuery now take the ORDER BY into account when deciding whether a newly added record should be appended or prepended to the result set. E.g. if you display the latest 10 tasks (latest first), useQuery will now add new tasks at the beginning of the result set instead of adding it to the end.