Prisma is a popular ORM for JavaScript (Node, Bun, Deno) frameworks, and supports a wide range of databases and deployment targets. If you look at the deployment paradigms (namely Traditional Servers, Serverless Functions, and Edge Functions), fly.io with its fast booting VMs and auto-stop/start feature fits all three categories.
Deploying a Prisma application involves a build step, creating a database, running of migrations, seeding the database, and an optional (depending on the framework) prerendering step.
With flyctl v0.3.58 and @flydotio/dockerfile release 0.6.0, all that is required is fly launch
.
Demo - PostgreSQL
Prisma has guides for deploying PostgreSQL applications to traditional servers - Heroku, Render, and Koyeb. Deploying the render example application to fly.io can be done as follows:
curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=2 prisma-examples-latest/deployment-platforms/render
cd render
fly launch
Accept the defaults and your app is up and running. From there, we have guides for monitoring, backing up, scaling, tuning, and upgrading your database.
Demo - Sqlite3 / Litestream
Prisma also has a number of Sqlite3 ORM Fullstack applications using Next.js, Nuxt, Sveltekit, and Remix. Deploying the Next.js tRPC example and deploying it to fly.io can be done as follows:
npx try-prisma@latest --template orm/nextjs-trpc
cd orm_nextjs-trpc
fly launch
Accept the defaults and again your app is up and running. The database will be placed on a volume so that it survives restarts. The volume is configured to start out at 1GB and expand to 10GB as needed. You can configure this in your fly.toml.
Unlike PostgreSQL, Sqlite3 is an embedded database which typically would mean that you are on your own and run the risk of exposure to data loss if there is a host or volume failure. That’s why we default to installing and configuring Litestream and Tigris - together they provide continuous and no-worry backups.
To opt out, indicate that you want to tweak the settings before proceeding and unselect Tigris.
To demononstrate worry free backups, use the application to make a draft blog post:
- Click on the “Signup” button
- Enter your name and email address, and click on “Signup”
- Click on “Create Draft”
- Enter title, your email address, content, and click “Create”
- See your post on Drafts
Now destroy your machine and volume. If you have jq installed you can do so with the following commands:
fly machine list --json | jq -r ".[]|.id" | xargs fly machine destroy -f
fly volume list --json | jq -r ".[]|.id" | xargs fly volume destroy -y
(If you don’t have jq
, omit --json
and run the destroy commands directly. Or just brew install jq
/ apt-get install jq
)
Once this is done, run fly deploy
, visit your web page, and click on drafts. Your post should be there.
While this handles disaster recovery, more work is needed to support automatic failover. Perhaps some combination of leasing, machine metadata, and dynamic request routing could make this seamless too.
How fly launch support for Prisma works
Ultimately this is all made possible because Prisma is declarative. Here’s an excerpt from a typical
prisma/schema.prisma
file:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
And an excerpt from a typical package.json
file:
"prisma": {
"seed": "node prisma/seed.js"
}
dockerfile-node
uses this information to customize the configuration for your application at fly launch
time.
The current implementation is limited to sqlite3 and postgresql. Adding other data source providers would be straightforward.
Adding support for ORMs like TypeORM or Sequelize would be predicated on finding ways to automatically determine this information in applications that use those ORMs.
Preview: prerendering/SSG
There is more to be done. We are continuing to look into prerendering/SSG. This needs to be done with live databases so we are currently running this step on each startup. A combination of rolling deploys and auto-stop="suspend"will minimize downtime, particularly for PostgreSQL applications; but we can do better. This can be done on an ephemeral machine with the results pushed to Tigris and either downloaded by deployed machines, or potentially served directly from there. With Litestream, this could even work with sqlite3 - we can use litestream restore
to obtain a current snapshot, and use it as a read-only replica to prerender against.
Meanwhile, you can specify --auto-stop=suspend
on fly launch
or modify your fly.toml
. Rolling deploys are the default for postgres applications, but not an option for a sqlite3 running on a single machine.
Try it out! Let us know what we are missing or could do better.