SemiLayerDocs

Example: Direct Queries

The query facet reads directly from your source with server-side filtering — useful when you need exact matches, range filters, or paginated browsing without semantic ranking.


Schema

Enable the query facet in your lens:

// sl.config.ts
import { defineConfig } from '@semilayer/core'

export default defineConfig({
  stack: 'my-app',
  sources: {
    'main-db': { bridge: '@semilayer/bridge-postgres' },
  },
  lenses: {
    events: {
      source: 'main-db',
      table: 'public.events',
      primaryKey: 'id',
      fields: {
        id:        { type: 'number', primaryKey: true },
        title:     { type: 'text', searchable: true },
        category:  { type: 'text' },
        startsAt:  { type: 'date', from: 'starts_at' },
        endsAt:    { type: 'date', from: 'ends_at' },
        cityId:    { type: 'number', from: 'city_id' },
        capacity:  { type: 'number' },
        price:     { type: 'number' },
      },
      facets: {
        search: { fields: ['title'] },
        query:  {},   // enable direct queries — no extra config needed
      },
      rules: {
        search: { allowPublicKey: true },
        query:  { allowPublicKey: false },  // backend only
      },
    },
  },
})
semilayer push --resume-ingest
semilayer generate

Querying with Filters

import { createBeam } from '@/generated/semilayer'

const beam = createBeam({
  baseUrl: process.env.SEMILAYER_BASE_URL!,
  apiKey: process.env.SEMILAYER_API_KEY!,
})

// Simple equality filter
const { rows } = await beam.events.query({
  where: { category: 'music', cityId: 5 },
  limit: 20,
})

// rows[0].title    ← string
// rows[0].startsAt ← Date
// rows[0].price    ← number

API Route with Filters

// app/api/events/route.ts
import { createBeam } from '@/generated/semilayer'
import { NextRequest } from 'next/server'

const beam = createBeam({
  baseUrl: process.env.SEMILAYER_BASE_URL!,
  apiKey: process.env.SEMILAYER_API_KEY!,
})

export async function GET(req: NextRequest) {
  const params = req.nextUrl.searchParams

  const where: Record<string, unknown> = {}
  if (params.get('category')) where.category = params.get('category')
  if (params.get('cityId'))   where.cityId = Number(params.get('cityId'))

  const page   = Number(params.get('page') ?? '1')
  const limit  = Number(params.get('limit') ?? '20')
  const offset = (page - 1) * limit

  const { rows, total } = await beam.events.query({
    where,
    orderBy: { field: 'startsAt', dir: 'asc' },
    limit,
    offset,
  })

  return Response.json({
    events: rows,
    pagination: {
      page,
      limit,
      total: total ?? rows.length,
      pages: total ? Math.ceil(total / limit) : undefined,
    },
  })
}

Hybrid: Search + Query in One Handler

Combine semantic search with a filtered query to let users switch modes:

// app/api/events/search/route.ts
import { createBeam } from '@/generated/semilayer'

const beam = createBeam({
  baseUrl: process.env.SEMILAYER_BASE_URL!,
  apiKey: process.env.SEMILAYER_API_KEY!,
})

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url)
  const q        = searchParams.get('q') ?? ''
  const category = searchParams.get('category') ?? undefined
  const limit    = Number(searchParams.get('limit') ?? '20')

  if (q.trim()) {
    // Semantic search — query drives results, category filters client-side
    const { results } = await beam.events.search({ query: q, limit: limit * 2, mode: 'hybrid' })
    const filtered = category ? results.filter(r => r.metadata.category === category) : results
    return Response.json({ mode: 'search', results: filtered.slice(0, limit) })
  } else {
    // No query — fall back to direct browse with server-side filter
    const { rows } = await beam.events.query({
      where: category ? { category } : {},
      orderBy: { field: 'startsAt', dir: 'asc' },
      limit,
    })
    return Response.json({ mode: 'query', results: rows })
  }
}

Curl

# Direct query via HTTP API
curl 'https://api.semilayer.com/v1/query/events' \
  -H 'Authorization: Bearer sk_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "where": { "category": "music", "cityId": 5 },
    "orderBy": { "field": "startsAt", "dir": "asc" },
    "limit": 20
  }'