Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Principles
Tooling & Workspace
Workspace Overview
Cli
Jobs & Commands
SDK
Error Handling
Extensions
Typescript
Go
Python
Docker
Ci
Frameworks
Typescript
OverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart Client
Go
OverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTesting
Platform
  1. DocsSeparator
  2. How ToSeparator
  3. Build An Api Service

Build an API service

You will create an API service with file-based routing, then add schema validation and typed request handling.

Companion sample: samples/ts/02-rest-api — a runnable CRUD API with validation and OpenAPI. Run bunx putnami serve @sample/ts-rest-api from the workspace root.

Steps

1) Create the workspace and API project

putnami init --workspace my-workspace
cd my-workspace
putnami projects create api --template typescript-server

The typescript-server template scaffolds an API project with @putnami/application, file-based routing, and example GET/POST endpoints.

2) Run it

putnami serve api

Open http://localhost:3000. You see the default JSON response from the GET endpoint.

3) Explore the project structure

The scaffolded project lives in packages/api/:

packages/api/
├── src/
│   ├── main.ts          # Application entrypoint
│   ├── serve.ts          # Server bootstrap
│   └── api/
│       ├── get.ts        # GET / endpoint
│       └── post.ts       # POST / endpoint
└── test/
    └── api.test.ts

4) Add a health check route

Create packages/api/src/api/health/get.ts:

import { endpoint } from '@putnami/application';

export default endpoint(() => {
  return { ok: true };
});

Navigate to http://localhost:3000/health.

5) Add a validated route

Create packages/api/src/api/items/post.ts:

import { endpoint, MinLength, Optional, ArrayOf } from '@putnami/application';

export default endpoint()
  .body({
    name: MinLength(1),
    description: Optional(String),
    tags: Optional(ArrayOf(String)),
  })
  .handle(async (ctx) => {
    const { name, description, tags } = await ctx.body();
    return {
      item: {
        id: crypto.randomUUID(),
        name,
        description: description ?? '',
        tags: tags ?? [],
      },
    };
  });

Sending an invalid body returns a 400 automatically:

POST /items
Content-Type: application/json
{ "name": "" }

-> 400 { "message": "body.name must have length >= 1", ... }

6) Add a route with path parameter validation

Create packages/api/src/api/items/[id]/get.ts:

import { endpoint, Uuid } from '@putnami/application';

export default endpoint()
  .params({ id: Uuid })
  .handle((ctx) => {
    // ctx.params.id is typed as string and validated as UUID
    return { item: { id: ctx.params.id } };
  });

Result

You have a running API service with file-based routing and built-in schema validation. Invalid requests are rejected before your handler runs with structured error messages.

Next steps

  • See the API reference for all schema types (Uuid, Email, Int, Min, Max, Pattern, ...) and builder methods
  • Add persistence for a database-backed API
  • Add authentication to protect routes

On this page

  • Build an API service
  • Steps
  • 1) Create the workspace and API project
  • 2) Run it
  • 3) Explore the project structure
  • 4) Add a health check route
  • 5) Add a validated route
  • 6) Add a route with path parameter validation
  • Result
  • Next steps