Basic Example
A simple example demonstrating core c4c features.
Overview
The basic example shows:
- Simple procedure definitions
- Auto-naming and explicit naming
- Workflow creation
- CLI execution
- Generated client usage
Location
examples/basic/
├── procedures/
│ ├── math.ts # Math operations
│ ├── data.ts # Data manipulation
│ ├── auto-naming-demo.ts
│ └── explicit-naming-demo.ts
├── workflows/
│ ├── math.ts # Math workflow
│ └── long.ts # Long-running workflow
├── package.json
└── tsconfig.jsonMath Procedures
Simple arithmetic operations:
typescript
// examples/basic/procedures/math.ts
import { z } from "zod";
import type { Procedure } from "@c4c/core";
export const add: Procedure = {
contract: {
input: z.object({
a: z.number(),
b: z.number(),
}),
output: z.object({
result: z.number(),
}),
},
handler: async (input) => {
return { result: input.a + input.b };
},
};
export const multiply: Procedure = {
contract: {
input: z.object({
a: z.number(),
b: z.number(),
}),
output: z.object({
result: z.number(),
}),
},
handler: async (input) => {
return { result: input.a * input.b };
},
};
export const subtract: Procedure = {
contract: {
input: z.object({
a: z.number(),
b: z.number(),
}),
output: z.object({
result: z.number(),
}),
},
handler: async (input) => {
return { result: input.a - input.b };
},
};Math Workflow
Orchestrate math operations:
typescript
// examples/basic/workflows/math.ts
import { workflow, step } from "@c4c/workflow";
import { z } from "zod";
export default workflow("calculator")
.name("Calculator Workflow")
.version("1.0.0")
.step(step({
id: "multiply",
input: z.object({ a: z.number(), b: z.number() }),
output: z.object({ result: z.number() }),
execute: ({ engine, inputData }) =>
engine.run("multiply", inputData),
}))
.step(step({
id: "add-ten",
input: z.object({ result: z.number() }),
output: z.object({ result: z.number() }),
execute: ({ context }) => {
const multiplyResult = context.get("multiply");
return { result: multiplyResult.result + 10 };
},
}))
.commit();Auto-Naming Example
Using export names as procedure names:
typescript
// examples/basic/procedures/auto-naming-demo.ts
import { z } from "zod";
import type { Procedure } from "@c4c/core";
// Auto-named as "createUser"
export const createUser: Procedure = {
contract: {
input: z.object({
name: z.string(),
email: z.string().email(),
}),
output: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}),
},
handler: async (input) => {
return {
id: crypto.randomUUID(),
...input,
};
},
};
// Auto-named as "getUser"
export const getUser: Procedure = {
contract: {
input: z.object({ id: z.string() }),
output: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}),
},
handler: async (input) => {
return {
id: input.id,
name: "John Doe",
email: "john@example.com",
};
},
};Explicit Naming Example
Using explicit procedure names:
typescript
// examples/basic/procedures/explicit-naming-demo.ts
import { z } from "zod";
import type { Procedure } from "@c4c/core";
export const create: Procedure = {
contract: {
name: "products.create", // Explicit name
input: z.object({
name: z.string(),
price: z.number(),
}),
output: z.object({
id: z.string(),
name: z.string(),
price: z.number(),
}),
},
handler: async (input) => {
return {
id: crypto.randomUUID(),
...input,
};
},
};
export const list: Procedure = {
contract: {
name: "products.list", // Explicit name
input: z.object({
limit: z.number().optional(),
}),
output: z.object({
products: z.array(z.object({
id: z.string(),
name: z.string(),
price: z.number(),
})),
}),
},
handler: async (input) => {
return {
products: [
{ id: "1", name: "Product 1", price: 10 },
{ id: "2", name: "Product 2", price: 20 },
].slice(0, input.limit || 10),
};
},
};Running the Example
Install Dependencies
bash
cd examples/basic
pnpm installStart Dev Server
bash
pnpm devServer starts on http://localhost:3000
Execute Procedures
bash
# Math operations
pnpm c4c exec add --input '{"a": 5, "b": 3}'
# Result: { "result": 8 }
pnpm c4c exec multiply --input '{"a": 5, "b": 3}'
# Result: { "result": 15 }
# User operations
pnpm c4c exec createUser --input '{"name":"Alice","email":"alice@example.com"}'
# Result: { "id": "...", "name": "Alice", "email": "alice@example.com" }
# Product operations
pnpm c4c exec products.create --input '{"name":"Widget","price":9.99}'
# Result: { "id": "...", "name": "Widget", "price": 9.99 }Execute Workflows
bash
pnpm c4c exec calculator --input '{"a": 5, "b": 3}'
# Result: { "result": 25 } (5 * 3 + 10)Generate Client
bash
pnpm c4c generate client --out ./client.tsUse the generated client:
typescript
import { createClient } from "./client";
const client = createClient({
baseUrl: "http://localhost:3000"
});
// Fully typed!
const result = await client.add({ a: 5, b: 3 });
console.log(result.result); // 8
const user = await client.createUser({
name: "Alice",
email: "alice@example.com"
});
console.log(user.id);Generate OpenAPI
bash
pnpm c4c generate openapi --out ./openapi.jsonView the generated OpenAPI spec:
bash
cat openapi.jsonHTTP API
With the dev server running:
RPC Endpoints
bash
# Add numbers
curl -X POST http://localhost:3000/rpc/add \
-H "Content-Type: application/json" \
-d '{"a": 5, "b": 3}'
# Create user
curl -X POST http://localhost:3000/rpc/createUser \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'List Procedures
bash
curl http://localhost:3000/proceduresOpenAPI Spec
bash
curl http://localhost:3000/openapi.jsonKey Takeaways
- Simple Setup - Minimal configuration required
- Type Safety - Full TypeScript support
- Auto-Naming - Optional names with IDE refactoring
- Multiple Transports - CLI, HTTP, workflows
- Generated Clients - Type-safe API clients
Next Steps
- Modules Example - Organized structure
- Integrations Example - External APIs
- Cross-Integration Example - Multi-app integration