CLI

Setup

Install

npm install @syncano/client --save

or

<script src="https://unpkg.com/syncano-client"></script>

Basic connection

const s = new SyncanoClient(instanceName)

Connect with options

const s = new SyncanoClient('MY_INSTANCE_NAME', {
  token: 'USER_TOKEN'
})

Authentication

Set user token

s.setToken(token)

Remove user token

s.setToken()

Requests

API

s(endpoint, data?, options?)

// HTTP Request method (s.post, s.get, etc.) declaration
// adds '{ "_method": "HTTP Method" }' param in the
// request to be handled by the Syncano Server lib
s.post(endpoint, data?, options?)
s.get(endpoint, data?, options?)
s.delete(endpoint, data?, options?)
s.put(endpoint, data?, options?)
s.patch(endpoint, data?, options?)

Call socket endpoint

// Send POST request
s('countries/list', {sort_by: 'population'})
  .then(country => {
    console.log(country.name)
  })

// Send GET request
s.get('countries/get', {name: 'Poland'})

Custom headers

s.get('countries/list', {}, {
  headers: {
    'Content-Type': 'application/json'
  }
})

Cache

endpoints:
  list:
    cache: 30
// skip cache set in the socket.yml
s.get('countries/list', {__skip_cache: 1})

Realtime

API

// Subscribe for continious changes
s.subscribe(endpoint, data?, callback)

// Subscribe only for first change
s.subscribe.once(endpoint, data?, callback)

Listen for changes on public channel

endpoints:
  messages:
    channel: messages
import {channel} from '@syncano/core'
channel.publish(`messages`, message)
s.subscribe('chat/messages', res => {})

Listen for changes on channel room

endpoints:
  messages:
    channel: messages.{room}
channel.publish(`messages.${room}`, message)
s.subscribe(endpoint, {room: 1}, callback)

Listen for changes on user private channel

endpoints:
  messages:
    channel: messages.{user}
channel.publish(`messages.${username}`, message)
s.subscribe(endpoint, {user_key: ''}, callback)

Unsubscribe from channel changes

const listener = s.subscribe(endpoint, data)
listener.stop()

Setup

Install

npm install @syncano/core --save

Usage

import Syncano from ‘@syncano/core’

export default (ctx) => {
  const {data} = new Syncano(ctx)

  data.posts
    .create({title: 'Lorem ipsum'})
    .then(post => {
      console.log(post.title)
    })
}

Response

Send response from socket.

Import

const {response} = new Syncano(ctx)

API

response(content, status?, mimetype, headers?)
response.header(key, value)
response.json(content, status?)

Basic response

response('Hello world')
response('Hello world', 200, 'text/plain', {
  'X-RATE-LIMIT': 50
})

Custom headers

response
  .header('X-RATE-LIMIT', 50)
  .header('X-USAGE', 35)
  ('Check headers', 200, 'plain/text')

JSON response

response.json({title: "Post title"})
response
  .header('X-RATE-LIMIT', 50)
  .json({title: "Post title"})

Logger

console.log for your sockets.

Import

const {logger} = new Syncano(ctx)

Usage

const log = logger('Socket scope')

log.debug('This is debug message!')
log.error('This is error message!')
log.warn('This is warning message!')
log.info('This is info message!', {hello: "world"})

Listen for events

logger.listen(event => {
  // Handle event - save to db or send email
})

Events

Publish global events to which other sockets can subscribe.

Import

const {event} = new Syncano(ctx)

Emiting event

event.emit('email_sent', {to: '[email protected]'})

Catching event

event_handlers:
  events.socket name.email_sent:
    file: email-sent.js

Groups

Syncano groups

Import

const {groups} = new Syncano(ctx)

Groups management


// Create group
await groups.create(name, description)

//Delete group
await groups.delete(name)

//Groups list
await groups.list()

//Get group with users list
await groups.get(name)
      

Users management in groups


//Get group with users list
await groups.get(name)

//Get user groups
await groups.user(userId)

//Add user to group
await groups.addUser(name, userId)

//Remove user from group
await groups.removeUser(name, userId)
      

Sockets

Call other socket enpoints.

Import

const {socket} = new Syncano(ctx)

Usage

socket.get('tags/list', {sort: 'latest'})
socket.post('tags/create', {name: 'nature'})
socket.delete('tags/delete', {id: 1})

Channels

Send messages to realtime channels.

Import

const {channel} = new Syncano(ctx)

Publishing to public channel

endpoints:
  messages:
    channel: messages
channel.publish('messages', {content: 'hello'})

Publishing to channel room

endpoints:
  messages:
    channel: messages.{room}
channel.publish(`messages.${room}`, {content: 'hello'})

Publishing to user private channel

endpoints:
  messages:
    channel: messages.{user}
channel.publish(`messages.${username}`, {content: 'hello'})

Data & Users

Create, update, delete, list users and class object.

Import

const {data, users} = new Syncano(ctx)

API

Users and Data share all methods.

users.method()
  .then(res => {})
  .catch(err => {})

data.<class_name>.method()
  .then(res => {})
  .catch(err => {})

Example "book" data class

classes:
  book:
    - name: title
      type: string
      filter_index: true
    - name: author
      type: reference
      target: user
    - name: pages
      type: integer
    - name: publish_date
      type: datetime
    - name: categories
      type: relation
      target: category
    - name: discount
      type: float
    author:
    - name: firstname
      type: string
    - name: lastname
      type: string
    category:
    - name: name
      type: string

Built-in fields: id, revision, created_at, updated_at

Creating

// Create single book
await data.book.create({title: 'Godfather'})

// Create multiple books
await data.book.create([
  {title: 'Book 1'},
  {title: 'Book 2'}
])

Updating

// Update all objects
await data.book.update({title: 'Lorem ipsum 1'})

// Update single object with given ID
await data.book.update(14, {title: 'Lorem ipsum 1'})

// Update multiple objects
await data.book.update([
  14, {title: 'Lorem ipsum 1'},
  15, {title: 'Lorem ipsum 1'}
])

// Update all objects matching query
await data.book
  .where('categories', 'in', ['horror', 'thriller'])
  .update({discount: 20})

Other Creation Methods

// Get first book with title 'Godfather' or create it
// Second param holds additional data assigned to object
await data.book.firstOrCreate({title: 'Godfather'}, {created_by: 1})
// Get first book with name 'Godfather' or create it
// Second param holds additional data assigned to object
await data.book.updateOrCreate({title: 'Godfather'}, {created_by: 1})

Deleting Class Object

// Delete all posts
await data.book.delete()

// Delete signle book with given ID
await data.book.delete(ctx.args.id)

// Delete multiple books
await data.book.delete([55, 56, 57])

// Delete all books where author id is equal to 15
await data.book.where('author', 15).delete()

Retrieving Objects

// Get all objects from class
await data.book.list()

// Get all objects with author equal Mario Puzo
await data.book.where('author', 'Mario Puzo').list()

// Get object by ID
await data.book.find(1)

// Get first object matching query
await data.book.where('category', 'Crime').first()

// Get object with given IDs
await data.book.find([4, 5, 10])

// Get object with given ID or throw error
await data.book.findOrFail(15)

// Get first object with pages > 100 or throw error
await data.book.where('pages', '>', 100).firstOrFail())

Retrieving Related Objects

// Categories column ids will be replaced with objects from targeted class (category)
await data.book.with('categories').list()

Filtering

// Get all books with pages = 500
await data.book.where('pages', '=', 500).list()

// You can ommit second param to simply check if value is equal
await data.book.where('pages', 500).list()

// Get all books with pages greater than 100
// Other selectors: <, <=, >, >=, =, !=
await data.book.where('pages', '>', 100).list()

// Get all books with one of the statuses
await data.book.where('status', 'in', ['draft', 'published']).list()
await data.book.whereIn('status', ['draft', 'published']).list()

// Get all books with id not in given array
await data.book.where('id', 'nin', [10, 20]).list()
await data.book.whereNotIn('id', [10, 20]).list()

// Get all books with title
await data.book.whereNotNull('title').list()

// Get all books without title
await data.book.whereNull('title')

// Get all books with pages between 200 and 400
await data.book.whereBetween('pages', 200, 400)

// Get all published books with id between 100 and 200
await data.book
  .where([
    ['id', '>', 100],
    ['id', '<', 200]
    ['status', 'published']
  ]).list()

// Get books with number of pages lower than 100 or higher than 300
await data.book
  .where('pages', '<', 100)
  .orWhere('pages', '>', 300)
  .list()

'icontains' operator works like %test% in sql:
.where('title', 'icontains', 'test')

Ordering, Limit, & Offset

// Get all books ordered descending by creation date
await data.book.orderBy('created_at', 'DESC').list()

// Get all books ordered ascending by creation date
await data.book.orderBy('created_at', 'ASC').list()
// Skip 5 books and then get next 10 books
await data.book.skip(5).take(10).list()

Helper methods

// Get only selected columns
await data.book
  .fields('id', 'title', 'user.username as author')
  .list()

// Get values of given column
await data.book.pluck('title')

// Get single object key
await data.book.where('id', 10).value('title')

// Get number of objects matching query
await data.book.where('id', '>', 10).count()

Basics

Install


npm install @syncano/cli

s is alias for syncano-cli

Output usage information

npx s --help

Output the version number

npx s --version

Initiate project

npx s init

# init with given instance nam
s init -i 

Login to/register Syncano account

npx s login

Logout (delete account keys from this computer)

npx s logout

Attach project to the chosen Instance

npx s attach --instance my_instance_name

Deploy

Deploy whole project

npx s deploy

Deploy whole project (with instance creation)

s deploy --create-instance <new instance name>

Deploy whole project

npx s deploy <socket name>

🔥 Hot deploy (deploy every change in the source code right away)

npx s hot 

🔥 Hot deploy chooses Socket

npx s hot <socket name>

Call Socket endpoint:

npx s call <socket name>/<endpoint>

Traces

Trace all Socket calls

npx s trace

Trace chosen Socket calls

npx s trace <socket name>

Sockets

List Sockets

npx s list

Socket details

npx s list <socket name>

Adding socket from the Sockets Registry (last version)

npx s add <socket name>

Remove Socket

npx s remove <socket name>

Create new Socket

npx s create <socket name>

Full configuration of given Socket

config:
  API_KEY:
    description: Slack API key
npx s config <socket name>

Show configuration of given socket

npx s config-show <socket name>

Change single configuration option

npx s config-set <socket name> <option name> <value>

Hosting

Add new hosting

npx s hosting add <local path>

Delete hosting

npx s hosting delete <hosting name>

List all hostinngs

npx s hosting list

List files of given hosting

npx s hosting files <hosting name>

Synchronize files of given hosting

npx s hosting sync <hosting name>

Configure hosting options

npx s hosting config <hosting name> --cname <domain name>
s hosting config <hosting name> --remove-cname <domain name>

Backups

Create new backup

npx s backups create

Delete backup

npx s backups delete <id>

Delete all backups

npx s backups delete all

Backups list

npx s backups list

Last backup

npx s backups last

Basics

Create socket

npx syncano-cli create <socket-name>
# This will create socket configuration at:
# ./syncano/socket-name/socket.yml

Basic definition

name: api # Socket name
description: My project API
version: 0.0.1

Endpoints

Define endpoints

endpoints:
  get-books:
    file: get-books.js # run src/get-books.js
  add-book:
    # If you don't pass file, src/SOCKET-NAME.js will be run
    parameters:
      title:
        type: string
      author:
        type: integer
    # cache a GET request for 5 seconds
    cache: 5
    # restrict access to admin only
    private: true

Define real-time endpoints

To learn about Channels, view Server Cheatsheet

Event Handlers

Define Event Handlers

event_handlers:
  data.cars.create:
    file: ./event_handlers/data.cars.create.js

Event Handler Types

data.<class>.<create/update/delete>
built-in events for classes
schedule.interval.5_minutes
built-in events for interval script runs
schedule.crontab.*/5 * * * *
built-in events for scheduled script runs with cron
events.<name of custom event>
user-defined event

Config

Define config

config:
  <config option name>:
    description: <config option short description>
    long_description: <config option long description>
    required: <true|false>

Example config

config:
  API_KEY:
    description: OpenWeatherMap API key
    long_description: |
      To find the API key, go to your OpenWeatherMap account and copy it from
      API keys section at (https://home.openweathermap.org/api_keys).
    required: true

Data Classes

Define class

classes:
  book:
    - name: book_title
      type: string
    - name: author
      type: reference
      target: user
    - name: publish_date
      type: datetime

Schema field types

string
text field limited to 128 chars
text
text field limited to 32000 chars
integer
number field
float
floating point field
boolean
boolean field
datetime
date in UTC format
file
binary data, max 128MB
reference
reference to single Data Object(ID)
book:
  - name: author
    type: reference
    target: user
data.book.create({author: 2})
relation
array of references
book:
  - name: tags
    type: relation
    target: tag
data.book.create({tags: [3, 5]})
array
array field of string types, int, boolean and float
object
object field - JSON like
geopoint
geographic coordinates field

Schema field indexes

filter_index
allow to filter by given field
book:
  - name: category
    type: string
    filter_index: true
data.books
  .where('category', 'horror')
  .list()
order_index
allow to order by given field
book:
  - name: total_pages
    type: integer
    order_index: true
data.books
  .orderBy('total_pages')
  .list()