Quick Start Guide
Welcome to Sylk's Quick Start Guide! Our powerful platform provides developers with a flexible and customizable approach to building microservices. In this tutorial, we'll guide you through creating a simple todo application using the Sylk CLI. Whether you're new to microservices or a seasoned developer, our guide will help you get started quickly and easily. So, let's get started and see how Sylk can simplify the process of building robust and scalable systems.
Before installing sylk project, the following requirements must be installed on your computer:
The following are optionals (depend on your use case):
🚀 Part A: Create a New Project with Sylk
Step 1:Install sylk CLI
First, you'll need to install the Sylk CLI. You can do this by running the following command in your terminal:
- pip
pip install sylk
Step 2: Start a New Sylk Project
After the installation is complete, you can start building your first service with Sylk. Start a new project using the installed Sylk CLI.
- Blank
- Python Server
- Typescript Server
- Go Server
sylk new todo-app
sylk new todo-app-py --server python --clients python
sylk new todo-app-ts --server typescript --clients typescript
sylk new todo-app-go --server go --clients go
If your are using Go
as one of the project languages (Client or Server) -
You will need to enter the project package name in Go
format e.g github.com/<your-user-name>
- Enter the domain name of you choice
- Choose a path for your project root directory
You have successfully created a new sylk project. Now, you can start exploring the Sylk CLI and discover the product on your own using our Commands Guide, or proceed to part B.
🛠 Part B: Building your API
Congratulations on setting up your first Sylk project! Now, let's focus on building our first simple todo service that will serve a todo list.
Step 1: Create the Todos package
Before proceeding, make sure that you are currently located inside the folder of the project generated by the Sylk CLI. To navigate to your project folder, use the terminal command to change the current working directory to the project directory.
cd todo-app
Upon navigating to your project folder, you should see a single file generated by the sylk CLI
called sylk.json
. This file holds all of your project resources, and you can learn more about its structure in the project structure documentation. Alternatively, you can continue with the next steps to see how the project is built.
- Blank
- shortand
- Optionals
sylk generate package Todos.v1
sylk g p Todos.v1
sylk -e generate package Todos.v1
If you chose to create the package without any optional arguments, the CLI prompter will ask you for a few details on your package. Feel free to fill in your own details, but for the purposes of this example, we will keep it simple.
Step 2: Create the Task service
Now that we have created a basic data structure with Task
and TaskId
messages types under Todo
package, let's use them to actually add endpoints to our project.
You can list all times the current snapshots of project resources via the sylk ls
command
- Blank
- shortand
- Optionals
sylk generate service Todos.v1.TaskService
sylk g s TaskService
sylk -e generate service TaskService
Step 3: Create a Service Schema
Create a "Task" message type
Before sylk cli introduced versioning and support multi-pattern for compiled protobuf files, you have to generate a Package
before continuing and creating messages.
After >= 0.3.*
you can create a message directly into Service
(Even without any package under Project
), just chose the service full name for the new message when the interactive prompter will ask you 😏
Our main entity for a simple "Todo" application is the "Task".
Since your todo service
handles a task list, you need to describe the fields to display when adding a new entry:
- Blank
- shortand
- Optionals
sylk generate message Todos.v1.Task
sylk g m Task
sylk -e generate message Todos.v1.Task
Your task
service will eventually include many messages, both inputs and outputs. To keep them organized, we need to create a collection of closely related messages, which we'll call a "package".
You can use the shortand version of the sylk generate command by running sylk g, and you can also use a shortcut for the resource you're creating. For creating a package, the shortcut is sylk g p
.
Give your message a name: Task.
Add fields to
Task
:
a. Add field by the name: id, of typestring
.b. Add field by the name: title, of type
string
.c. Add field by the name: description, of type
string
.d. Add field by the name: done, of type
boolean
.
Create a "TaskId" message type
You should think ahead when it comes to API's building and planning is crucial task. It is a good practice to have 2 unique messages for each endopoints your services will serve. One for input and the other will serve as output for the method.
For the simplicity if this tutorial we will create one more message for querying a task by the id
field, for that we will need a simple "util" message:
- Blank
- shortand
- Optionals
sylk generate message Todos.v1.TaskId
sylk g m TaskId
sylk -e generate message Todos.v1.TaskId
- Add name to your message: TaskId
- Add field of type
string
and give it a name: id
Step 3: Create a Method
Now we have our service schema ready to be consumed and published we need to create our service methods definitions (Endpoints).
We will follow a simple CRUD
api design pattern, our service needs to be able serving our Task
entity details.
For that simple use case we will create a simple GET
RPC.
Follow the next steps:
- Enter service name: Task
For older sylk version you need to inject a package into the service before creating new RPC (Method)
- Create new method (RPC - Remote procedure call)
- Blank
- shortand
- Optionals
sylk generate rpc Todos.v1.TaskService.GetTask
sylk g r GetTask
sylk -e generate rpc Todos.v1.TaskService.GetTask
- Enter endpoint name: GetTask
- Chose the endpoint type: Unary
- Chose the endpoint input type: TaskId
- Chose the endpoint output type: Task
The Task service is now ready to be implemented with a single simple "GetTask" endpoint.
Any method can be of 4 available types - Unary, Client-stream, Server-stream and Bi-directional stream.
Learn more about Methods
The sylk CLI allow you to edit at any time your project resources you have just created with sylk edit
command
Step 4: Build Project Resources
We have just added a service that serves 1 endpoint which get 2 messages as it's I/O. We now have enough resources to build our project.
sylk build
After few secondes you should see the project folder tree gets full with new files and sub-folders. You can learn more on project structure or continue and start focus on what your service does best, serve a todo's task list.
Step 5: Write Some Code
Lets deep dive into the service implementation file which should be visible under services
folder on root project directory.
- Python Server
- Typescript Server
- Go Server
Navigate to
./services/TaskService/v1/TaskService.py
And you can copy the code below and replace the whole content of the service file:
Make sure you replace the <replace-with-your-domain>
with the real domain you used to create the project with
"""sylk.build service implemantation for -> TaskService"""
import grpc
from google.protobuf.timestamp_pb2 import Timestamp
from typing import Iterator
from services.protos.<replace-with-your-domain>.Todos.v1 import (
Todos_pb2_grpc,
Todos_pb2
)
class TaskService(Todos_pb2_grpc.TaskServiceServicer):
def __init__(self) -> None:
self.tasks = [
Todos_pb2.Task(
id='1',
title='Build App',
description='Create a distributed system with sylk.build',
done=True,
),
Todos_pb2.Task(
id='2',
title='Implement AI Chatbot',
description='Build an AI-powered chatbot using Python and TensorFlow',
done=False,
),
Todos_pb2.Task(
id='3',
title='Optimize Database Queries',
description='Improve the performance of database queries for faster application response',
done=False,
),
]
# @rpc @@sylk - DO NOT REMOVE
def GetTask(self, request: Todos_pb2.TaskId, context: grpc.ServicerContext) -> Todos_pb2.Task:
print('[GetTask] Got request for task data: %s' % request.id)
response = next((t for t in self.tasks if t.id == request.id),None)
if response is not None:
return response
else:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details(f'Task by id: "{request.id}" not found.')
return context
Create now a small client script which will instantiate a the auto-generated class fot making connection to our todo-app services
All the generated client code can be found inside root project directory clients/python/
./client.py
from clients.python import TaskService_v1
from clients.python.protos.<replace-with-your-domain>.Todos.v1.Todos_pb2 import TaskId
import grpc
client = TaskService_v1(
client_opt={
'host': 'localhost',
'port': 44880
}
)
try:
task = client.GetTask(TaskId(id='1'))
print(task)
# Custom handling of exceptions
except grpc.RpcError as rpc_error:
if rpc_error.code() == grpc.StatusCode.NOT_FOUND:
print(f"Received RPC error: code={rpc_error.code()} message={rpc_error.details()}")
else:
print(f"Received unknown RPC error: code={rpc_error.code()} message={rpc_error.details()}")
Navigate to
./services/Task.ts
And you can copy the code below and replace the whole content of the service file:
Make sure you replace the <replace-with-your-domain>
with the real domain you used to create the project with
import {
handleUnaryCall,
sendUnaryData,
ServerUnaryCall,
status,
UntypedHandleCall,
} from '@grpc/grpc-js';
import { ServiceError } from '../../utils/error';
import { ApiType } from '../../utils/interfaces';
import * as Todos from '../../protos/sylklabs/Todos/v1/Todos';
import { TaskServiceService } from '../../protos/sylklabs/Todos/v1/Todos';
class TaskService implements Todos.TaskServiceServer, ApiType<UntypedHandleCall> {
[method: string]: any;
tasks: Todos.Task[] = [
Todos.Task.fromPartial({
id: '1',
title: 'Build App',
description: 'Create a distributed system with sylk.build',
done: true
}),
Todos.Task.fromPartial({
id: '2',
title: 'Implement AI Chatbot',
description: 'Build an AI-powered chatbot using Python and TensorFlow',
}),
Todos.Task.fromPartial({
id: '3',
title: 'Optimize Database Queries',
description: 'Improve the performance of database queries for faster application response',
}),
];
// @rpc @@sylk - DO NOT REMOVE
public getTask: handleUnaryCall<Todos.TaskId, Todos.Task> = (
call: ServerUnaryCall<Todos.TaskId, Todos.Task>,
callback: sendUnaryData<Todos.Task>
) => {
const {id } = call.request;
console.log(`[GetTask] Got request for task data: ${id}`);
let task = this.tasks.find(t => t.id === id);
if(task) {
callback(null, task, call.metadata);
} else {
callback(
new ServiceError(
status.NOT_FOUND,
`Task "${id}" is not found.`,
undefined,
call.metadata
),
undefined,
call.metadata
);
};
}
}
export {
TaskService,
TaskServiceService
};
Create now a small client script which will instantiate a the auto-generated class fot making connection to our todo-app services
All the generated client code can be found inside root project directory clients/typescript/
The generated client will be compiled with every run of sylk build
command
./client.ts
import { todoappts } from './clients/typescript';
import { TaskId } from './clients/typescript/protos/Todo';
const client = new todoappts();
client.GetTask(TaskId.fromPartial({
id: '1'
})).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
});
Navigate to
./services/Task/v1/Task/Task.go
And you can copy the code below and replace the whole content of the service file:
Make sure you replace the <replace-with-your-domain>
with the real domain you used to create the project with
package Task
import (
"context"
"fmt"
TaskServicer "github.com/sylk-build/todoapp-go/services/protos/Task"
Todo "github.com/sylk-build/todoapp-go/services/protos/<replace-with-your-domain>/Todo"
"github.com/sylk-build/todoapp-go/services/utils"
"golang.org/x/exp/slices"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
var tasks = []Todo.Task{
{
Id: "1",
Title: "Build awesome services",
Description: "Build manage and deploy micro-services with powerful easy to use sylk.build CLI and cloud platform",
Done: true,
},
}
type Task struct {
TaskServicer.UnimplementedTaskServer
}
// [sylk.build] - Task.GetTask - None
func (TaskServicer *Task) GetTask(ctx context.Context, TaskId *Todo.TaskId) (response *Todo.Task, err error) {
printLog("GetTask", ctx, TaskId)
if idx := slices.IndexFunc(tasks, func(t Todo.Task) bool { return t.Id == TaskId.Id }); idx == -1 {
errMsg := fmt.Sprintf("Task: '%s' not found.", TaskId.Id)
return nil, status.Error(codes.NotFound, errMsg)
} else {
return &tasks[idx], nil
}
}
func printLog(name string, ctx context.Context, message interface{}) {
contextMetadata, _ := metadata.FromIncomingContext(ctx)
utils.InfoLogger.Printf("[%s] Got RPC request: %v", name, message)
utils.DebugLogger.Printf("[%s] Metadata: %v", name, contextMetadata)
}
Create now a small client script which will instantiate a the auto-generated class fot making connection to our todo-app services
All the generated client code can be found inside root project directory clients/go/
package main
import (
"fmt"
client "github.com/sylk-build/todoapp-go/clients/go"
"github.com/sylk-build/todoapp-go/services/protos/<replace-with-your-domain>/Task/v1/Task"
)
func main() {
//Init the client
c := client.Defualt()
// Construct a message
msg := Task.TaskId{Id: "1"}
res, _, _ := c.GetTask(&msg)
// Send unary
fmt.Printf("Got server unary response: %v", res)
}
Step 6: Run The Service
After we have our code implemented let's run our service 🚀
Inside the project root directory run the command:
sylk run
Step 7: Use the API
Lets run the client script we added to our project earlier Open a new terminal window (Best side by side view), and run the following command:
- Python Client
- Typescript Client
- Go Client
python3 client.py
Client Response
id: "1"
title: "Build App"
description: "Create a distributed system with sylk.build"
done: true
Server Response
[*] Started sylk.build server at -> 0.0.0.0:44880
[GetTask] Got request for task data: 1
tsc test/client.ts
node test/client.js
Client Response
{
id: '1',
title: 'Build App',
description: 'Create a distributed system with sylk.build',
done: true
}
Server Response
[sylk.build] Starting gRPC:server:44880 at -> 4/25/2023, 7:03:53 PM)
[GetTask] Got request for task data: 1
go run tests/client.go
Client Response
20:46:16 channel.go:105: Target to connect = localhost:44880
20:46:16 main.go:99: Calling GetTask id:"1"
Got server unary response: id:"1" title:"Build awesome services" description:"Build manage and deploy micro-services with powerful easy to use sylk.build CLI and cloud platform" done:true%
Server Response
2023/04/25 20:46:16 [sylk.build] Starting server (Go) at -> 44880
INFO: 2023/04/25 20:46:16 Task.go:42: [GetTask] Got RPC request: id:"1"
Now your service is up and running now you can continue and scale it with more resources and methods or to deploy it
You can always check for the code reference for this project and others at our sylk-examples
github repository.
⏩ What to do next?
Now that you know the basics of creating and publishing content with sylk, we encourage you to explore and dig deeper into some sylk features:
- 👀 Explore all of sylk samples in Github sylk-samples
- 👉 learn how to use sylk CLI Commands
- 👉 learn on SBU's