Ploy
Ploy
Ploy Start

Getting Started

Build type-safe Cloudflare Workers with Ploy Start framework.

Ploy Start

Ploy Start is a type-safe framework for building Cloudflare Workers with automatic type inference, Zod validation, and built-in support for databases, queues, and workflows.

Features

  • Type-Safe Routing - Full TypeScript inference for params, query, body, and response
  • Zod Validation - Automatic request/response validation with Zod schemas
  • Drizzle ORM - Built-in database integration with Drizzle
  • Queue Handlers - Type-safe message queue processing
  • Workflow Handlers - Durable workflow execution with step functions
  • OpenAPI Generation - Auto-generated API documentation
  • Built-in Middleware - CORS, logging, auth, and rate limiting

Installation

pnpm add @meetploy/start
pnpm add -D drizzle-orm

Minimal Example

The simplest Ploy Start worker:

src/index.ts
import { ploy, z } from "@meetploy/start";

const worker = ploy()
	.get(
		"/",
		{
			response: z.object({ message: z.string() }),
		},
		async () => {
			return { message: "Hello from Ploy Start!" };
		},
	)
	.build();

export default worker;

That's it! The ploy() function creates a builder, you chain route definitions, and .build() generates the worker export.

Project Setup

Directory Structure

my-worker/
├── src/
│   ├── index.ts      # Worker entry point
│   └── env.d.ts      # Environment types
├── package.json
├── tsconfig.json
├── wrangler.json
└── ploy.yaml

package.json

package.json
{
	"name": "my-worker",
	"version": "0.0.0",
	"private": true,
	"type": "module",
	"main": "src/index.ts",
	"scripts": {
		"build": "tsc --noEmit && wrangler build",
		"types": "ploy types"
	},
	"dependencies": {
		"@meetploy/start": "latest",
		"@meetploy/types": "latest"
	},
	"devDependencies": {
		"typescript": "^5.9.0",
		"wrangler": "^4.0.0"
	}
}

ploy.yaml

ploy.yaml
kind: worker
build: pnpm build
out: dist

Environment Types

Generate environment types with the Ploy CLI:

pnpm types

This creates env.d.ts with your bindings typed.

Complete Example

Here's a full example with all features:

src/index.ts
import { ploy, withDrizzle, cors, logger, z } from "@meetploy/start";
import * as schema from "./schema.js";

const worker = ploy<PloyEnv>()
	// Middleware
	.use(cors())
	.use(logger())

	// Database state
	.state(withDrizzle("DB", schema))

	// OpenAPI docs
	.openapi({
		path: "/docs",
		info: { title: "My API", version: "1.0.0" },
	})

	// Routes
	.get(
		"/",
		{
			response: z.object({ message: z.string() }),
		},
		async () => {
			return { message: "Hello!" };
		},
	)

	.get(
		"/users",
		{
			response: z.object({
				users: z.array(
					z.object({
						id: z.number(),
						name: z.string(),
					}),
				),
			}),
		},
		async (ctx) => {
			const users = await ctx.state.db.select().from(schema.users);
			return { users };
		},
	)

	.build();

export default worker;

Method Chaining

Ploy Start uses method chaining with TypeScript generics to accumulate types. Each method returns a new typed builder:

const worker = ploy<PloyEnv>()
  .state(withDrizzle("DB", schema))  // Adds db to state
  .use(cors())                        // Adds middleware
  .get("/users", {...}, handler)      // Adds GET /users route
  .post("/users", {...}, handler)     // Adds POST /users route
  .queue("TASKS", {...}, handler)     // Adds queue handler
  .workflow("flow", {...}, handler)   // Adds workflow handler
  .build();                           // Returns { fetch, message?, workflows? }

The final .build() call returns a PloyHandler<PloyEnv> compatible object with:

  • fetch - HTTP request handler
  • message - Queue message handler (if queues defined)
  • workflows - Workflow handlers (if workflows defined)

What's Next

  • Routes - Define HTTP routes with validation
  • Database - Integrate Drizzle ORM with databases
  • Queues - Handle queue messages
  • Workflows - Build durable workflows

How is this guide?

Last updated on