Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Object storage (/docs/compute/object-storage)

For the complete Prisma documentation index, see llms.txt. A markdown version of any docs page is available by appending .md to its URL.

Store and serve files from a Prisma Compute app with an Object Store bucket, S3-compatible storage that lives in the same Prisma project as your database.

Location: Compute > Object storage

Object Store buckets are S3-compatible file storage for your Prisma project: a place for user avatars, PDF exports, uploaded CSVs, generated images. A bucket lives inside the project, next to its Prisma Postgres databases, and is managed from the same Console and REST API, so you don't need a separate storage provider or a second set of credentials.

This page walks the whole path: create a bucket, mint an access key, and deploy a Compute route that writes a file and serves it back over a presigned URL.

Buckets speak the S3 API, so any S3 client or SDK works. Compute apps run on Bun, and Bun ships a built-in S3 client, so the examples below need no extra dependency.

  • A bucket belongs to a project. It can be associated with a branch at creation, via branchId or branchGitName. Omit both and the bucket attaches to the project's default branch. Paired with Compute branching, a preview environment can carry its own files next to its own database.
  • Access keys are minted per bucket with a read or read_write role, enforced by the storage layer: a write with a read key is rejected with AccessDenied. The secretAccessKey is returned exactly once at mint time and is not retrievable afterward.
  • Deleting a bucket removes its contents and keys in the same call, even when it is not empty. There is no empty-the-bucket-first step, so treat deletion as destructive and confirm it deliberately.

You can manage buckets from the Console, with the CLI's bucket commands, or over the REST API with a service token. This page uses the API, since that is what you would automate against. The Console and CLI cover the same operations, so the steps map one to one.

The whole API surface is seven endpoints:

Operation Endpoint
Create a bucket POST /v1/buckets
List buckets GET /v1/buckets
Get a bucket GET /v1/buckets/{bucketId}
Delete a bucket DELETE /v1/buckets/{bucketId}
Mint an access key POST /v1/buckets/{bucketId}/keys
List keys GET /v1/buckets/{bucketId}/keys
Revoke a key DELETE /v1/buckets/{bucketId}/keys/{keyId}

[!NOTE] Object Store pricing and plan limits are not published yet. Keep that in mind before provisioning storage unattended, for example from an agent workflow.

  • A Prisma project. The deploy quickstart creates one if you don't have one yet.
  • A service token for the API examples, created in the Console under your workspace's Settings, then Service Tokens. If you prefer clicking, you can create the bucket and key in the Console instead and skip to step 3.
  • Your project id, visible in the project's Console URL or one GET /v1/projects call away.

Create a bucket in your project. The response includes the bucket id you need for every later call.

Create a bucket
curl -X POST https://api.prisma.io/v1/buckets \
  -H "Authorization: Bearer $PRISMA_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId": "<your project id>", "name": "uploads"}'

The response comes back with "status": "ready" and a branchId pointing at your project's default branch. There is nothing to wait for; you can write to the bucket right away. To tie it to a specific branch instead, pass branchId or branchGitName in the request body.

Mint a key for the bucket. The response contains everything an S3 client needs: accessKeyId, secretAccessKey, endpoint, and bucketName.

Mint a read-write key
curl -X POST https://api.prisma.io/v1/buckets/<bucketId>/keys \
  -H "Authorization: Bearer $PRISMA_SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "app-key", "role": "read_write"}'

Save the secretAccessKey now; this is the only time the API returns it. If you lose it, revoke the key and mint a new one.

Give each key the narrowest role that works: a deployment that only serves files should hold a read key, and read_write stays with the code paths that upload.

Store the four values as environment variables so your deployed app can reach the bucket. Bun's S3 client reads these exact names by default, so the code below needs no explicit configuration:

Set the bucket variables
npx prisma project env add S3_ENDPOINT=<endpoint> --role production
npx prisma project env add S3_BUCKET=<bucketName> --role production
npx prisma project env add S3_ACCESS_KEY_ID=<accessKeyId> --role production
npx prisma project env add S3_SECRET_ACCESS_KEY=<secretAccessKey> --role production

Environment variables are scoped by role and branch, so production and previews can point at different buckets: repeat the commands with --role preview (or --branch <name>) and the credentials of a second, branch-associated bucket. Values resolve at deploy time, so set them before the deploy that should use them.

4. Read, write, and serve files from a route

Section titled “4. Read, write, and serve files from a route”

A storage route is an ordinary Compute deployment. The example below uses Hono to match the other Compute examples; the S3 code is identical in any framework. Bun.s3 picks up the S3_* variables from step 3.

src/index.ts
import { Hono } from "hono";

const app = new Hono();

// Upload: write the request body to the bucket.
app.put("/files/:name", async (c) => {
  const name = c.req.param("name");

  if (!/^[a-zA-Z0-9_.-]+$/.test(name)) {
    return c.text("Invalid file name", 400);
  }

  await Bun.s3.file(`uploads/${name}`).write(await c.req.arrayBuffer());
  return c.json({ stored: `uploads/${name}` }, 201);
});

// Serve: redirect to a presigned URL instead of proxying the bytes.
// The URL is time-boxed and needs no credentials on the client.
app.get("/files/:name", (c) => {
  const name = c.req.param("name");

  if (!/^[a-zA-Z0-9_.-]+$/.test(name)) {
    return c.text("Invalid file name", 400);
  }

  const url = Bun.s3.file(`uploads/${name}`).presign({ expiresIn: 3600 });
  return c.redirect(url, 302);
});

export default app;

Deploy it as part of your Composer app, as the deploy quickstart sets it up: build, then run deploy. If you have set up deploy on push, committing and pushing deploys it instead. Then check the result:

Deploy
npm run build
npx prisma deploy module.ts
npx prisma service show

Upload a file to the deployed app, then fetch it back through the presigned redirect:

Upload and read back
curl -X PUT --data "hello from a bucket" https://<your-app-url>/files/hello.txt
curl -L https://<your-app-url>/files/hello.txt

You should see the upload respond with {"stored":"uploads/hello.txt"} and the second request print hello from a bucket. If the upload fails with AccessDenied, the app is holding a read key; mint a read_write key for the uploading deployment and update the environment variables.

Deleting a bucket removes its objects and its access keys in one call:

Delete a bucket
curl -X DELETE https://api.prisma.io/v1/buckets/<bucketId> \
  -H "Authorization: Bearer $PRISMA_SERVICE_TOKEN"

This succeeds even when the bucket still contains objects. That makes tearing down an environment a single call, but it also means there is no undo, so double-check the bucket id before running it. If an agent manages your buckets, have it confirm deletions with you first.

  • Alchemy: Provision Prisma Postgres and deploy applications to Prisma Compute in one TypeScript stack.
  • Branching: Branches are isolated environments that map to your Git branches, so preview work never touches production.
  • Deploy Button: Add a Deploy with Prisma button that copies a public Composer repository and starts a Composer-managed deployment.
  • Deploy on push: Graduate a Composer app from manual deploys to a Git workflow, with production deploys on push and an isolated preview environment per branch.
  • Deployments: How deploys create service versions on Prisma Compute, and how to inspect, promote, roll back, start, and stop them.
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu