Quick Start
Get started with ZAP Protocol in 5 minutes
Quick Start
This guide will help you build your first ZAP service in under 5 minutes.
Prerequisites
- Go 1.21+ or Rust 1.75+ or Node.js 20+
- ZAP CLI (
zap)
Installation
# Install the ZAP CLI
go install github.com/zap-protocol/zap/cmd/zap@latest# Add to Cargo.toml
cargo add zap-protocol
# Install the code generator
cargo install zap-codegen# Install packages
npm install @zap-protocol/client @zap-protocol/codegen
# Or with pnpm
pnpm add @zap-protocol/client @zap-protocol/codegenDefine Your Schema
Create a file named calculator.zap (or calculator.capnp for legacy Cap'n Proto format):
calculator.zap
# ZAP schema - no file ID needed, no ordinals
interface Calculator
# Basic arithmetic operations
add (a Float64, b Float64) -> (result Float64)
subtract (a Float64, b Float64) -> (result Float64)
multiply (a Float64, b Float64) -> (result Float64)
divide (a Float64, b Float64) -> (result Float64)
# Batch operations with streaming
sum (numbers List(Float64)) -> (total Float64)Generate Code
# Generate Go code
zap generate calculator.zap --lang=go --out=./gen
# Generate Rust code
zap generate calculator.zap --lang=rust --out=./gen
# Generate TypeScript code
zap generate calculator.zap --lang=ts --out=./genImplement the Server
package main
import (
"context"
"errors"
"log"
"github.com/zap-protocol/zap"
"yourmodule/gen"
)
type calculatorServer struct{}
func (s *calculatorServer) Add(ctx context.Context, a, b float64) (float64, error) {
return a + b, nil
}
func (s *calculatorServer) Subtract(ctx context.Context, a, b float64) (float64, error) {
return a - b, nil
}
func (s *calculatorServer) Multiply(ctx context.Context, a, b float64) (float64, error) {
return a * b, nil
}
func (s *calculatorServer) Divide(ctx context.Context, a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func (s *calculatorServer) Sum(ctx context.Context, numbers []float64) (float64, error) {
var total float64
for _, n := range numbers {
total += n
}
return total, nil
}
func main() {
server := zap.NewServer(
zap.WithAddress(":9000"),
zap.WithLogger(log.Default()),
)
gen.RegisterCalculatorServer(server, &calculatorServer{})
log.Println("Server listening on :9000")
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}use zap_protocol::{Server, Result};
use gen::calculator::{Calculator, CalculatorServer};
struct CalculatorImpl;
#[async_trait::async_trait]
impl Calculator for CalculatorImpl {
async fn add(&self, a: f64, b: f64) -> Result<f64> {
Ok(a + b)
}
async fn subtract(&self, a: f64, b: f64) -> Result<f64> {
Ok(a - b)
}
async fn multiply(&self, a: f64, b: f64) -> Result<f64> {
Ok(a * b)
}
async fn divide(&self, a: f64, b: f64) -> Result<f64> {
if b == 0.0 {
return Err(zap_protocol::Error::new("division by zero"));
}
Ok(a / b)
}
async fn sum(&self, numbers: Vec<f64>) -> Result<f64> {
Ok(numbers.iter().sum())
}
}
#[tokio::main]
async fn main() -> Result<()> {
let server = Server::builder()
.register(CalculatorServer::new(CalculatorImpl))
.bind("[::]:9000")
.await?;
println!("Server listening on :9000");
server.serve().await
}import { createServer } from '@zap-protocol/server';
import { Calculator } from './gen/calculator';
const calculator: Calculator = {
async add(a: number, b: number): Promise<number> {
return a + b;
},
async subtract(a: number, b: number): Promise<number> {
return a - b;
},
async multiply(a: number, b: number): Promise<number> {
return a * b;
},
async divide(a: number, b: number): Promise<number> {
if (b === 0) {
throw new Error('division by zero');
}
return a / b;
},
async sum(numbers: number[]): Promise<number> {
return numbers.reduce((acc, n) => acc + n, 0);
},
};
const server = createServer({ port: 9000 });
server.register('Calculator', calculator);
console.log('Server listening on :9000');
server.listen();Create a Client
package main
import (
"context"
"fmt"
"log"
"github.com/zap-protocol/zap"
"yourmodule/gen"
)
func main() {
client, err := zap.Dial("localhost:9000")
if err != nil {
log.Fatal(err)
}
defer client.Close()
calc := gen.NewCalculatorClient(client)
ctx := context.Background()
result, err := calc.Add(ctx, 10, 5)
if err != nil {
log.Fatal(err)
}
fmt.Printf("10 + 5 = %.0f\n", result)
result, err = calc.Multiply(ctx, 6, 7)
if err != nil {
log.Fatal(err)
}
fmt.Printf("6 * 7 = %.0f\n", result)
total, err := calc.Sum(ctx, []float64{1, 2, 3, 4, 5})
if err != nil {
log.Fatal(err)
}
fmt.Printf("sum(1..5) = %.0f\n", total)
}use gen::calculator::CalculatorClient;
use zap_protocol::Client;
#[tokio::main]
async fn main() -> zap_protocol::Result<()> {
let client = Client::connect("localhost:9000").await?;
let calc = CalculatorClient::new(client);
let result = calc.add(10.0, 5.0).await?;
println!("10 + 5 = {}", result);
let result = calc.multiply(6.0, 7.0).await?;
println!("6 * 7 = {}", result);
let total = calc.sum(vec![1.0, 2.0, 3.0, 4.0, 5.0]).await?;
println!("sum(1..5) = {}", total);
Ok(())
}import { createClient } from '@zap-protocol/client';
import { CalculatorClient } from './gen/calculator';
async function main() {
const client = await createClient('localhost:9000');
const calc = new CalculatorClient(client);
const sum = await calc.add(10, 5);
console.log(`10 + 5 = ${sum}`);
const product = await calc.multiply(6, 7);
console.log(`6 * 7 = ${product}`);
const total = await calc.sum([1, 2, 3, 4, 5]);
console.log(`sum(1..5) = ${total}`);
client.close();
}
main();Run Your Service
# Terminal 1: Start the server
go run server.go
# Output: Server listening on :9000
# Terminal 2: Run the client
go run client.go
# Output:
# 10 + 5 = 15
# 6 * 7 = 42
# sum(1..5) = 15Next Steps
Now that you have a working ZAP service:
- Learn about Architecture to understand the design
- Explore Transports for different network options
- Set up a Gateway for HTTP clients
- Add Consensus for distributed deployments