Next.js
Use the JavaScript SDK in browser code with a project user session. Keep TEFILY_SERVICE_TOKEN on the server only. Turn on project auth and owner rules in Auth and owner rules before shipping writes.
NEXT_PUBLIC_* is inlined into the browser bundle. TEFILY_SERVICE_TOKEN is read only in server code.
NEXT_PUBLIC_TEFILY_API_URL=https://api.tefily.comNEXT_PUBLIC_TEFILY_PROJECT_ID=YOUR_PROJECT_ID
TEFILY_API_URL=https://api.tefily.comTEFILY_PROJECT_ID=YOUR_PROJECT_IDTEFILY_SERVICE_TOKEN=kfxs_…NEXT_PUBLIC_* is compiled into the client bundle. Never put kfxs_… or kfx_… there. The project id is not a secret. The service token is. Never prefix TEFILY_SERVICE_TOKEN with NEXT_PUBLIC_.
Client
Section titled “Client”import { KataflaxClient } from '@kataflax/sdk';
export function createBrowserClient() { return new KataflaxClient({ baseUrl: process.env.NEXT_PUBLIC_TEFILY_API_URL!, projectId: process.env.NEXT_PUBLIC_TEFILY_PROJECT_ID!, });}The browser client has no serviceToken. After login, client.token is the project JWT. The browser sends that JWT to your route. It never sends kfxs_.
Server route
Section titled “Server route”app/api/posts/route.ts. The handler verifies the project JWT, then writes with the service token. KataflaxClient constructed with serviceToken sets that token on every call, including auth.verify, which POSTs a JSON body with token to /api/p/:projectId/auth/verify.
import { NextResponse } from 'next/server';import { KataflaxClient } from '@kataflax/sdk';
function serverClient() { return new KataflaxClient({ baseUrl: process.env.TEFILY_API_URL ?? 'https://api.tefily.com', projectId: process.env.TEFILY_PROJECT_ID!, serviceToken: process.env.TEFILY_SERVICE_TOKEN!, });}
export async function POST(request: Request) { const header = request.headers.get('authorization') ?? ''; const userJwt = header.startsWith('Bearer ') ? header.slice('Bearer '.length) : ''; if (!userJwt) { return NextResponse.json({ error: 'Missing session' }, { status: 401 }); }
const server = serverClient(); const checked = await server.auth.verify(userJwt); if (!checked.valid || !checked.user) { return NextResponse.json({ error: 'Invalid session' }, { status: 401 }); }
const body = (await request.json()) as { title?: string }; const created = await server.collection('posts').create({ title: body.title ?? 'Untitled', status: 'draft', authorId: checked.user['_id'], }); return NextResponse.json(created);}The browser call does not include a service token:
await fetch('/api/posts', { method: 'POST', headers: { Authorization: `Bearer ${client.token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ title: 'Hello Tefily' }),});Create the token in the console: project Developers → Service tokens → Create service token. The value is shown once and starts with kfxs_. The verify contract is on Backend service token.
The data API reflects Origin for CORS, so the browser can call https://api.tefily.com directly when owner rules are in place. You do not have to proxy CRUD through Next.js unless you want to hide filters or attach a service token (which you should not expose). This route is for writes that must stay behind the service token.