← Back to Blog

Backend with TypeScript, PostgreSQL & Prisma: CI & Deployment

Daniel Norman
Daniel Norman
September 17, 2020
Updated July 3, 2026

Update (July 2026): This article was updated for Prisma ORM 7. Continuous integration still runs on GitHub Actions, but deployment now targets Prisma Compute, which runs your app next to your Prisma Postgres database, instead of Heroku. All commands work with Prisma ORM 7 and Node.js 20 or later.

In this fourth and final part of the series, you'll configure continuous integration (CI) with GitHub Actions to run the tests, and deploy the backend to Prisma Compute.

Introduction

The goal of the series is to explore and demonstrate different patterns, problems, and architectures for a modern backend by solving a concrete problem: a grading system for online courses.

What the series covers

TopicPart
Data ModelingPart 1
CRUDPart 1
AggregationsPart 1
REST API layerPart 2
ValidationPart 2
TestingPart 2
Passwordless AuthenticationPart 3
AuthorizationPart 3
Integration with external APIsPart 3
Continuous IntegrationPart 4 (current)
DeploymentPart 4 (current)

In the previous parts you built and secured a Hono REST API on top of a Prisma data model. In this article, you will set up GitHub Actions to run the tests on every push, then deploy the backend to Prisma Compute.

Prisma Compute is TypeScript app hosting that runs your application on the same platform as your Prisma Postgres database. Because the app and database live together, you avoid the connection churn and cross-network latency that come with hosting them separately, and there's no separate database add-on to provision.

Note: Throughout the guide, you'll find various checkpoints that enable you to validate whether you performed the steps correctly.

Prerequisites

This article continues from part 3. You'll need:

  • The grading-app project from the previous parts, pushed to a GitHub repository.
  • A free Prisma Data Platform account (the same one you used to create your Prisma Postgres database).
  • Node.js 20 or later.

Continuous integration and continuous deployment

Continuous integration (CI) is the practice of integrating work into the main repository frequently, running an automated pipeline on every change to catch bugs early. Continuous deployment (CD) automates releasing those changes so they ship rapidly and consistently.

While CI and CD have different responsibilities, they're often handled with the same tooling. Here you'll use GitHub Actions for CI (running the tests) and Prisma Compute's Git integration for CD (deploying on push).

Continuous integration pipelines

The main building block of CI is a pipeline: a set of steps that ensure no regressions are introduced. A pipeline might run tests, a linter, and the TypeScript compiler. If a step fails, the run stops and reports back to GitHub. When working with pull requests, the pipeline runs automatically for every PR.

The tests you wrote in part 2 and extended in part 3 send requests to the API's endpoints, and the handlers talk to the database. So the CI run needs a PostgreSQL database with the backend's schema for the duration of the tests. You'll configure GitHub Actions to start a throwaway Postgres database and apply the migrations before running the tests.

Note: CI is only as good as your tests. If coverage is low, passing tests can create a false sense of confidence.

Defining a workflow with GitHub Actions

GitHub Actions runs workflows defined in YAML files under .github/workflows/. A workflow has jobs, and each job has steps. Create .github/workflows/grading-app.yml:

name: grading-app
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: grading-app
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    env:
      DATABASE_URL: postgresql://postgres:postgres@localhost:5432/grading-app
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx prisma migrate deploy
      - run: npx prisma generate
      - run: npm test

The test job:

  1. Checks out the repository.
  2. Sets up Node.js 20.
  3. Installs dependencies with npm ci.
  4. Applies the committed migrations to the throwaway Postgres database with prisma migrate deploy.
  5. Generates Prisma Client with prisma generate.
  6. Runs the tests with npm test.

The services block starts a PostgreSQL container that lives for the duration of the job, and DATABASE_URL points the app at it. prisma migrate deploy applies your existing migration files (unlike migrate dev, it never generates new ones), which is exactly what you want in CI and production.

Note: on: [push, pull_request] runs the pipeline for every push and every pull request.

Checkpoint: Commit and push the workflow, then open the Actions tab of your GitHub repository. You should see the grading-app workflow run and the test job pass.

Deploying to Prisma Compute

With CI in place, you'll deploy the backend to Prisma Compute using the Prisma CLI.

Prepare the app to read the port

Compute assigns your app a port at runtime. Make the server read it from the environment, falling back to 3000 locally. Update src/index.ts:

import { serve } from "@hono/node-server";
import app from "./app";

serve(
  {
    fetch: app.fetch,
    port: Number(process.env.PORT) || 3000,
  },
  (info) => {
    console.log(`Server is running on http://localhost:${info.port}`);
  },
);

Authenticate the CLI

Sign in once so the CLI can deploy on your behalf:

npx @prisma/cli@latest auth login

This opens the browser and stores a local session that later commands reuse.

Deploy

From the project directory, deploy the app. The CLI auto-detects that this is a Hono app, builds it, and returns a live URL:

npx @prisma/cli@latest app deploy

Your app connects to the same Prisma Postgres database you've used throughout the series, so there's no separate database to provision. (If you wanted Compute to provision a fresh database as part of the deploy, you'd add the --db flag.)

Checkpoint: When the deploy finishes, the CLI prints your live URL. Open it and confirm the status route responds:

npx @prisma/cli@latest app open

Visiting the root should return {"up":true}.

Note (optional): For reproducible deploys you can commit a typed prisma.compute.ts config. It's not required, since app deploy auto-detects the framework, and it needs the @prisma/compute-sdk package:

import { defineComputeConfig } from "@prisma/compute-sdk/config";

export default defineComputeConfig({
  app: {
    framework: "hono",
    entry: "src/index.ts",
    httpPort: 3000,
  },
});

Fields you set become defaults for app deploy; explicit flags override them. See the Prisma Compute docs for details.

Setting environment variables

The backend needs two runtime secrets from part 3: JWT_SECRET (used to sign JWTs) and RESEND_API_KEY (used to send login emails). DATABASE_URL is managed for you by the platform. Set the secrets for the production environment:

npx @prisma/cli@latest project env add JWT_SECRET=your-secret --role production
npx @prisma/cli@latest project env add RESEND_API_KEY=your-key --role production

Note: Generate a strong JWT_SECRET with: node -e "console.log(require('crypto').randomBytes(64).toString('base64'))". Use --role preview to set values for preview deployments.

Deploying automatically on every push

To make deployment continuous, connect the repository to Prisma Compute's Git integration:

npx @prisma/cli@latest git connect

Once connected, every push to your default branch deploys automatically, and pushes to other branches get their own preview URL. This is the modern equivalent of the deploy-on-push pipeline, with the CLI handling the build and release instead of a custom workflow job. Your GitHub Actions workflow stays focused on running the tests.

To stream the deployed app's logs while you debug:

npx @prisma/cli@latest app logs

Testing the login flow in production

With the app deployed, exercise the passwordless login flow from part 3 against the live URL. First, start login:

curl -X POST -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}' \
  https://YOUR_APP_URL/login

Because RESEND_API_KEY is set in production, the token is emailed rather than logged. Check your inbox for the eight-digit token, then exchange it for a JWT:

curl -i -X POST -H "Content-Type: application/json" \
  -d '{"email":"you@example.com", "emailToken":"12345678"}' \
  https://YOUR_APP_URL/authenticate

Checkpoint: The response has a 200 status and an Authorization header containing the JWT:

Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbklkIjo4fQ.ea2lBPMJ6mrPkwEHCgeIFqqQfkQ2uMQ4hL-GCuwtBAE

Summary

Your backend is now tested on every push and deployed to Prisma Compute. Well done, and congratulations on finishing the series.

You configured a GitHub Actions workflow that spins up a throwaway PostgreSQL database, applies your migrations, and runs the tests, then deployed the app to Prisma Compute with the Prisma CLI and wired up deploy-on-push with the Git integration. Because the app runs next to your Prisma Postgres database, you skipped the separate database add-on and the connection-churn concerns of hosting them apart.

Over the four parts, you designed a data model, built a REST API with Hono, secured it with passwordless authentication and authorization, and shipped it with CI/CD, using Prisma Client and Prisma Migrate throughout, with the schema as the single source of truth.

If AI agents are part of your workflow, the Prisma MCP server lets them create and manage Prisma Postgres databases as they need them.

This series teaches Prisma ORM 7, the current production release. To see where Prisma ORM is heading for AI-assisted development, read The Next Evolution of Prisma ORM. Prisma Next is the agent-native evolution of the ORM, available in Early Access today, and it becomes Prisma 8 at GA. It builds on the same schema-as-contract idea you relied on across this series, with structured, type-safe output that humans and coding agents can build on safely, and you can adopt it incrementally alongside an existing project. The performance benchmark covers what the rewrite means for throughput and client size.

Build your next app with Prisma

Start free. Scale when you’re ready.

Try Prisma
Share this article