Autoscaling Report: Production databases on Neon use 2.4x less compute and 50% less cost than if they were running on a provisioned platform.

Objects

Upload, download, list, and delete files

Private Preview

This feature is in private preview: it's not ready for production use, and it may be briefly unavailable as we deploy updates. To get access, sign up here.

Objects in Neon Storage are files stored inside a bucket. Every object has a key (its path within the bucket), a body, a content type, and optional metadata. Objects branch with your database. Each branch inherits the parent's objects at the moment of forking without copying any data.

The examples below show both the Files SDK and the AWS S3 client. See Get started to configure either client, or Authentication if you need to create a credential.

Upload

import { files } from './client';

await files.upload('images/photo.jpg', fileBuffer, {
  contentType: 'image/jpeg',
  metadata: { 'uploaded-by': 'user-123' },
});

note

neon buckets object put uploads via a presigned URL and supports files up to the presign size limit. For large or streaming uploads use the AWS SDK with multipart upload.

Multipart upload

For large files, the AWS SDK automatically uses multipart upload above a configurable threshold. You can also initiate multipart upload manually for fine-grained control.

import { Upload } from '@aws-sdk/lib-storage';
import { client } from './client';
import { createReadStream } from 'fs';

const upload = new Upload({
  client,
  params: {
    Bucket: 'my-bucket',
    Key: 'large-file.zip',
    Body: createReadStream('./large-file.zip'),
  },
  partSize: 10 * 1024 * 1024, // 10 MiB per part
});

await upload.done();

Download

import { files } from './client';

const result = await files.download('images/photo.jpg');
const buffer = await result.arrayBuffer();

You can use range requests for partial downloads:

const response = await client.send(new GetObjectCommand({
  Bucket: 'my-bucket',
  Key: 'video.mp4',
  Range: 'bytes=0-1048575', // first 1 MiB
}));

List objects

Use a prefix to filter results. The Files SDK returns a flat array of items; the S3 client supports a delimiter to simulate folder structure.

import { files } from './client';

const { items } = await files.list({ prefix: 'images/' });
for (const item of items) {
  console.log(item.key, item.size);
}

For buckets with more than 1,000 objects, paginate using ContinuationToken:

let token: string | undefined;
do {
  const response = await client.send(new ListObjectsV2Command({
    Bucket: 'my-bucket',
    ContinuationToken: token,
  }));
  for (const obj of response.Contents ?? []) {
    console.log(obj.Key);
  }
  token = response.NextContinuationToken;
} while (token);

Delete objects

Single object:

import { files } from './client';

await files.delete('images/photo.jpg');

Batch delete (up to 1,000 objects per request):

import { files } from './client';

await files.delete(['images/photo1.jpg', 'images/photo2.jpg']);

Delete a folder (all objects under a prefix):

# The prefix must end with /
neon buckets object delete my-bucket/images/ --recursive

Presigned URLs

Generate a time-limited URL that allows a browser or unauthenticated client to upload or download a specific object, without exposing your credentials.

Presigned GET (download):

import { files } from './client';

const url = await files.url('report.pdf', { expiresIn: 3600 }); // 1 hour
console.log(url); // share this URL — no credentials needed

Presigned PUT (upload from browser):

import { files } from './client';

// Returns { url, method, headers } — pass all three to fetch on the client side
const { url, method, headers } = await files.signedUploadUrl('uploads/user-avatar.png', {
  contentType: 'image/png',
  expiresIn: 300, // 5 minutes
});

// On the client side:
// await fetch(url, { method, headers, body: file });

Object branching

Objects branch with your database. When you fork a branch, the child immediately inherits the parent's buckets and objects at that point in time. No data is copied. From that point:

  • Uploading a new object to a child branch is only visible on that branch and its descendants.
  • Deleting an object on a child branch does not affect the parent.
  • The parent's objects remain unchanged regardless of what happens on child branches.

Next steps

  • Buckets: set access levels, understand bucket branching
  • Authentication: credential scopes and read vs write access

Need help?

Join our Discord Server to ask questions or see what others are doing with Neon. For paid plan support options, see Support.

Was this page helpful?
Edit on GitHub