Create a GitHub Action that can create a new Turso database when a new GitHub branch is made.
A database per branch is a common request from developers using Turso. This approach not only streamlines the development process but also isolates changes, preventing conflicts and preserving data integrity.
In this post, we're going to guide you through setting up a GitHub Action that does exactly this. Learn how to automatically create a new Turso database by cloning an existing one each time a new branch is created using the Turso Platform API.
The Turso CLI makes it super easy to create a database from an existing one:
turso db create my-new-db --from-db my-existing-db
We can do the same thing using the Turso Platform API to create a database:
curl -X POST \
-H "Authorization: Bearer API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "NEW_DATABASE_NAME", "group": "GROUP_NAME", "seed": {"type": "database", "name": "EXISTING_DATABASE_NAME"} }' \
"https://api.turso.tech/v1/organizations/ORGANIZATION_NAME/databases"
The Platform API is what we'll use to create a GitHub Action that creates a database from an existing one when we create a new branch.
Go to your repository settings and go to Security > Security > Secrets and variables > Actions:
Add a new repository secret:
Follow the convention of prefixing secrets with TURSO_
and uppercase:
TURSO_API_TOKEN
TURSO_EXISTING_DATABASE_NAME
TURSO_GROUP_NAME
TURSO_ORGANIZATION_NAME
We will assign NEW_DATABASE_NAME
later inside the Action code.
Create the file .github/workflows/main.yml
with the following contents:
name: Create Database for Branch
on: create
jobs:
create_database:
name: 'Create Database'
runs-on: ubuntu-latest
Before we can execute the cURL to create a new database, we need to update it to reference the secrets from the repository:
curl -X POST \
-H "Authorization: Bearer ${{ secrets.TURSO_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"name": "NEW_DATABASE_NAME", "group": "${{ secrets.TURSO_GROUP_NAME }}", "seed": {"type": "database", "name": "${{ secrets.TURSO_EXISTING_DATABASE_NAME }}"} }' \
"https://api.turso.tech/v1/organizations/${{ secrets.TURSO_ORGANIZATION_NAME }}/databases"
We also assigned NEW_DATABASE_NAME
to the branch name by using github.ref_name
in the code above.
We'll now put it all together and add the cURL request as a step inside of the create_database
job:
jobs:
create_database:
name: 'Create Database'
runs-on: ubuntu-latest
steps:
- name: Create Database via Turso API
shell: bash
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.TURSO_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"name": "${{ github.ref_name }}", "group": "${{ secrets.TURSO_GROUP_NAME }}", "seed": {"type": "database", "name": "${{ secrets.TURSO_EXISTING_DATABASE_NAME }}"} }' \
"https://api.turso.tech/v1/organizations/${{ secrets.TURSO_ORGANIZATION_NAME }}/databases"
We know as developers that not everything goes to plan, and we should handle errors in our GitHub Action. If the API request fails, GitHub Action will still mark the Action as successful.
We want to throw an exit code if the API request fails or there's no Hostname
in our response.
- name: Create Database via Turso API
shell: bash
run: |
RESPONSE=$(curl -s -f -X POST \
-H "Authorization: Bearer ${{ secrets.TURSO_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"name": "${{ github.ref_name }}", "group": "${{ secrets.TURSO_GROUP_NAME }}", "seed": {"type": "database", "name": "${{ secrets.TURSO_EXISTING_DATABASE_NAME }}"} }' \
"https://api.turso.tech/v1/organizations/${{ secrets.TURSO_ORGANIZATION_NAME }}/databases")
if [ $? -ne 0 ]; then
echo "API call failed"
exit 1
fi
It would be useful to assign the Hostname
of the database returned by the API to the GitHub environment so we can use it in further steps. One useful step could be to trigger a deployment for your app with the new Hostname
.
We'll use jq
to parse RESPONSE
and extract the Hostname
value:
- name: Create Database via Turso API
shell: bash
run: |
RESPONSE=$(curl -s -f -X POST \
-H "Authorization: Bearer ${{ secrets.TURSO_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"name": "${{ github.ref_name }}", "group": "${{ secrets.TURSO_GROUP_NAME }}", "seed": {"type": "database", "name": "${{ secrets.TURSO_EXISTING_DATABASE_NAME }}"} }' \
"https://api.turso.tech/v1/organizations/${{ secrets.TURSO_ORGANIZATION_NAME }}/databases")
if [ $? -ne 0 ]; then
echo "API call failed"
exit 1
fi
HOSTNAME=$(echo $RESPONSE | jq -r '.database.Hostname')
if [ -z "$HOSTNAME" ]; then
echo "Hostname not found in response"
exit 1
fi
echo "hostname=$HOSTNAME" >> $GITHUB_ENV
Now we can access the hostname
variable from inside another step:
- name: Echo Hostname
run: echo "The hostname is ${{ env.hostname }}"
You can trigger the workflow by creating a new branch:
Once the branch has been created it will automatically trigger the GitHub Action. Go to the Actions tab and view the Create Database job:
If we create a branch with a name that doesn't meet the requirements of what you can name a database with Turso, the Action will handle the failure and return the exit code:
In the current GitHub Action, there's a chance that your branch name will conflict with the database name requirements set by Turso.
Your branch name can only contain lowercase letters, numbers, and hyphens. You can always add a step to the GitHub Action that uses the ref_name
to create something more bespoke for your application requirements.
We've made it even easier to implement the code above by publishing the code to the GitHub Action Marketplace.
Instead of making a cURL request yourself, you can rely on the official GitHub Action to do all the work!
name: Create Database for Branch
on: create
jobs:
create_database:
name: 'Create Database'
runs-on: ubuntu-latest
steps:
- name: Create Database
uses: tursodatabase/create-database-action@v1
with:
organization_name: ${{ secrets.TURSO_ORGANIZATION_NAME }}
api_token: ${{ secrets.TURSO_API_TOKEN }}
existing_database_name: ${{ secrets.TURSO_DATABASE_NAME }}
new_database_name: ${{ github.ref_name }}
# optional
# group_name: ${{ secrets.TURSO_GROUP_NAME }}
We'd love to hear your feedback, so please join us on Discord to share your ideas. All of the code is available on GitHub too.