Primitives.org.ai

Endpoints

Define service endpoints

Endpoints

Endpoints define how clients interact with your service. Use HTTP method helpers for a clean, declarative API.

HTTP Method Helpers

import { POST, GET, PUT, DELETE, PATCH } from 'services-as-software'
MethodDescription
POST()Create resources, submit data
GET()Retrieve resources
PUT()Replace resources
PATCH()Update resources
DELETE()Remove resources

Basic Endpoint

import { Service, POST } from 'services-as-software'

const service = Service({
  name: 'my-service',
  endpoints: [
    POST({
      name: 'process',
      path: '/process',
      handler: async (input, context) => {
        return { processed: true }
      },
    }),
  ],
})

Endpoint Properties

POST({
  name: 'endpoint-name',       // Identifier
  path: '/path',               // URL path
  handler: async (input, context) => {
    // Implementation
    return result
  },
  rateLimit: {                 // Optional rate limiting
    requests: 100,
    window: 60,
  },
  auth: true,                  // Require authentication
  description: 'What it does', // Documentation
})

Handler Function

The handler receives two parameters:

handler: async (input, context) => {
  // input - Request body/params
  // context - Request context (user, headers, etc.)

  return { result: 'data' }
}

Input

Contains the request data:

POST({
  name: 'translate',
  handler: async (input) => {
    const { text, targetLanguage } = input
    // Process input
    return { translated: '...' }
  },
})

Context

Contains request metadata:

POST({
  name: 'protected',
  handler: async (input, context) => {
    const { user, headers, ip } = context

    if (!user) {
      throw new Error('Unauthorized')
    }

    return { userId: user.id }
  },
})

Multiple Endpoints

const apiService = Service({
  name: 'api-service',
  endpoints: [
    GET({
      name: 'list',
      path: '/items',
      handler: async () => {
        return { items: await db.items.findMany() }
      },
    }),

    GET({
      name: 'get',
      path: '/items/:id',
      handler: async ({ id }) => {
        return await db.items.findById(id)
      },
    }),

    POST({
      name: 'create',
      path: '/items',
      handler: async (data) => {
        return await db.items.create(data)
      },
    }),

    PUT({
      name: 'update',
      path: '/items/:id',
      handler: async ({ id, ...data }) => {
        return await db.items.update(id, data)
      },
    }),

    DELETE({
      name: 'delete',
      path: '/items/:id',
      handler: async ({ id }) => {
        await db.items.delete(id)
        return { deleted: true }
      },
    }),
  ],
})

Rate Limiting

Add rate limits per endpoint:

POST({
  name: 'expensive-operation',
  path: '/generate',
  rateLimit: {
    requests: 10,    // Max requests
    window: 60,      // Per 60 seconds
  },
  handler: async (input) => {
    // Rate-limited operation
  },
})

Authentication

Require authentication:

POST({
  name: 'protected',
  path: '/private',
  auth: true,
  handler: async (input, context) => {
    // context.user is guaranteed to exist
    return { userId: context.user.id }
  },
})

Error Handling

Return errors from handlers:

POST({
  name: 'validate',
  handler: async (input) => {
    if (!input.data) {
      throw new Error('Data is required')
    }

    if (!isValid(input.data)) {
      return {
        error: {
          code: 'VALIDATION_ERROR',
          message: 'Invalid data format',
        },
      }
    }

    return { valid: true }
  },
})

Async Operations

For long-running tasks:

POST({
  name: 'generate-report',
  handler: async (input, context) => {
    // Start async job
    const jobId = await jobs.create({
      type: 'report',
      params: input,
    })

    return {
      jobId,
      status: 'processing',
      checkUrl: `/jobs/${jobId}`,
    }
  },
})

GET({
  name: 'check-job',
  path: '/jobs/:id',
  handler: async ({ id }) => {
    const job = await jobs.get(id)
    return {
      status: job.status,
      result: job.result,
    }
  },
})

Generic Endpoint Helper

For more control, use the generic Endpoint:

import { Endpoint } from 'services-as-software'

Endpoint({
  method: 'POST',
  name: 'custom',
  path: '/custom',
  handler: async (input) => {
    return { custom: true }
  },
})

Type Definition

interface EndpointConfig {
  name: string
  path?: string
  handler: (input: any, context: Context) => Promise<any>
  rateLimit?: {
    requests: number
    window: number
  }
  auth?: boolean
  description?: string
}
Was this page helpful?

On this page