Skip to content

Store with product images

Product images for a Next.js store. lib/vaif.ts exports a browser client, and lib/vaif-server.ts exports createVaifServer(), a server client with the secret key. lib/storage.ts uses the server client to keep each product’s images in a product-images bucket, in a folder named after the product:

  • uploadProductImage() uploads a file and returns its path and URLs;
  • listProductImages() lists a product’s images;
  • getSignedImageUrl() returns a link that expires, one hour by default;
  • deleteProductImage() removes one.

The secret key skips every row rule, so call them from Route Handlers and Server Actions only, after checking that the signed-in user may manage products (getCurrentUser() in lib/vaif-server.ts). Both server files import server-only, so a build that pulls them into browser code fails.

Terminal window
vaif init --template ecommerce-api

Run it in your app's folder. It writes vaif.config.json and the template's files there. It stops if the folder already has a vaif.config.json, and leaves any other file that already exists alone; --force overwrites both.

It asks which features to include. This template has files for Database and Functions; Database is selected to start with. To skip the question, name them: vaif init --template ecommerce-api --features database,functions. Add one later with vaif init --template ecommerce-api --add-features <feature>.

FileWritten
lib/vaif.tsAlways
lib/vaif-server.tsAlways
lib/storage.tsAlways
.env.exampleAlways
README.mdAlways
drizzle/0001_initial.sqlWith Database
drizzle.config.tsWith Database
src/db/schema.tsWith Database
functions/hello.tsWith Functions

This template adds files to an app you already have, and writes no package.json. Install the packages its files import:

Terminal window
npm install @vaifstudio/client @vaifstudio/auth server-only

Copy .env.example to .env and fill it in. The project ID and the keys are on Security > Credentials in the console.

VariableValue
NEXT_PUBLIC_VAIF_API_URLThe API address. Keep the value in the file, https://api.vaif.studio.
NEXT_PUBLIC_VAIF_PROJECT_IDYour project ID, for the app.
NEXT_PUBLIC_VAIF_PUBLISHABLE_KEYThe project's publishable key (vk_pub_). Row rules apply to it, so it is safe in an app.
VAIF_SECRET_KEYThe project's secret key (vk_srv_), for server code only. It bypasses row rules; never ship it in a browser or mobile app.
VAIF_PROJECT_IDYour project ID again, without the prefix, for the server client.

Create a bucket named product-images on the Storage page in the console. Make it private and serve images with getSignedImageUrl(), or make it public if every image may be seen at a plain URL. See Buckets and policies.

The template writes one SQL file for your database. The comment line above each CREATE TABLE sets that table's row rules.

drizzle/0001_initial.sql, written with the Database feature, is an example: edit it to describe your own tables before you apply it.

drizzle/0001_initial.sql
-- Initial migration. Customize this schema for your project; vaif db push
-- applies it. The comment directly above each CREATE TABLE sets that table's
-- row rules: here each signed-in user reads and changes only their own rows,
-- and the owner column is filled in from their sign-in on insert. Keep a rule
-- comment on every table you add: a table with no row rules is closed to
-- signed-out visitors but open to every signed-in user.
-- vaif:owner id
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"email" text NOT NULL UNIQUE,
"name" text,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
-- vaif:owner user_id
CREATE TABLE IF NOT EXISTS "posts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"user_id" uuid NOT NULL REFERENCES "users"("id"),
"title" text NOT NULL,
"content" text,
"published" boolean NOT NULL DEFAULT false,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
  • users (-- vaif:owner id): each signed-in user reads and changes only their own rows, and id is filled in from their sign-in.
  • posts (-- vaif:owner user_id): each signed-in user reads and changes only their own rows, and user_id is filled in from their sign-in.

Put your project ID in vaif.config.json ("projectId"), sign in, and apply it. With no --dir, vaif db push finds the template's SQL folder, or a ./migrations folder, and applies its .sql files in name order.

Terminal window
vaif login
vaif db push --dry-run # what each file would change
vaif db push