1---
2title: Build an upload journal
3description: Sign in, upload private files, process real storage events, and view Postgres-backed status with Neon and Alchemy.
4---
5
6Build a small upload application using Neon Postgres, private Object Storage, Functions, managed Better Auth, and a Vite frontend. A real storage event checks the uploaded metadata and records the result in Postgres. The browser never marks an upload ready itself.
7
8The complete source is in [`examples/neon`](https://github.com/alchemy-run/alchemy/tree/main/examples/neon). Use that directory as the runnable project; the following pages explain its changes one concept at a time. Both the default Effect Function and the native Fetch alternative implement the same browser API.
9
10## Prerequisites
11
12Complete [Setup](/neon/setup) before deploying. Use Node.js 24, Bun, pnpm, and a Neon account with Object Storage and Functions available. Configure the Alchemy `testing` profile for Neon or supply `NEON_API_KEY` to the deployment process only. This tutorial selects `aws-us-east-2`, where the combined backend is available. Resources can incur normal Neon usage charges; no AI gateway, model call, credits, or plan purchase is required.
13
14Install the repository's workspace dependencies before running the example. Keep the local `.alchemy` state directory: it records which resources a later destroy command owns.
15
16## Open the example
17
18```diff lang="sh"
19+cd examples/neon
20```
21
22All subsequent commands run from this directory, so the migration and Function entrypoint paths resolve correctly.
23
24## Deploy the Effect application
25
26```diff lang="sh"
27+pnpm deploy --profile testing --stage upload-journal
28```
29
30The stack returns the website URL, API URL, Auth URL, project ID, branch ID, and bucket name. Open the website URL to create an account; it contains no database password or deployment API key.
31
32## Use the native alternative instead
33
34```diff lang="sh"
35+pnpm deploy:native --profile testing --stage upload-journal
36```
37
38This selects `alchemy.native.ts`, which deploys a separate stack with an ordinary asynchronous Fetch handler and an explicit `FunctionTrigger`. Choose one alternative for normal use; deploying both intentionally creates two independent backends.
39
40## Run the frontend against a deployed backend
41
42```diff lang="sh"
43+VITE_API_URL='<apiUrl>' VITE_NEON_AUTH_URL='<authUrl>' pnpm dev:web
44```
45
46Vite opens on `http://127.0.0.1:43187`. Managed Auth permits localhost for this tutorial, and the API defaults to wildcard CORS for bearer-token requests without cookies. Every data request still requires a verified JWT. For an origin-restricted deployment, set `UPLOAD_APP_ORIGIN` to the known frontend origin before deployment. Starting Vite does not create a fake backend or bypass authentication.
47
48:::caution[Deployment verification]
49The example depends on the new Neon provider and Website integrations. The current SDK probes report stale runtime code and environment values after both configuration-only and full-ZIP redeploys, despite the requested active deployment ID advancing. There is no verified workaround. A successful control-plane update is therefore not proof that Functions serve the new values: verify runtime behavior after deployment. If `APP_ORIGIN`, Auth configuration, or Function deployment readiness fails, keep that failure visible rather than substituting deployment credentials or bypassing verification.
50:::
51
52## Follow the implementation
53
541. [Create the database and storage](/neon/tutorial/backend/).
552. [Protect and process uploads](/neon/tutorial/functions/).
563. [Connect the browser](/neon/tutorial/frontend/).
574. [Fork a preview and clean up](/neon/tutorial/previews/).
58
59## Understand the tutorial's limits
60
61Processing verifies object size and content type; it does not scan for malware. The 10 MiB limit is an application check before signing and after delivery, not an S3-enforced upload quota. A signed URL is a temporary bearer capability and can be reused until expiry. Add rate limits, upload budgets, stronger content validation, and an appropriate download domain before exposing a production service.
62
63The example deliberately disables email verification and enables localhost. Follow [Production Auth](/neon/guides/production-auth) to prepare SMTP, OAuth callbacks, trusted origins, email verification, and localhost restrictions before launch. Signout revokes the managed session, but already issued JWTs can remain valid until their short expiry.