Skip to content

Quick Start

Build a working API with authentication, validation, and role-based field projection in five minutes. You’ll create a user resource with public and authenticated endpoints, then test them with curl.

  1. Create a new project

    Using the --auth template here:

    Terminal window
    npx create-velox-app my-api --auth
    cd my-api
    pnpm approve-builds # Allow native module compilation (pnpm only)
    pnpm db:push
  2. Define your procedure

    Edit src/procedures/users.ts:

    import { procedures, procedure, resourceSchema, resource } from '@veloxts/velox';
    import { z } from '@veloxts/velox';
    // Define resource schema with field visibility
    const UserSchema = resourceSchema()
    .public('id', z.string())
    .public('name', z.string())
    .authenticated('email', z.string())
    .build();
    export const userProcedures = procedures('users', {
    // GET /api/users - public endpoint, manual projection
    listUsers: procedure()
    .query(async ({ ctx }) => {
    const users = await ctx.db.user.findMany();
    return users.map(user => resource(user, UserSchema.public));
    }),
    // GET /api/users/:id - public endpoint, manual projection
    getUser: procedure()
    .input(z.object({ id: z.string() }))
    .query(async ({ input, ctx }) => {
    const user = await ctx.db.user.findUniqueOrThrow({
    where: { id: input.id }
    });
    return resource(user, UserSchema.public);
    }),
    // POST /api/users - public endpoint
    createUser: procedure()
    .input(z.object({
    name: z.string().min(1),
    email: z.string().email(),
    }))
    .mutation(async ({ input, ctx }) => {
    const user = await ctx.db.user.create({ data: input });
    return resource(user, UserSchema.public);
    }),
    });

    For authenticated endpoints, add a guard and use a tagged schema view to return the appropriate fields:

    import { authenticated } from '@veloxts/auth';
    // Authenticated endpoint
    getProfile: procedure()
    .input(z.object({ id: z.string() }))
    .guard(authenticated)
    .query(async ({ input, ctx }) => {
    const user = await ctx.db.user.findUniqueOrThrow({ where: { id: input.id } });
    return resource(user, UserSchema.authenticated); // { id, name, email }
    }),
  3. Start the dev servers

    This starts both the API backend and the frontend dev server:

    Terminal window
    pnpm dev

    Your frontend is now running at http://localhost:8080.

  4. Test your API

    Terminal window
    # List users
    curl http://localhost:3030/api/users
    # Create a user
    curl -X POST http://localhost:3030/api/users \
    -H "Content-Type: application/json" \
    -d '{"name": "Alice", "email": "alice@example.com"}'
    # Get a user
    curl http://localhost:3030/api/users/1

Velox TS uses procedure name prefixes to determine HTTP methods:

PrefixHTTP MethodPath
list*GET/api/{resource}
get*GET/api/{resource}/:id
create*POST/api/{resource}
update*PUT/api/{resource}/:id
delete*DELETE/api/{resource}/:id

See REST Conventions for the full list.

Every procedure handler receives a ctx object containing:

  • ctx.db - Prisma client for database access
  • ctx.request - Fastify request object
  • ctx.reply - Fastify reply object