ZetaPush management
Manage ZetaPush account
The ZetaPush account gives you access to the ZetaPush cloud. As a developer, you need an account to be authorized to develop your product. As a manager, you need an account to organize your teams.
Create your account
Currently, the only way to get a ZetaPush account is by contacting us. The aim is to understand your needs and to advise you even if you just want to test it:
Your account will be created soon after the first contact and an application that fits your needs will be available. Then you are ready to start working with ZetaPush.
Soon we will open access to everyone for free trial. Account creation will be available through our CLI tool, from the main site and the web console. |
Manage your profile
The management of your ZetaPush account and profile will soon be available on the web console. Now you can contact us if you want us to manage your informations.
Manage your organization
An organization is a concept to let you work in team.
The organization commonly groups all ZetaPush accounts of your company. You can have several applications for your organization and you can manage access rights on applications according to members of your team.
What is an application
An application is a logical container designed to perform a group of coordinated tasks or activities for the benefit of the user. |
For example, if you are in the company named "MyCompany" which has two applications named "MyApplication1" and "MyApplication2" :
-
One organization is created with the name "MyCompany"
-
Two applications are created and named "MyApplication1" and "MyApplication2"
The ZetaPush administrator account of this organization can access to these two applications. He can also change the access rights by application.

In the above example, we have one organization which contains two separate applications. Each application has 3 users. The users of one application cannot work on the other application.
Manage access rights from web console
The access rights are managed by ZetaPush for now in order to fit your needs. Thereafter, you will be able to manage them via the web console. |
Manage applications
An application is a logical container designed to perform a group of coordinated tasks or activities for the benefit of the user.
Add an application
There are several ways to create a new application :
-
Via the web console (not available yet)
-
Via the CLI (not available yet)
Sign up for a free trial
When you ask for a free trial, a ZetaPush account and an application are provided to you.
You can ask for a free trial via our web site.
Get free trial
Now, when you asking for a free trial, the ZetaPush account and the application are created by ZetaPush to fit your needs. Soon we will open access to everyone for get a free trial of 30 days. In this case the account and the application will be automatically created. |
Access control per application
As seen previously, an organization can have several teams or/and several applications. So you may want to control who can work on which application.
Grant access rights
Contact us to manage access rights
Security per application is not currently available to our clients. Instead we prefer that you contact us to understand your needs and to guide you how to organize your teams and application. |
Remove access rights
Contact us to remove access rights
Security per application is not currently available to our clients. Instead we prefer that you contact us to understand your needs and to guide you how to organize your teams and application. |
Manage environments
An application is a logical container designed to perform a group of coordinated tasks or activities for the benefit of the user.
Add an environment
Environment support
The environment support is not completely done. It will be soon because it is our next high priority. The management of this environments will be done via the web console. If you want to use environments you can anyway contact us to manage them for you. |
Access control per environment
Environment support
The environment support is not completely done. It will be soon because it is our next high priority. The access control of the environments will be done via the web console. If you want to use environments you can anyway contact us to manage them for you. |
Architecture
General architecture
One of the most important thing to understand with ZetaPush is that you can use it in two steps: The run and the push. The run
mode is when you develop your application, your worker is running locally and the push
mode is when you have pushed your application on the ZetaPush Cloud and that your worker is running on it.

Data between the two phases
Whether you run or push your application, you use the same data. For example, if you store data during your development phase (using the |

Both front and worker are running locally. The front interacts with custom cloud services contained in the worker through ZetaPush Cloud. As ZetaPush Cloud provides bidirectional connection, the custom cloud services can also interact with the front through ZetaPush Cloud too.
A custom cloud service can interact with built-in cloud services provided by ZetaPush.
When you start using ZetaPush, you only develop locally so from your point of vue, your front seems to directly interact with your custom cloud services. In fact, all messages go through the Internet and ZetaPush Cloud. It means that a published front can interact with a worker that is running locally and vice versa. |
ZetaPush choices
High availability
ZetaPush ensures a high-availability of your appplications.
Indeed, all systems on the ZetaPush cloud (servers, database, …) are redundant. This will also be the case for workers in next versions.
If a server goes down, another takes over.
Load-balancing
ZetaPush provides Load-balancing feature between workers. Indeed, if an application has many workers (The back part of your application), the ZetaPush cloud will automatically spread incoming requests between each of them.
Running local and pushed worker on same application
In the case of you have already deployed your application in the ZetaPush cloud, a worker is running on it. Then, if you run your application locally, a second worker is running on your computer.
In this case, the load-balancing is always effective, the incoming requests will be spread between each worker.
If you want to grab all traffic in your local worker, you need to use the flag --grab-all-trafic
of the CLI.
For example:
$ zeta run --grab-all-trafic
With this option, all requests will be redirected to your local worker.
You do not use this option in production because all of your client’s requests will be redirected on your local worker. |
The possibility to grab traffic for a specific user (for debug for example) will be possible in a next version. |
Scalability
ZetaPush provides the scalability of yours workers.
For example, if you have deployed an application on the ZetaPush cloud, it is possible to increase the number of worker to scale the application.
For now, to scale your application, you need to contact us. But in the next versions, the scalability will be automatic. Another feature that will be available in the next versions is the possibility to scale up your application on a short period to absorb peak loads without extra cost. |
Application code structure
The purpose of this section is to explain the various elements of a ZetaPush application and the interaction between them.
In the first instance we will see the organization of a ZetaPush application, then we will see the role of each file or folder.
Organization of an application
A ZetaPush application includes several components. You can see this with the following picture that represents the files tree of a basic application:

The worker
folder contains your business logic. this is the code that is not directly visible to your users. It is often named as backend part. The front
folder is the visible part of your application. It contains the code of the web pages displayed to your users. It is often named the frontend part.
The rest of the files are needed for the functioning of the application. Each role of this files is explained below :

1 | A ZetaPush application is mainly based on the JavaScript ecosystem . An application is similar to a JavaSscript module . So at the root of the application, we have a package.json file, more information about his role is below. |
2 | In addition of the JavaScript ecosystem , a ZetaPush application uses the TypeScript ecosystem by default. So a tsconfig.json file is necessary at the root folder. You can see more information about his role also see why we use TypeScript. |
3 | The worker folder contains your business logic. |
4 | The front folder contains the web pages displayed to end users. |
5 | The application.json file lets you configure the logic of your application. (Token, API key, Mailjet configuration, etc…). |
6 | The .zetarc file contains the application and ZetaPush account information (developer login, developer password, appName, etc…). It should not be commited (it contains login and password) so by default it is added to the .gitignore file. |
Files tree
As seen in the last section, each file has a specific role. We will explain each of them step by step:
package.json
A ZetaPush application is based of the JavaScript ecosystem
.
So each application has his package.json
file.
package.json
file
1
2
3
4
5
6
7
8
{
"name": "myApp",
"main": "worker/index.ts", (1)
"dependencies": {
"@zetapush/cli": "^v0.37.1" (2)
"@zetapush/core": "^v0.37.1", (3)
}
}
1 | Entry point of your worker. In this file you will expose your custom cloud services |
2 | Dependency to the CLI (Command Line Interface). Use it to run your worker or deploy your code. |
3 | Dependency to the ZetaPush Core to write some custom cloud services |
The main
property defines the path of the worker entry point. That means that when you run or push your ZetaPush application, this is from this entry point that your business logic is launched.
Location of the entry point
At the present time, the worker code needs to be in the |
tsconfig.json
The tsconfig.json
file is here because by default ZetaPush uses the TypeScript ecosystem
. For example, the cloud services are written in TypeScript
and the worker
folder contains by default a TypeScript
file.
You can see why we use TypeScript.
In the TypeScript ecosystem
, the tsconfig.json
file is needed to define the root of the application. So in our case, the tsconfig.json
file is placed at the root of the ZetaPush application.
If you are not familiar with TypeScript
, you can see more informations here.
In addition to define the root of our application, the tsconfig.json
file defines how the transpilation from TypeScript
to JavaScript
is done.
You can define a lot of configurations according to the TypeScript documentation.
For the proper functioning of the ZetaPush application, the tsconfig.json
file needs to have some minimal configuration like explained below :
1
2
3
4
5
6
{
"compilerOptions": {
"experimentalDecorators": true (1)
"emitDecoratorMetadata": true (2)
}
}
1 | experimentalDecorators is needed to activate the ES7 decorators. There are used in most worker code. |
2 | emitDecoratorMetadata is needed for the proper functioning of the ES7 decorators. In particular for types. |
In the case you need to customize your tsconfig.json
file, for example to use features only declared in ES6 (es2015), you can customize your tsconfig.json
like below. Of course, you can customize with any properties provided by TypeScript.
1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"lib": [
"es2015"
],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
worker folder
As seen above, the worker
folder contains the business logic of your application. It is often named "backend code.
This is in this part that you will create your business logic and interact with the built-in cloud services provided by ZetaPush (User management, file system, database, notifications, etc…).
By default (When the application is generated by the CLI), the worker
folder contains only one file, called index.ts
.
1
2
3
4
5
export default class {
hello() {
return `Hello World from Worker at ${Date.now()}`;
}
}
Develop your worker in several ways
In this example we see a worker that is created with only one class. You will see several ways to create a worker in the cloud service development section. |
Don’t need to transpile TypeScript
Even if your worker code is in |
front folder
The front
folder contains your front code.
Like any other website host, you need to have a index.html
file in this folder.
application.json
This file is optional in a ZetaPush application. For now you just need to use it when you use the StandardUserWorkflow. Of course, you can also define your own properties
List of application.json properties
To know which keys are available for the |
.zetarc
The .zetarc
file is the most important file in your application.
It lets you define your ZetaPush account and which ZetaPush application you want to use.
1
2
3
4
5
{
"developerLogin": "user@gmail.com",
"developerPassword": "password",
"appName": "myAppName"
}
In addition to the properties displayed in the example, 2 properties are used:
-
developerSecretToken: The CLI encrypts the
developerPassword
in the.zetarc
file. You don’t need to modify it. If you want to change the ZetaPush account password in the.zetarc
file, you can delete thedeveloperSecretToken
and type again thedeveloperPassword
. -
platformUrl: That corresponds to the used ZetaPush Cloud for your ZetaPush application. When using ZetaPush public cloud, you don’t need to configure it. If you need a private ZetaPush cloud, you can contact us to ask for it.
Do not commit the .zetarc
The |
ZetaPush development concepts
Npm modules provided by ZetaPush
ZetaPush provides several npm modules for helping you in your development. Each module has a specific role.
Module | Role |
---|---|
@zetapush/cli |
Contains the Command Line Interface of ZetaPush.
You can launch |
@zetapush/client |
When you develop the front part of your application, i.e. the web pages displayed to your users, you use a ZetaPush client to interact with your worker and the ZetaPush cloud. |
@zetapush/cometd |
You don’t use this module directly. It is used internally for the communication with the ZetaPush cloud via the CometD protocol. |
@zetapush/common |
This module includes some functions and classes that are used in other modules. |
@zetapush/core |
This is the core of ZetaPush. Notably, it provides the dependency injection |
@zetapush/create |
This module is used when you create a new development project using ZetaPush through the command |
@zetapush/example |
It contains a basic example of a ZetaPush application |
@zetapush/http-server |
ZetaPush uses a HTTP server for some features, for example to confirm an account in the Standard User Workflow. You have information about this module in his dedicated section |
@zetapush/testing |
ZetaPush integrates testing and provides some useful features about it via this module. |
@zetapush/integration |
This package is used internally by ZetaPush. It contains end to end tests to ensure that each new version is well tested and that there is no regression. |
@zetapush/platform-legacy |
ZetaPush contains some Technical Services that are the fundations to build more functional and advanced services. These services will be improved and some may be replaced by a service that matches more your needs. This module contains them and you have more information about their utilization in the dedicated section. |
@zetapush/troubleshooting |
When you have a technical error, it can be hard to understand what really happens. This module diagnoses all errors (errors when worker starts, errors when worker is pushed, errors in connection/network…) and gives you an help to understand the error and to help you fix it. For example, a network error can have several causes:
This module will give you help to understand where the problem is and how to fix it. |
@zetapush/user-management |
In almost every application, you need to provide user management features like user creation, user authentication, profile management… This module provides functional cloud services that directly provides all these features. You don’t need to bother about implementing it again and again. You just consume one of these cloud services like StandardUserWorkflow. |
@zetapush/worker |
This module provides the container that executes the code of your business logic i.e. the worker. It is internally used by the CLI when you launch |
Dependency injection
Dependency Injection is a technique whereby one component supplies the dependencies of another component. A dependency is a component that can be used. An injection is the passing of a dependency to a dependent component that would use it.
In our case, we the Dependency Injection to provides the cloud services (builtin or not) and other classes. The benefic is that you don’t need to instanciate a dependency. You ask it and you have it.
In the following example we need to have the GDA builtin cloud service (database). We ask the dependency injection to provide one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Gda } from '@zetapush/platform-legacy';
@Injectable() (1)
export default class MyApi {
constructor(private gda: Gda) {} (2)
putData(name: string) {
return await this.gda.put({ (3)
table: 'my-table',
column: 'my-column',
key: Date.now().toString(),
data: name
});
}
}
1 | Say to the dependency injection that it should provide some dependencies in this class |
2 | I just say that I need a GDA instance in my class |
3 | I use my dependency. I know that my dependency is automatically instantiated by the Dependency Injection. |
Dependencies are singletons by default
By default, when you use the dependency injection, each component that is instanciated is a singleton. |
ZetaPush uses Injection JS library for manage the Dependency Injection. It is an extraction of the Angular’s dependency injection.
You can have more information about the dependency injection in the Angular Dependency Injection Documentation
Current running context
When you develop your cloud services, you may want to use the current context. The most important thing about this is that it depends of your runtime environment. Indeed, the properties will be not the same when your worker is running locally or when your worker is running on the ZetaPush cloud.
Different contexts
The context is not the same between locally and in the ZetaPush cloud because in the first case the context is provided by the CLI and in the second case the context is provided by the ZetaPush platform. |
To handle this, ZetaPush provides some util functions to get the current context. There are 3 classes to manage the context :
Environment
This object is just a wrapper for ConfigurationProperties and ZetaPushContext.
You can use it like this :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Injectable, Environment } from '@zetapush/core'; (1)
@Injectable()
export default class Api {
constructor(private env: Environment) {} (2)
getEnvironment() {
return {
context: this.env.context, (3)
name: this.env.name, (4)
properties: this.env.properties, (5)
variables: this.env.variables (6)
};
}
}
1 | The Environment class is in the @zetapush/core package. |
2 | The dependency injection provides us a Environment instance. |
3 | We get the ZetaPushContext |
4 | We get the name of the environment (dev, prod, etc…) |
5 | We get the ConfigurationProperties |
6 | We get the environment variables |
As the Environment object is a wrapper for ConfigurationProperties and ZetaPushContext, we retrieve the values for these objects via the properties context
and properties
.
Environment feature is not ready in the full chain
The name of the environment is already available in the current context but the Environment management feature is not ready yet. The integration in the CLI is missing and it is our next high priority. |
Because the Environment
object also contains the ConfigurationProperties
and ZetaPushContext
,
you can use the methods of this nested properties. For example to get the application name via
the ZetaPushContext
:
1
2
3
4
5
6
7
8
9
10
import { Injectable, Environment } from '@zetapush/core'; (1)
@Injectable()
export default class Api {
constructor(private env: Environment) {} (2)
getAppName() {
return this.env.context.getAppName(); (3)
}
}
1 | The Environment class is in the @zetapush/core package. |
2 | The dependency injection provides us a Environment instance. |
3 | Get the appName via getAppName() method. |
ConfigurationProperties
This object contains the content of your application.json
file.
Access to application.json properties
To interact with your application.json
file, you can use these provided methods:
-
get()
: Get a property value via his name, returnsundefined
if doesn’t exists -
getOrThrow()
: Get a property value via his name, returnsAPI_ERROR
exception if doesn’t exists -
has()
: Check if a property exists in theapplication.json
file (returns boolean)
In the case of we have the following application.json
file:
1
2
3
4
5
6
{
"organization": {
"name": "ZetaPush"
},
"my-name": "MyName",
}
We can get properties like this :
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable, ConfigurationProperties } from '@zetapush/core'; (1)
@Injectable()
export default class {
constructor(private properties: ConfigurationProperties) {} (2)
getUserInfo() {
return {
name: this.properties.get('my-name'), (3)
orga: this.properties.get('organization.name') (4)
}
}
}
1 | The ConfigurationProperties class is in the @zetapush/core package. |
2 | The dependency injection provides us a ConfigurationProperties instance. |
3 | We read the application.json property via a getter |
4 | We read the application.json nested property via a getter |
ZetaPushContext
This last object contains (amongst others) the URL of your worker and your front (deployed on the ZetaPush cloud or running locally).
Use ZetaPushContext
The ZetaPushContext
provides you some methods to interact with it:
-
getAppName()
: Returns the appName of your application -
getPlatformUrl()
: Returns the platformUrl of your application -
getFrontUrl()
: Returns the url of the used front part of your application -
getWorkerUrl()
: Returns the url of the used worker -
getLocalZetaPushHttpPort()
: Returns the port used on HTTP server
Role of URL
|
The ZetaPushContext
can be used like below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { ZetaPushContext, Injectable } from '@zetapush/core'; (1)
@Injectable()
export default class Api {
constructor(private context: ZetaPushContext) {} (2)
getContext() {
return {
front: this.context.getFrontUrl(), (3)
worker: this.context.getWorkerUrl(),
zetapushPort: this.context.getLocalZetaPushHttpPort(),
appName: this.context.getAppName(),
platformUrl: this.context.getPlatformUrl()
};
}
}
1 | ZetaPushContext is provided by the @zetapush/core module |
2 | We ask the Dependency Injection to provide an instance of ZetaPushContext |
3 | We use all methods provided by the ZetaPushContext |
Develop with ZetaPush
In the Getting started section you already see how to develop with ZetaPush. So in this section we will see in more details how to develop with ZetaPush and some advanced usages.
Develop your front only
ZetaPush client
ZetaPush provides a ZetaPush Client to interact with the ZetaPush cloud. Each of the 3 types of Client has a specific purpose:
-
Weak client for an anonymous connection
-
Simple client for a basic authenticated connection
-
Smart client that automatically chooses between Weak or Simple client according to presence or not of credentials
Weak Client
The Weak client lets you create an anonymous connection with the ZetaPush cloud. That means that you are only connected via a token.
Token of anonymous user
The token is only used to identify the anonymous user during his session. Indeed, each opened connection has its own session on the ZetaPush cloud so we need to identify each of them. |
How to use the Weak Client :
Connection with Weak Client
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Weak client</title>
</head>
<body>
Weak client
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="./index.js"></script>
</html>
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// If you use npm you can do :
// import { WeakClient } = from '@zetapush/client';
client = new ZetaPushClient.WeakClient();
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
// Disconnection
client.disconnect();
-
Simple Client
The Simple Client is used to have an authenticated connection with the ZetaPush cloud.
Simple client authentication
With the Simple Client the authentication is done with a couple login/password of the end user previously created. (It’s not your ZetaPush developer credentials at all) |
How to use the Simple Client :
Connection with Simple Client
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Simple client</title>
</head>
<body>
Simple client
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="./index.js"></script>
</html>
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// If you use npm you can do :
// import { Client } = from '@zetapush/client';
client = new ZetaPushClient.Client({
authentication() { (1)
return ZetaPushClient.Authentication.simple({
login: 'my-login',
password: 'my-password'
});
}
});
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
// Disconnection
client.disconnect();
1 | We define the used authentication (simple in this case) |
-
Smart Client
SmartClient is recommended
We advise you to use this ZetaPush Client for convenience. |
The Smart Client is a combinaison of the Weak Client and the Simple Client. If the Client has credentials, a Simple Client is used, otherwise a Weak Client is used.
How to use the Smart Client :
Connection with Smart Client
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Smart client</title>
</head>
<body>
Smart client
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="./index.js"></script>
</html>
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// If you use npm you can do :
// import { SmartClient } = from '@zetapush/client';
client = new ZetaPushClient.SmartClient();
// Set credentials (optional)
client.setCredentials({ login: 'my-login', password: 'my-password'});
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
// Disconnection
client.disconnect();
-
Call your API
Once you are connected to the ZetaPush cloud, you can call your API declared in the worker
part of your application.
The best way is to use the method createProxyTaskService()
. This will use proxy object.
Use proxy for API
Using proxy avoids you to have to create a definition class of your API. |
Call API through proxy :
Call API through proxy
index.ts (worker)
1
2
3
4
5
export default class {
hello() {
return `Hello World from Worker at ${Date.now()}`;
}
}
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Weak client</title>
</head>
<body>
<button class="js-Hello">call hello()</button>
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="./index.js"></script>
</html>
index.js (front)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// If you use npm you can do :
// import { WeakClient } = from '@zetapush/client';
client = new ZetaPushClient.WeakClient();
api = client.createProxyTaskService();
// Handle the click
document.querySelector('.js-Hello').addEventListener('click', async () => {
console.log(await api.hello()); (1)
});
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
1 | Call hello() method of the worker |
-
Call API without proxy
In the case of the Proxy is not available on your platform, you can use the createAsyncTaskService()
method.
For this, you need to create a definition class:
Definition class
A definition class is used to define which methods are available in your worker. It has always the same format. |
Call API without proxy
index.ts (worker)
1
2
3
4
5
export default class {
hello(name: string) {
return `Hello ${name} from Worker at ${Date.now()}`;
}
}
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Weak client</title>
</head>
<body>
<button class="js-Hello">call hello()</button>
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="https://unpkg.com/@zetapush/platform-legacy"></script>
<script src="./index.js"></script>
</html>
index.js (front)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// If you use npm you can do :
// import { WeakClient } = from '@zetapush/client';
// import { Queue } = from '@zetapush/platform-legacy';
client = new ZetaPushClient.WeakClient();
// Handle the click
document.querySelector('.js-Hello').addEventListener('click', async () => {
console.log(await api.hello('test')); (1)
});
// Our definition API
class MyApi extends ZetaPushPlatPlatformLegacy.Queue { (2)
hello(name) { (3)
return this.$publish('hello', name); (4)
}
}
// create our AsyncTaskService
const api = client.createAsyncTaskService({
Type: MyApi
});
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
1 | Call hello() method of the worker |
2 | We extends the Queue Technical Service that allows us to communicate with ZetaPush Cloud |
3 | Each method returns a Promise |
4 | The definition API is always the same format : return this.$publish('name-cloud-function', param1, paramN); |
-
Injection of your application’s information
The creation of a ZetaPush Client needs to have some information for commucating with the right application.
For this, there are the appName
and optionally the platformUrl
.
1
2
3
4
client = new ZetaPushClient.WeakClient({
appName: 'my-app-name', (1)
platformUrl: 'my-platform-url' (2)
});
1 | Your application name |
2 | The optional URL of your used platform (by default you don’t need to use this variable) |
If you develop the front part of your application in the folder defined by convention (i.e. the front/
folder of your application) the information (appName
and platformUrl
) will be automatically injected by the ZetaPush CLI.
When define
appName or platformUrl ?You only need to define |
Storage of credentials
ZetaPush client is able to automatically store some information in order to reconnect the end users without requiring to enter credentials again. As said before there are two different clients:
-
The token for the Weak Client
-
The credentials for the Simple Client (and the Smart Client if credentials are used)
In the two cases, a cookie is created to save the user session. It is related to the appName
to avoid conflicts.
The token has different expiration duration according to the client type:
-
Weak client
has no expiration meaning that once the client connection is established the first time, the cookie is set. The next connections reuse the same token. It is useful to know that the connection is for the same anonymous user as a previous session. -
Simple client
stores the cookie for 30 days meaning that if a user authenticates the first time on your application and then leaves it, when he comes back he can directly use your application without authenticating again.
Obviously, token storage can be disabled and expiration duration can be configured. This section will later describe how to enable/disable token storage, how to change storage implementation (local storage, session storage, …) and how to change expiration duration.
Multiple API
When you expose several cloud services in your worker, some cloud functions may have the same name. So you need a way to indicate which cloud service is targeted. Therefore in your worker you can use namespaces to name each cloud service. Then you also need to indicate which cloud service to call in your front too.
Mobile development
The mobile development with ZetaPush is already possible but the documentation is not ready. Two SDKs are available (Android and iOS). If you need to use them, please contact us.
Generate SDKs from custom cloud service code
When you develop your business logic, you may want to create a SDKs to expose your methods. This feature will soon be ready.
Develop custom cloud services
What is a custom cloud service
A custom cloud service combines many cloud functions like the cloud services exposed by ZetaPush. The only difference is that you create the cloud functions. Generally, you will want to put the business logic of your application in the custom cloud services.
Architecture
You develop cloud services that are contained in a concept named worker. In your code it is
materialized by a folder named worker
. The worker is the ZetaPush handler that starts your code (your custom cloud services).
Your application is composed of a logic part (the worker) and a UI part (the front).
There are two ways of running an application (worker and front):
-
You develop on your machine and iterate to provide features. Your application runs locally and interacts with ZetaPush Cloud.
-
Once you are ready to make the developed features available to your end-users, you publish your application. Your application runs directly in ZetaPush Cloud.

Both front and worker are running locally. The front interacts with custom cloud services contained in the worker through ZetaPush Cloud. As ZetaPush Cloud provides bidirectional connection, the custom cloud services can also interact with the front through ZetaPush Cloud too.
A custom cloud service can interact with built-in cloud services provided by ZetaPush.
When you start using ZetaPush, you only develop locally so from your point of vue, your front seems to directly interact with your custom cloud services. In fact, all messages go through the Internet and ZetaPush Cloud. It means that a published front can interact with a worker that is running locally and vice versa. |

Once published, everything runs in the ZetaPush Cloud. However the behavior is the same. Every interaction between the front and custom cloud services goes through the ZetaPush Cloud.
The only main difference is that ZetaPush Cloud manages the hosting, the scalability and the high availability for you.
Develop a custom cloud services
Define a custom cloud service
As a reminder worker is the ZetaPush container that will handle your code that defines custom cloud services.
By convention, the code of your custom cloud services is placed in files under worker
directory:
myApp
├── .zetarc
├── .gitignore
├── front
│ ├── ...
│ └── ...
├── worker
│ ├── file1.ts
│ ├── file2.ts
│ ├── ...
│ ├── fileN.ts
│ └── index.ts
├── package.json
├── README.md
└── tsconfig.json
Change directory structure
See more information about directory structure convention and how to adapt the structure. |
Translated into code, a custom cloud service is just a class
and its methods
are the cloud functions.
The code is written in any ts
file defined in worker
directory. For small application like a "hello world",
write your code directly in index.ts
file.
So the most basic example of a custom cloud service is below:
1
2
3
4
5
6
7
8
class HelloWorldAsCustomCloudService { (1)
constructor() {} (2)
helloWorld() { (3)
return "Hello World";
}
}
1 | A custom cloud service is encapsulated in a JavaScript/TypeScript class. HelloWorldAsCustomCloudService is your first custom cloud service. |
2 | A class can define a constructor. We will see later why it is important |
3 | helloWorld is your first custom cloud function |
A cloud function is always asynchronous (with the async keyword or not) |
Change entry point
By default, the code above is directly written in You can write code of your custom cloud services in any other file named as you want. You will see in the reference part how to indicate to ZetaPush worker how to find your files if you don’t want to follow convention. |
Each cloud function in a custom cloud service is a standard JavaScript/TypeScript method. For example, if you want a cloud function that receives two parameters you write:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class HelloWorldAsCustomCloudService { (1)
constructor() {}
helloWorld() {
return "Hello World";
}
saySomething(message: string, times: number) { (2)
let fullMessage = '';
for(let i=0 ; i<times ; i++) { (3)
fullMessage += `${message}\n`
}
return fullMessage (4)
}
}
1 | The custom cloud service definition as seen before |
2 | Declaration of a cloud function named saySomething . This cloud function accepts
two parameters. As TypeScript is the recommended way, each parameter is typed. |
3 | Just for an example, the business logic consists in looping a number of times and concatenating the message. You can obviously write any code you want here. |
4 | The custom cloud function simply returns the contactenated string. |
Why typing ?
Typing is optional but recommended to have a well defined API. Thanks to typing, ZetaPush is able to generate more accurate documentation based on your code and also generate mobile/web/IoT SDKs from your code with the right types so it makes developing your clients easier (for example, auto-completion can be used). |
Tips about cloud functions
A custom cloud service can have as many custom cloud functions as you want. A custom cloud function can have as many parameters as you want. Parameters can be anything: A custom cloud function can return anything including a Promise. You can also write your code using async/await syntax. |
Define several custom cloud services
Obviously when your application grows, you need to split your custom cloud service into several classes in order to make your API more understandable and more maintainable. You can learn how to define several custom cloud services. |
You have now a custom cloud service that provides two cloud functions. But until it is exposed, it can’t be called from outside of the worker.
The next section describes how you can expose your custom cloud service.
Expose a custom cloud service
When you define a custom cloud service that you want to expose to the client, you need to declare it. There are 2 cases:
-
Only one custom cloud service exposed
-
Many custom cloud services exposed
In this section we only address one custom cloud service exposed.
How to expose several custom cloud services
You can also learn how to expose several custom cloud services in the advanced sections. |
We follow npm conventions to indicate the entry point of your worker. Indeed, the package.json
defines a property named main
.
We use this property to indicate which file is the main file that declares the exposed custom cloud service.
By default, the main file is named index.ts
and this file is placed in worker
directory. So the main
property is by default worker/index.ts
.

Now that your custom cloud service is marked as the entry point, it can be exposed by ZetaPush. However you still have a little change to make on your code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class HelloWorldAsCustomCloudService { (1) (2)
constructor() {}
helloWorld() {
return "Hello World";
}
saySomething(message: string, times: number) {
let fullMessage = '';
for(let i=0 ; i<times ; i++) {
fullMessage += `${message}\n`
}
return fullMessage
}
}
1 | export : this is required by TypeScript.
In fact, declaring a class in a file makes it private.
It means that if you have another .ts file and you want to import HelloWorldAsCustomCloudService declaration,
it won’t be possible without export keyword. This is for code encapsulation. |
2 | default : TypeScript provides this keyword.
When exposing only one custom cloud service, default tells the worker that there is only one class (only one
custom cloud service defined in the index.ts ). So the worker can directly analyze it and instantiate it. |
How to expose several custom cloud services
As seen above, you can also learn how to expose several custom cloud services in the advanced sections. |
Now your custom cloud service can be loaded by the ZetaPush worker and your custom cloud service is automatically exposed. It means that now a client can call the cloud functions defined in your custom cloud service.
The next section shows how to call a cloud function from a web page using pure JavaScript.
Use a custom cloud service in your front
In this chapter we will see how to consume our cloud functions from a web page.
At the end of this example, we will have one button to call helloWorld
cloud function and
one section with a message, a number of repetitions and a button to call saySomething
cloud function.
Mobile applications
Here we show how to create a web page that consumes our custom cloud service. You can also create a mobile application for Android, iOS and Windows Phone as well as code for a device. You can use any language you want for the client part. By default we target web because it is currently the most used technology (even to build some mobile applications using hybrid technologies). |
As a reminder, here is the code of custom cloud service named HelloWorldAsCustomCloudService
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class HelloWorldAsCustomCloudService {
constructor() {}
helloWorld() {
return "Hello World";
}
saySomething(message: string, times: number) {
let fullMessage = '';
for(let i=0 ; i<times ; i++) {
fullMessage += `${message}\n`
}
return fullMessage
}
}
By convention the directory structure of a ZetaPush application is defined below. You place
the code of your web page in the front
directory:
myApp
├── .zetarc
├── .gitignore
├── front
│ ├── ...
│ ├── index.js
│ └── index.html
├── worker
│ ├── ...
│ ├── ...
│ └── index.ts
├── package.json
├── README.md
└── tsconfig.json
Other front files
For this example, we only need an HTML page and a JavaScript file. Needless to say that you can have CSS files, images and anything you want too. See more information about directory structure convention and how to adapt the structure. Moreover, you are not limited to write pure JavaScript code. You can also use any framework you want: |
For the example, we create an HTML page with a button to display the HelloWorld message in the page each time the button is clicked:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Celtia</title>
</head>
<body>
<button onclick="hello()">hello</button> (1)
<ul id="result-container"></ul> (2)
<script src="https://unpkg.com/@zetapush/client"></script> (2)
<script src="./index.js"></script> (3)
</body>
</html>
1 | We call hello() function that is defined later in index.js |
2 | Define a node that will display received messages |
3 | We include the client module provided by ZetaPush |
4 | We include a JavaScript file to distinguish HTML code from JavaScript code. All could be written in the HTML |
Then, in order to interact with ZetaPush cloud, we need to create a client instance and to connect to the cloud:
1
2
3
4
5
6
7
8
9
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient(); (1)
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService(); (2)
// Handle connection
client.connect() (3)
.then(() => { (4)
console.debug('onConnectionEstablished');
});
1 | Use ZetaPushClient factory to instantiate a client. In this example, we ask for an anonymous connection (it means that actions are not bound to a particular user of your application) |
2 | Custom cloud service provides some functions that can be called from a client. For example, the custom cloud service exposes helloWorld and saySomething cloud functions.
Instead of having to write the signature of these functions in the client too, we simply use a JavaScript Proxy.
Therefore, you can directly interact with your custom cloud service without writing any pass-through code on the client side. |
3 | The client is ready, now connects it to the ZetaPush cloud. |
4 | Once the connection is established, the Promise is resolved and you can write some code in then callback. |
Available client types
|
hello
cloud functionOur client is ready and now we want to call cloud function named helloWorld
, we add the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient();
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService();
// Handle connection
client.connect()
.then(() => {
console.debug('onConnectionEstablished');
});
// Handle DOM events
async function hello() { (1)
const messageFromCloudFunction = await api.helloWorld(); (2)
document.getElementById('result-container').innerHTML += `<li>${messageFromCloudFunction}</li>` (3)
}
1 | Each time a user clicks on the button defined in the HTML, this method is called. |
2 | Calls the helloWorld cloud function and store the result in a variable. |
3 | Add a new list item in the HTML page |
Before running this sample, we improve our front in order to also understand how to call a cloud function that has parameters.
saySomething
cloud functionWe add two inputs to be able to send values to the saySomething
cloud function.
The first input is a text to repeat. The second input is the number of times to repeat the message.
Here is the updated code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Celtia</title>
</head>
<body>
<button onclick="hello()">hello</button>
<div style="border: 1px solid #ccc">
Message: <input type="text" id="message-input" /> (1)
Repeat: <input type="text" value="1" id="repeat-input" /> (2)
<button onclick="saySeveralTimes()">say something</button> (3)
</div>
<ul id="result-container"></ul>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="./index.js"></script>
</body>
</html>
1 | The first input to enter a message |
2 | The second input to enter the number of times to repeat the message |
3 | A new button to call saySeveralTimes() function defined in index.js |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient();
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService();
// Handle connection
client.connect()
.then(() => {
console.debug('onConnectionEstablished');
});
// Handle DOM events
async function hello() {
const messageFromCloudFunction = await api.helloWorld();
document.getElementById('result-container').innerHTML += `<li>${messageFromCloudFunction}</li>`
}
async function saySeveralTimes() { (1)
const message = document.getElementById('message-input').value; (2)
const repeat = document.getElementById('repeat-input').value; (3)
const messages = await api.saySomething(message, parseInt(repeat)); (4)
document.getElementById('result-container').innerHTML += `<li>${messages}</li>` (5)
}
1 | Each time a user clicks on the button 'say something' defined in the HTML, this method is called. |
2 | Reads the value of the input for getting the message. |
3 | Reads the value of the input for getting the number of times to display the message. |
4 | Calls saySomething cloud function with parameters. Note that as second parameter is a number, we have to convert the string from
the input to a number using parseInt . |
5 | Add a new list item in the HTML page containing the repeated messages |
Now everything is ready to run our application.
When you call a cloud function from the client, the result is always a Promise even if the custom cloud function is synchronous because everything goes through the network. |
Run application
You have now defined a custom cloud service with one or several cloud function(s). You have also exposed your custom cloud service. So it is ready to be called from a client. Your client is also ready to call the custom cloud service.
The next step is to start the project (both front and worker) using the ZetaPush CLI on your computer.
Npm script aliases
Npm provides a handy feature to run scripts provided by dependencies without needing to change your computer settings or install the tool. ZetaPush defines npm script aliases to run the ZetaPush CLI through npm. |
To start your worker and you front using the ZetaPush CLI through npm, you simply run:
$ npm run start -- --serve-front
|
Run only the worker
It is also possible to run only your worker. |
Automatic injection of ZetaPush information in your front
If you look closely to the code we have written, there is no information about your application at all in this sample code (the The ZetaPush CLI automatically injects the When you run your project locally, the local HTTP server that exposes the HTML page is lightly modified to include the When your project is run in the cloud, the same principle is applied. This way, you just use |
Now when we click the "hello" button, "Hello World" is displayed on the page.

When you start your project locally, the first thing that happens is that your worker connects himself automatically to the ZetaPush cloud 1 2.
Then when you open your web browser, the connection from the client is established between the web page and the ZetaPush cloud 3 4.
When you click on the button, a message is sent through the opened connection in order to tell ZetaPush cloud to execute some
remote code 5. ZetaPush cloud routes the message to your worker 6 (that is running on your machine here). The worker receives the message
and calls the hello
cloud function 7.
The cloud function generates a result 8. The worker picks this result and transform it to a message 9. This message is then sent to the ZetaPush cloud 10. The ZetaPush cloud routes the response message to the calling client 11. The client receives the message and the response is parsed 12 and available in your JavaScript.
You can also try to enter a message and a number of repetitions and hit the "say something" button.
The behavior is exactly the same. This time a message is sent to call the cloud function with also the parameters.
Serialization/deserailization between client and custom cloud service
When you call a cloud function from the client, under the hood, the values are serialized in JSON. This is understandable because everything goes through the network. On the worker side, everything is deserialized by the worker and your custom cloud service receives the values as they were written in the front side. |
Compose cloud services
You can compose cloud services either by using built-in cloud services provided by ZetaPush or by using another of your custom cloud services.
Dependency injection is a powerful software technique that is totally managed by the worker.
You don’t need to manage the creation of neither built-in cloud services nor your custom cloud services. You just indicate in your code that you need a dependency and ZetaPush instantiates it for you. The instantiated dependency is then injected everywhere the dependency is needed.
The dependency injection of ZetaPush uses Angular injection-js library. |
To mark a custom cloud service injectable (meaning that can be automatically created by ZetaPush
and then injected everywhere the dependency is aksed),
you need to import the package @zetapush/core
in order to use Injectable
decorator.
1
$ npm install --save @zetapush/core
Once the npm package is installed, you can import Injectable
decorator and place it on your custom cloud service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Injectable } from '@zetapush/core'; (1)
@Injectable() (2)
export default class HelloWorldAsCustomCloudService {
constructor() {}
helloWorld() {
return "Hello World";
}
saySomething(message: string, times: number) {
let fullMessage = '';
for(let i=0 ; i<times ; i++) {
fullMessage += `${message}\n`
}
return fullMessage
}
}
1 | Import the ZetaPush module that contains core features for custom cloud services such as injectable |
2 | Mark you custom cloud service candidate for dependency injection |
Here we just indicate that this custom cloud service can have dependencies that will be automatically injected and also that this custom cloud service can be injected anywhere it is needed.
In the next sections, we will see in concrete terms how to use it to reuse either a built-in cloud service or one of your custom cloud services.
ZetaPush built-in cloud services are available in @zetapush/platform-legacy
module.
Add this module to your package.json
by running the following command:
1
$ npm install --save @zetapush/platform-legacy
List of cloud services provided by ZetaPush
As a reminder, here is the list of built-in cloud services |
In the following example, we will use the Stack
cloud service provided by ZetaPush.
In our use-case, we want to put some data associated with the current timestamp and be able to list all stored data.
To do this, the Stack
service already provides some methods:
-
push({ stack: string, data: object });
-
list({ stack: string });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Injectable } from '@zetapush/core';
import { Stack } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class MyStorageService {
private stackName = 'stack-example';
constructor(private stack: Stack) {} (2)
/**
* Store data with associated timestamp
*/
storeWithTimestamp(value: any) {
return this.stack.push({ (3)
stack: this.stackName,
data: {
value,
timestamp: Date.now()
}
});
}
/**
* List all stored data
*/
getAllData() {
return this.stack.list({ (4)
stack: this.stackName
});
}
}
1 | We import the Stack service so that TypeScript knows it |
2 | We ask ZetaPush to inject a dependency of type Stack (stack is an instance of Stack ).
Here we use the shorthand syntax for declaring a constructor parameter as well as a property.
So the property this.stack is defined and is initialized with stack parameter. |
3 | Calls the Stack service to store data (method push ) |
4 | Calls the Stack service to list data (method list ) |
The example defines a custom cloud service named MyStorageService
that provides two cloud functions:
-
storeWithTimestamp
that receives a value from a client and calls theStack
service to store received value (value
parameter) as well as the current timestamp (usingDate.now()
) -
getAllData
that has no parameters and callsStack
service to get all previsouly stored pairs of<value, timestamp>
.
The most important part to understand is in the constructor
. As described before,
the example uses dependency injection. You simply tell ZetaPush that you need a dependency of type Stack
.
You don’t create it in your custom cloud service because it is not the responsibility of
your custom cloud service to create the Stack
service. Instead, you let ZetaPush
handle the creation. Thanks to @Injectable
decorator, ZetaPush detects that you have
a custom cloud services with needed dependencies. ZetaPush understands that you need a Stack
instance so it instantiates it before instantiating your custom cloud service. Then
ZetaPush instantiates your custom cloud service by providing, as the first argument of your
constructor here, the instance of Stack
.
This behavior avoids you to have complex code to instantiate built-in cloud services. Moreover,
if you have several custom cloud services that are using the Stack
service, thanks to
dependency injection, there will be only one instance shared between your custom cloud services.
In this example, we will have 2 custom cloud services:
-
Calendar
: Utils function to return the current date -
HelloWorldService
: Basic example usingCalendar
cloud function
The first custom cloud service (Calendar
) is defined in the file worker/calendar.js
.
1
2
3
4
5
export class Calendar {
getNow() { (1)
return new Date().toLocalDateString('fr-FR');
}
}
1 | The only cloud function of the Calendar service |
Then, we have the HelloWorldWithDateService
that use our Calendar
service. It is defined in the file worker/index.ts
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Injectable } from '@zetapush/core';
import { Calendar } from './calendar'; (1)
@Injectable()
export default class HelloWorldWithDateService {
constructor( (2)
private calendar: Calendar
) {}
/**
* Return 'Hello World' with current date
*/
helloWorldWithDate() {
return `Hello world at ${this.calendar.getNow()}`;
}
}
1 | We import the Calendar service from the worker/calendar.ts file |
2 | calendar is an instance of Calendar and this.calendar is initialized with calendar value |
As for built-in cloud services, dependency injection works for your custom cloud services. Here it was an example
Private custom cloud service
In this example, we still have only one custom cloud service exposed. One Sometimes you may want to expose several custom cloud services. |
Shared module of custom cloud services
As you can see, a custom cloud service is no more than just a standard class with methods written
in TypeScript. If you want to develop reusable custom cloud services that could be
used in different applications, you can do it easily by following standards.
Actually, you can just create a npm module and import it like we do with You can also import any existing library that is available in the community. |
Add initialization code
ZetaPush manages scalability and redundancy of your workers. So there may have several workers that start at the same time. And if you initialize some data or configure some cloud services at the same time, it may have conflicts or duplicated data. To avoid that ZetaPush provides a way to initialize code that will ensure that is executed by only one worker.
For this, ZetaPush provides the bootstrap feature. To use it you need to implement the method onApplicationBootstrap()
in your custom cloud service.
Bad practice for initialization code
You may want to put your initialization code in the constructor but this is a bad practice.
Indeed, even if in the most cases, a custom cloud service is a singleton, it may have several instances of your custom cloud service and your initialization code will be called many times. An other drawback is that the constructor is synchronous and the |
In the following example we will create a worker to store data in a database. The initialization code let you create a table in our database. Then we will create methods to add and list our data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { Injectable, Bootstrappable } from '@zetapush/core';
import { GdaConfigurer, Gda, GdaDataType } from '@zetapush/platform-legacy';
const NAME_TABLE = 'table';
const NAME_COLUMN = 'column';
@Injectable()
export default class implements Bootstrappable { (1)
constructor(private gdaConfigurer: GdaConfigurer, private gda: Gda) {}
async onApplicationBootstrap() { (2)
this.gdaConfigurer.createTable({ (3)
name: NAME_TABLE,
columns: [{
name: NAME_COLUMN,
type: GdaDataType.STRING
}]
});
}
async addData(data: string) {
return await this.gda.put({
table: NAME_TABLE,
column: NAME_COLUMN,
data,
key: Date.now().toString()
});
}
async getData() {
return await this.gda.list({
table: NAME_TABLE
});
}
}
1 | We implement the Bootstrappable interface. This is optional (see below) |
2 | The onApplicationBootstrap() method is always asynchronous, the async keyword is optional |
3 | In our initialization code we create a table in our database |
Bootstrappable interface
The implementation of the |
You can implement the onApplicationBootstrap()
method in several custom cloud services.
A dependency tree will be created to execute all onApplicationBootstrap()
methods in the proper order (regarding of the needed dependencies).
For example in the following scheme, we have 2 exposed API named Dashboard and Mobile. For the case of the Dashboard API, it uses another service named Admin, that uses User Management and Stock Management services and so on.
So the services are initialized in a specific order :
-
Utils
-
User Data / Stock Data
-
User Management / Stock Management / Stock Market
-
Admin / Client API / Guest Access
-
Dashboard / Mobile

Advanced usage of cloud services
Several custom cloud services
Develop a cloud service that is not exposed
During the development of your custom cloud services you may want to not expose some of them. In this case, the affected custom cloud services are not reachable by the client.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Injectable } from '@zetapush/core';
/**
* This is a private cloud service
*/
class MyTimeManager { (1)
/**
* This is a private cloud function
*/
getCurrentTime() { (2)
return Date.now();
}
}
@Injectable() (3)
export default class {
constructor(private timeManager: MyTimeManager) {} (4)
hello() { (5)
return `Hello from Worker at ${this.timeManager.getCurrentTime()}`;
}
}
1 | Our private custom cloud service (no export keyword) |
2 | getCurrentTime() is not reachable by the client (front part of your application) |
3 | We specify that we want to use the dependency injection in our class |
4 | We ask the dependency injection to have an instance of MyTimeManager |
5 | hello() is the only method reachable by the client (front part of your application) |
Expose several cloud services
ZetaPush provides the possibility to expose several custom cloud services if you want. For example, you may have two custom cloud services that have same cloud function names. To differentiate them we provide namespaces
Namespaces
With the namespaces you can separate several custom cloud services. The result is that if a ZetaPush API created by a ZetaPush Client is configured to reach one namespace, the cloud functions exposed in the others namespaces will not be reachable with this API.
If we take the previous example, we may want to expose our two custom cloud services in two namespaces named time
and default
.
Example of several custom cloud services exposed
index.ts (worker)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Injectable, Module } from '@zetapush/core';
class MyTimeManager { (1)
getCurrentTime() {
return Date.now();
}
hello() {
return `Hello from MyTimeManager at ${this.getCurrentTime()}`;
}
}
@Injectable()
class MyDefaultApi { (2)
constructor(private timeManager: MyTimeManager) {}
hello() {
return `Hello from Worker at ${this.timeManager.getCurrentTime()}`;
}
}
@Module({ (3)
expose: {
timer: MyTimeManager,
default: MyDefaultApi
}
})
export default class {}
1 | A custom cloud service definition that defines hello cloud function |
2 | Another custom cloud service definition that also defines hello cloud function |
3 | We expose a class that is defined as a Module.
In the property expose we define in which namespace we expose each of our custom cloud services. By explicitly associating MyTimeManager to timer key, MyTimeManager cloud service is exposed through timer namespace. Same applies to MyDefaultApi that is exposed through default namespace |
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Several Custom Cloud Services</title>
</head>
<body>
<button class="js-Hello">call hello() in default namespace</button>
<button class="js-Timer">call hello() in timer namespace</button>
</body>
<script src="https://unpkg.com/@zetapush/client"></script>
<script src="https://unpkg.com/@zetapush/platform-legacy"></script>
<script src="./index.js"></script>
</html>
index.js (front)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// If you use npm you can do :
// import { WeakClient } = from '@zetapush/client';
// import { Service } = from '@zetapush/platform-legacy';
client = new ZetaPushClient.WeakClient();
// First api to reach 'default' namespace
const defaultApi = client.createProxyTaskService({ (1)
namespace: 'default'
});
// Second API to reach 'timer' namespace
const timerApi = client.createProxyTaskService({ (2)
namespace: 'timer'
});
// Handle the clicks
document.querySelector('.js-Hello').addEventListener('click', async () => {
console.log(await defaultApi.hello());
});
document.querySelector('.js-Timer').addEventListener('click', async () => {
console.log(await timerApi.hello());
});
// Connection
client
.connect()
.then(() => console.log('connected'))
.catch(() => console.error('connection failed'));
-
<1> We create an API that reach only the default
namespace
<2> We create an API that reach only the timer
namespace
Available cloud services
Standard User Workflow
Some information will soon be added about utilization of Standard User Workflow. For now, you can follow the dedicated developer manual section.
Technical cloud services
User Management
Simple
End-user API for the simple local authentication
These API verbs allow end-users to manage their account(s).
createUser
Creates a new account in this 'simple' authentication realm. Returns a map of account fields, including a field named <i>zetapushKey</i> containing the global user key of the user (value of the <b>__userKey</b> pseudo-constant when this new account will be used)
1
const response = await this.simple.createUser(parameters);
credentials
Returns the list of account credentials in this service for the asking user. Might return an empty list.
1
const response = await this.simple.credentials(parameters);
updateKey
Updates an existing account primary key (login, NOT <b>__userKey</b>) in this 'simple' authentication realm. The updated account MUST belong to the user making the call. The configured login field MUST be given, as a user (identified by his zetapush userKey) might possess several accounts. Returns a map of account fields
1
const response = await this.simple.updateKey(parameters);
updateUser
Updates an existing account in this 'simple' authentication realm. The updated account MUST belong to the user making the call. The configured login field MUST be given, as a user (identified by his zetapush userKey) might possess several accounts. Returns a map of account fields
1
const response = await this.simple.updateUser(parameters);
deleteUser
Deletes an existing account in this 'simple' authentication realm.
1
const response = await this.simple.deleteUser(parameters);
checkUser
Checks whether the given account already exists in this 'simple' authentication realm. This verb returns all the information about the user, including non public fields.
1
const response = await this.simple.checkUser(parameters);
requestReset
Requests a password reset for the given unique account key. The account key must exist and must be given, as it cannot obviously be deduced from the currently logged in user. The returned token needs to be sent to the intended recipient only. The typical use case is to define a macro that requests a reset, generates a email template and emails the user. The macro can then be safely called by a weakly authenticated user. Requesting a reset does not invalidate the password. Requesting a reset again invalidates previous reset requests (only the last token is usable)
1
const response = await this.simple.requestReset(parameters);
changePassword
Changes a user password for this authentication realm. The user can be either explicit, implicit (one of the current user’s accounts) or deduced from the token. You should provide at least one of 'key' and 'token'. If you do not, the server will try and find any key for the current user. The change is effective immediately. However, already logged in users might stay connected. The password and token fields are always null in the output.
1
const response = await this.simple.changePassword(parameters);
checkPassword
1
const response = await this.simple.checkPassword(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Simple, BasicAuthenticatedUser } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private simple: Simple (2)
) {}
async doStuff(parameters: BasicAuthenticatedUser) {
const response = await this.simple.createUser(parameters); (3)
return response;
}
}
1 | Import Simple from platform |
2 | Declare injected service |
3 | Call injected service |
Weak
User API for weak devices control
User API for control and release of weakly authenticated user sessions.
control
Takes control of a weak user session, identified by the given public token. The public token has been previously made available by the controlled device, for example by displaying a QRCode. Upon control notification, the client SDK of the controlled session is expected to re-handshake.
1
const response = await this.weak.control(parameters);
release
Releases control of a weak user session, identified by the given public token. The weak user session must have been previously controlled by a call to 'control'.
1
const response = await this.weak.release(parameters);
provision
Provisions an arbitrary number of accounts. The maximum number of accounts that you can create in one single call is configured per server.
1
const response = await this.weak.provision(parameters);
getToken
Returns your current session’s private token. The token field may be null, if you did not log in with this authentication. The token can be used to log in as the same weak user another time.
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Weak, UserControlRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private weak: Weak (2)
) {}
async doStuff(parameters: UserControlRequest) {
const response = await this.weak.control(parameters); (3)
return response;
}
}
1 | Import Weak from platform |
2 | Declare injected service |
3 | Call injected service |
Userdir
User API for user information
userInfo
1
const response = await this.userdir.userInfo(parameters);
search
1
const response = await this.userdir.search(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Userdir, UserInfoRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private userdir: Userdir (2)
) {}
async doStuff(parameters: UserInfoRequest) {
const response = await this.userdir.userInfo(parameters); (3)
return response;
}
}
1 | Import Userdir from platform |
2 | Declare injected service |
3 | Call injected service |
Groups
User API for groups and rights.
Groups are stored per user. This means that two users can own a group with the same identifier. A couple (owner, group) is needed to uniquely identify a group inside a group management service. The triplet (deploymentId, owner, group) is actually needed to fully qualify a group outside of the scope of this service.
grant
The granting API does not do any check when storing permissions. In particular when granting rights on a verb and resource of another API, the existence of said verb and resource is not checked.
createGroup
Creates a group owned by the current user. Group creation may fail if the group already exists.
listOwnedGroups
Returns the whole list of groups owned by the current user
1
const response = await this.groups.listOwnedGroups(parameters);
listDetailedOwnedGroups
Returns the whole list of groups owned by the current user, with their members
1
const response = await this.groups.listDetailedOwnedGroups(parameters);
myGroups
Returns the whole list of groups the current user is part of. Groups may be owned by anyone, including the current user.
1
const response = await this.groups.myGroups(parameters);
listJoinedGroups
Returns the whole list of groups the current user is part of. Groups may be owned by anyone, including the current user.
1
const response = await this.groups.listJoinedGroups(parameters);
delGroup
Removes the given group owned by the current user or the given owner. Also removes all grants to that group.
1
const response = await this.groups.delGroup(parameters);
addMe
Adds me (the caller) to a group. This verb exists so that group owners may grant the right to join their groups without granting the right to add other users to those groups. The 'user' field is implicitly set to the current user’s key.
memberOf
Tests whether I (the caller) am a member of the given group. This verb exists so that users can determine if they are part of a group without being granted particular rights. The 'user' field is implicitly set to the current user’s key.
1
const response = await this.groups.memberOf(parameters);
addUsers
Users are processed in the given order In case of failure in the middle of a user list, this verb may have succeeded to add the first users, but will not continue processing the end of the list.
1
const response = await this.groups.addUsers(parameters);
delUsers
1
const response = await this.groups.delUsers(parameters);
listGrants
This API lists explicitly configured rights. Effective rights include configured rights, implicit rights and inherited rights.
1
const response = await this.groups.listGrants(parameters);
listGroupGrants
This API lists explicitly configured rights. Effective rights include configured rights, implicit rights and inherited rights.
1
const response = await this.groups.listGroupGrants(parameters);
listPresences
Returns the list of members of the given groups, along with their actual and current presence on the zetapush server. The current implementation does not include information about the particular devices users are connected with. If a user is connected twice with two different devices, two identical entries will be returned.
1
const response = await this.groups.listPresences(parameters);
listGroupPresences
Returns the list of members of the given groups, along with their actual and current presence on the zetapush server. The current implementation does not include information about the particular devices users are connected with. If a user is connected twice with two different devices, two identical entries will be returned.
1
const response = await this.groups.listGroupPresences(parameters);
addUser
Adds the given user to the given group. Addition may fail if the given group does not already exist.
groupUsers
Returns the whole list of users configured inside the given group.
1
const response = await this.groups.groupUsers(parameters);
allGroups
Returns the whole list of groups owned by the current user, with their members
1
const response = await this.groups.allGroups(parameters);
groups
Returns the whole list of groups owned by the current user
1
const response = await this.groups.groups(parameters);
check
This API checks if the given user has the proper authorizations to perform the given action on the owner’s resource. If you give the same value for 'user' and 'owner', the check always passes.
1
const response = await this.groups.check(parameters);
exists
Returns whether a group exists or not.
1
const response = await this.groups.exists(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Groups, Grant } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private groups: Groups (2)
) {}
async doStuff(parameters: Grant) {
const response = await this.groups.grant(parameters); (3)
return response;
}
}
1 | Import Groups from platform |
2 | Declare injected service |
3 | Call injected service |
Data Management
Gda
GDA User API
User API for Generic Data Access. The data are stored on a per-user basis. Users can put, get, list their data.
puts
Creates or replaces the (maybe partial) contents of a collection of rows. This method only creates or replaces cells for non-null input values.
1
const response = await this.gda.puts(parameters);
inc
Increments a cell 64-bit signed integer value and returns the result in the data field. The increment is atomic : if you concurrently increment 10 times a value by 1, the final result will be the initial value plus 10. The actual individual resulting values seen by the 10 concurrent callers may vary discontinuously, with duplicates : at least one of them will see the final (+10) result.
removeRow
Removes all columns of the given row from the given table.
1
const response = await this.gda.removeRow(parameters);
removeColumn
Removes all cells of the given column of the given row from the given table.
1
const response = await this.gda.removeColumn(parameters);
removeCell
Removes only one cell of the given column of the given row from the given table.
1
const response = await this.gda.removeCell(parameters);
mget
Returns full data rows, in the order they were asked.
1
const response = await this.gda.mget(parameters);
getCells
Returns a precise list of cells from a column in a data row.
1
const response = await this.gda.getCells(parameters);
get
Returns a full data row.
1
const response = await this.gda.get(parameters);
list
Returns a paginated list of rows from the given table.
1
const response = await this.gda.list(parameters);
removeRange
Removes the specified columns of the given range of rows from the given table.
1
const response = await this.gda.removeRange(parameters);
filter
Similar to range, but rows can be filtered out according to a developer-supplied predicate. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.
1
const response = await this.gda.filter(parameters);
range
Returns a paginated range of rows from the given table. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.
1
const response = await this.gda.range(parameters);
reduce
Returns a computed single reduced result from a range of rows from the given table. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.
1
const response = await this.gda.reduce(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Gda, GdaPuts } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private gda: Gda (2)
) {}
async doStuff(parameters: GdaPuts) {
const response = await this.gda.puts(parameters); (3)
return response;
}
}
1 | Import Gda from platform |
2 | Declare injected service |
3 | Call injected service |
Stack
Data stack user API
Data is stored on a per user basis. However, notifications can be sent to a configurable set of listeners. Stack names are arbitrary and do not need to be explicitly initialized.
purge
Removes all items from the given stack.
1
const response = await this.stack.purge(parameters);
getListeners
Returns the whole list of listeners for the given stack.
1
const response = await this.stack.getListeners(parameters);
setListeners
Sets the listeners for the given stack.
1
const response = await this.stack.setListeners(parameters);
remove
Removes the item with the given guid from the given stack.
1
const response = await this.stack.remove(parameters);
update
Updates an existing item of the given stack. The item MUST exist prior to the call.
1
const response = await this.stack.update(parameters);
push
Pushes an item onto the given stack. The stack does not need to be created.
1
const response = await this.stack.push(parameters);
list
Returns a paginated list of contents for the given stack. Content is sorted according to the statically configured order.
1
const response = await this.stack.list(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Stack, StackRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private stack: Stack (2)
) {}
async doStuff(parameters: StackRequest) {
const response = await this.stack.purge(parameters); (3)
return response;
}
}
1 | Import Stack from platform |
2 | Declare injected service |
3 | Call injected service |
Search
ElasticSearch Service
This API is a very thin wrapper around ElasticSearch’s API.
index
Inserts or updates a document into the elasticsearch engine.
1
const response = await this.search.index(parameters);
get
Retrieves a document from the elasticsearch engine by id.
1
const response = await this.search.get(parameters);
delete
Deletes a document from the elasticsearch engine by id.
1
const response = await this.search.delete(parameters);
search
1
const response = await this.search.search(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Search, SearchData } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private search: Search (2)
) {}
async doStuff(parameters: SearchData) {
const response = await this.search.index(parameters); (3)
return response;
}
}
1 | Import Search from platform |
2 | Declare injected service |
3 | Call injected service |
File Management
Zpfs_hdfs
User API for local file management
User API for file content manipulation User API for virtual file management and http file upload This API contains all the verbs needed to browse, upload and remove files. Files are stored on a per-user basis: each user has his or her own whole virtual filesystem. Uploading a file is a 3-step process : request an upload URL, upload via HTTP, notify this service of completion.
readToken
Requests a token. This token can be used to retrieve a compressed folder via HTTP.
1
const response = await this.zpfs_hdfs.readToken(parameters);
create
Creates a file, writes content and closes the file as a single operation. Calling this verb is functionnally equivalent to successively calling 'newUploadUrl', posting the content and calling 'newFile'
1
const response = await this.zpfs_hdfs.create(parameters);
open
Opens a file for reading.
1
const response = await this.zpfs_hdfs.open(parameters);
link
Links a file or folder to another location. May fail if the target location is not empty.
1
const response = await this.zpfs_hdfs.link(parameters);
stat
Returns information about a single file. The entry field will be null if the path does not exist
1
const response = await this.zpfs_hdfs.stat(parameters);
cp
Copies a file or folder (recursively) to a new location. May fail if the target location is not empty.
1
const response = await this.zpfs_hdfs.cp(parameters);
ls
Returns a paginated list of the folder’s content.
1
const response = await this.zpfs_hdfs.ls(parameters);
mv
Moves a file or folder (recursively) to a new location. May fail if the target location is not empty.
1
const response = await this.zpfs_hdfs.mv(parameters);
snapshot
Creates a new folder and then copies the given files inside
1
const response = await this.zpfs_hdfs.snapshot(parameters);
du
Returns an recursively aggregated number of used bytes, starting at the given path.
1
const response = await this.zpfs_hdfs.du(parameters);
rm
Removes a file or folder (recursively).
1
const response = await this.zpfs_hdfs.rm(parameters);
freeUploadUrl
1
const response = await this.zpfs_hdfs.freeUploadUrl(parameters);
newUploadUrl
Requests an HTTP upload URL. The URL contains temporary credentials (typically valid for a few minutes) and is meant for immediate use.
1
const response = await this.zpfs_hdfs.newUploadUrl(parameters);
updateMeta
1
const response = await this.zpfs_hdfs.updateMeta(parameters);
newFile
The client application calls this verb to notify that it’s done uploading to the cloud. Calling that verb MAY trigger additional events such as thumbnail/metadata creation.
1
const response = await this.zpfs_hdfs.newFile(parameters);
mkdir
Creates a new folder. May fail if the target location is not empty.
1
const response = await this.zpfs_hdfs.mkdir(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Zpfs_hdfs, ZpfsRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private zpfs_hdfs: Zpfs_hdfs (2)
) {}
async doStuff(parameters: ZpfsRequest) {
const response = await this.zpfs_hdfs.readToken(parameters); (3)
return response;
}
}
1 | Import Zpfs_hdfs from platform |
2 | Declare injected service |
3 | Call injected service |
Template
User API for templates
Users use this API to evaluate pre-configured templates.
evaluate
Evaluates the given template and returns the result as a string. Templates are parsed the first time they are evaluated. Evaluation may fail early due to a parsing error.
1
const response = await this.template.evaluate(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Template, TemplateRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private template: Template (2)
) {}
async doStuff(parameters: TemplateRequest) {
const response = await this.template.evaluate(parameters); (3)
return response;
}
}
1 | Import Template from platform |
2 | Declare injected service |
3 | Call injected service |
Communication
Messaging
Messaging service
Simple and flexible user-to-user or user-to-group messaging service.
send
Sends the given message to the specified target on the given (optional) channel. The administratively given default channel name is used when none is provided in the message itself.
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Messaging, Message } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private messaging: Messaging (2)
) {}
async doStuff(parameters: Message) {
const response = await this.messaging.send(parameters); (3)
return response;
}
}
1 | Import Messaging from platform |
2 | Declare injected service |
3 | Call injected service |
Notif
Notification User API
User API for notifications. For notifications to work properly, it is imperative that the resource name of a device remain constant over time.
send
Sends a native push notification to the target.
1
const response = await this.notif.send(parameters);
unregister
Unregisters the device for the current user and resource. This verb does not need any parameters.
register
Registers the device for the current user and resource. This service maintains a mapping of userkey/resource to device registration IDs. You MUST NOT re-use the same resource name from one device to another if you want to target specific devices with 'send'. Only one registration can be active for a given userKey/resource pair in a notification service. Device registration can be <b>neither impersonated nor called indirectly</b> (from a scheduled job).
1
const response = await this.notif.register(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Notif, NotificationMessage } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private notif: Notif (2)
) {}
async doStuff(parameters: NotificationMessage) {
const response = await this.notif.send(parameters); (3)
return response;
}
}
1 | Import Notif from platform |
2 | Declare injected service |
3 | Call injected service |
Sendmail
Mail service user API
This service is statically configured with an outgoing SMTP server. Users call the API here to actually send emails.
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Sendmail, Email } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private sendmail: Sendmail (2)
) {}
async doStuff(parameters: Email) {
const response = await this.sendmail.send(parameters); (3)
return response;
}
}
1 | Import Sendmail from platform |
2 | Declare injected service |
3 | Call injected service |
Sms_ovh
SMS service
User API for SMS.
send
Sends the given message to the given recipients.
1
const response = await this.sms_ovh.send(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Sms_ovh, SmsMessage } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private sms_ovh: Sms_ovh (2)
) {}
async doStuff(parameters: SmsMessage) {
const response = await this.sms_ovh.send(parameters); (3)
return response;
}
}
1 | Import Sms_ovh from platform |
2 | Declare injected service |
3 | Call injected service |
Utilities Management
Cron
User API for the Scheduler
User endpoints for scheduling : users can schedule, list and delete tasks. Tasks are stored on a per-user basis: a task will run with the priviledges of the user who stored it. Tasks are run on the server and thus can call api verbs marked as server-only.
schedule
Schedules a task for later execution. Tasks are executed asynchronously with the identity of the calling user. Tasks will be executed at a fixed moment in time in the future, or repeatedly, with minute precision. If a task already exists with the same cronName (a cronName is unique for a given user), this new task completely replaces it. A task can be scheduled with a cron-like syntax for repetitive or one-shot execution. Wildcards are not allowed for minutes and hours. When scheduling for one-shot execution, the time must be at least two minutes into the future.
1
const response = await this.cron.schedule(parameters);
setTimeout
Schedules a task for later execution. Tasks are executed asynchronously with the identity of the calling user. Tasks will be executed with second precision in the near future (120 seconds delay max).
1
const response = await this.cron.setTimeout(parameters);
unschedule
Removes a previously scheduled task. Does absolutely nothing if asked to remove a non-existent task.
1
const response = await this.cron.unschedule(parameters);
list
Returns a paginated list of the asking user’s tasks.
1
const response = await this.cron.list(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Cron, CronTaskRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private cron: Cron (2)
) {}
async doStuff(parameters: CronTaskRequest) {
const response = await this.cron.schedule(parameters); (3)
return response;
}
}
1 | Import Cron from platform |
2 | Declare injected service |
3 | Call injected service |
Logs
Log API
User API for logging.
log
Adds some server generated data and stores the entry into the sink defined by configuration.
1
const response = await this.logs.log(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Logs, LogRequest } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private logs: Logs (2)
) {}
async doStuff(parameters: LogRequest) {
const response = await this.logs.log(parameters); (3)
return response;
}
}
1 | Import Logs from platform |
2 | Declare injected service |
3 | Call injected service |
Trigger
Trigger service
Register listeners and trigger events.
trigger
Triggers an event. All listeners previously registered for that event will be called, in no particular order.
1
const response = await this.trigger.trigger(parameters);
How to use it ?
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Trigger, EventTrigger } from '@zetapush/platform-legacy'; (1)
@Injectable()
export default class Api {
constructor(
private trigger: Trigger (2)
) {}
async doStuff(parameters: EventTrigger) {
const response = await this.trigger.trigger(parameters); (3)
return response;
}
}
1 | Import Trigger from platform |
2 | Declare injected service |
3 | Call injected service |
Configuration
ZetaPush prefers convention over configuration. However, conventions can be overriden.
Account configuration
To use ZetaPush
, you need to have a ZetaPush account and a ZetaPush application.
If you don’t have any of them, you can signup for a free trial or contact us for more suitable account.
Then, you can set your account configuration in the .zetarc
file.
This file is present at the root of your application folder.
Here is the list of properties that can be used. For each of them, you also have the associated cli flag and environment variable.
Name of the property | Definition | environment variable | Name in the .zetarc | CLI parameter |
---|---|---|---|---|
Developer login |
This login is the email used by the developer when logging in to the ZetaPush console. It does not concern the applications developed by the developer. It is only used to identify the developer account. |
ZP_DEVELOPER_LOGIN |
developerLogin |
--developer-login <login> |
Developer password |
Like the property |
ZP_DEVELOPER_PASSWORD |
developerLogin |
--developer-password <password> |
Application name |
A developer can develop many applications. Hence he needs to identify each of them. The |
ZP_APP_NAME |
appName |
--app-name <name> |
Platform URL |
By default, the developer uses the public ZetaPush cloud to develop and deploy its applications. In most cases, the developer doesn’t need to set this property. This property if useful when another (on premise, or dedicated) ZetaPush cloud is to be used. |
ZP_PLATFORM_URL |
platformUrl |
--platform-url <platform> |
Code structure configuration
It is possible to configure the structure of your application. In this part, we list the different possibilities.
Entry point for worker
By default, the entry point of your worker code is the file ./worker/index.ts
.
You can configure the entry point of the worker setting the main
property of the package.json
file.
For example if you want to use the file ./worker/main.ts
as entry point you need to have the following package.json
content :
1
2
3
4
5
6
{
"name": "myApp",
"version": "0.1.0",
"main": "worker/main.ts", (1)
// Other properties...
}
1 | Path of the file used as entry point |
Detail about path of entry point
For now, the entry point of your worker need to be in the |
Custom directory structure
This section will be useful if you want to change the structure of your application. The most common case is when you want to use framework (Angular, React, VueJS for example). This documentation will soon be ready.
Script alias
Npm provides a handy feature to run scripts provided by dependencies without needing to change your computer settings or install the tool. ZetaPush defines npm script aliases to run the ZetaPush CLI through npm.
Configuration properties
ZetaPush provides the application.json
file to let you configure the behavior of your application.
For now, the application.json
file is mainly used for the Standard User Workflow but you can also configure your own properties for your application.
The list of available properties for the Standard User Workflow are listed in this section.
New properties will added
New Cloud Services will be developed (to handle data, security, etc…). With them, some properties may be added and configurable through the application.json file. |
Future features
New features about
|
You will soon can add your own configuration property source. For example download properties from an external service or from a Git repository. (You will also be able to configure the priority of the source).
Configuration override
You can put many properties in several locations (environment variables, CLI…). ZetaPush includes and overrides this properties in a specific order:
Customize cloud services
User Management
Some properties are available in the application.json
file to configure the Standard User Workflow. We will list all of them in this section:
Organization
Name of your organization. It is used in the default email sender (noreply@${organizationName}.com
).
{
"organization": {
"name": "ZetaPush"
}
}
Optional property
This property is optional. If it is not configured, you need to configure an email sender. |
Let you configure the sender of the different emails used in your application. For example for account registration.
Default email sender
The default email sender is |
This property support the standard notation for email (name <email@domain.com>
):
{
"email": {
"sender": "Sender Name <sender@my-domain.com>" (1)
}
}
1 | You can also only type the email (ex: "sender@my-domain.com") |
Account registration
Account confirmation
During the account creation workflow of the StandardUserWorkflow, an email is sent to ask the end-user to confirm his email. You can configure many properties about this.
You can set the email subject of the confirmation account creation like below:
{
"registration": {
"confirmation": {
"email": {
"subject": "Please confirm your account registration"
}
}
}
}
Mandatory property
For now, this is a mandatory property. In a future version, the subject could be extracted from the email template. This allows you to define a single source of information about the sent email and moreover, it simplifies internationalization of your email. |
When an end-user validates his account, he is redirected to a specific URL of your application. There are 2 cases, if the confirmation is OK or if it failed:
{
"registration": {
"confirmation": {
"success-url": "https://my-app-url/#login", (1)
"failure-url": "https://my-app-url/#confirmation-failed" (2)
}
}
}
1 | The end-user is redirected to this URL when the confirmation is OK |
2 | The end-user is redirected to this URL when the confirmation failed |
You can also only set the path of your urls. The base url will be automatically provided by ZetaPush:
{
"registration": {
"confirmation": {
"success-url": "#login",
"failure-url": "#confirmation-failed"
}
}
}
Mandatory property
This property is mandatory. |
Reset password workflow
It is possible to configure the path of your application where the end-user will be able to choose his new password.
{
"reset-password": {
"ask": {
"url": "http://localhost:3000/reset-password#${token}", (1)
}
}
}
1 | The token is sent through the URL. To use it you need to put ${token} in your URL. |
You can also only set the path of your url. The base url will be automatically provided by ZetaPush:
{
"reset-password": {
"ask": {
"url": "/reset-password#${token}", (1)
}
}
}
Mandatory property
This property is mandatory. |
You can configure the subject of the sent email to reset the password:
{
"reset-password": {
"ask": {
"email": {
"subject": "Please choose a new password"
}
}
}
}
Mandatory property
For now, this is a mandatory property. In a future version, the subject could be extracted from the email template. This allows you to define a single source of information about the sent email and moreover, it simplifies internationalization of your email. |
Email provider configuration
To use the StandardUserWorkflow you need to configure an email provider. Today, you can use two possibilities: Mailjet or SMTP.
Future implementations
In future versions, other implementatins will be provided as-is. Moreover, you will be able to replace email sending by SMS or anything else. |
Mandatory property
You need to set at least one email provider configuration and you need to have only one email provider configuration enabled. |
Mailjet configuration
To use the Mailjet configuration you need to configure the application.json
file like this:
{
"mailjet": {
"apikey-public": "my-mailjet-apikey-public", (1)
"apikey-private": "my-mailjet-apikey-private",
"enable": "true", (2)
"url": "https://api.mailjet.com/v3.1/send" (3)
}
}
1 | The apikey-public and apikey-private are provided by Mailjet and you can find them in your Mailjet account information. |
2 | Optional property to define that we use this email provider configuration. (The default value is true if you have configured apikey-public and apikey-private ) |
3 | Optional property to define the URL to use for your Mailjet email provider. By default the used URL is https://api.mailjet.com/v3.1/send . |
SMTP configuration
To use the SMTP configuration you need to configure the application.json
file like this:
{
"smtp": {
"host": "my-smtp-host",
"port": "465", (1)
"username": "my-smtp-username",
"password": "my-smtp-password",
"ssl": true, (2)
"starttls": true, (2)
"enable": true (3)
}
}
1 | Optional, deducted from SSL and starttls |
2 | Optional, default to true |
3 | Optional property to define that we use this email provider configuration. (The default value is true if you have configured at least host and port) |
Auto implementation about properties
The implementation of the StandardUserWorkflow can change about the presence or not of particular properties.
For example, if you configure an email sender, the default email sender using the organization name will be overriden.
A most common case is for email provider configuration. If you change your STMP configuration to a Mailjet configuration (for example), the implementation will be automatically changed.
Logs
To configure the log level of your application you use the CLI or from the web console (Not ready yet).
To do this via the CLI, you need to launch the zeta config
command (See more details in this section).
$ zeta config --get
Today, there are only two available log levels:
-
verbose
-
default
If you want leaner log level, you can contact us.
$ zeta config --logs verbose
$ zeta config --logs default
Default verbose level
The default verbose level traces only errors. |
Console.log
It will soon be possible to see your console.log()
in the web console.
Utility methods provided by @zetapush/common
The utility methods from @zetapush/common
for Logs will soon be documented.
Built-in log cloud service
ZetaPush provides a Log service. For now, you can use the requestContext object for more convenience.
Logs using requestContext
The requestContext object is an abstraction of the logs service.
The dedicated section explains what is the requestContext and how to use it. So in this section we will see a practical utilization of requestContext.
Set correct log level
To be sure to see all logs, you can put the log level to |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import {
Injectable,
RequestContext,
RequestContextAware,
Bootstrappable
} from "@zetapush/core";
import { Gda, GdaConfigurer, GdaDataType } from "@zetapush/platform-legacy";
@Injectable()
export default class Api implements RequestContextAware, Bootstrappable { (1)
requestContext!: RequestContext; (2)
constructor(private gda: Gda, private gdaConfigurer: GdaConfigurer) {}
async onApplicationBootstrap() { (3)
await this.gdaConfigurer.createTable({
name: "table",
columns: [
{
name: "column",
type: GdaDataType.STRING
}
]
});
}
async addToDatabase(data: string) {
this.requestContext.logger.info(`[addToDatabase] - Add ${data} info`); (4)
return await this.gda.put({
key: Date.now().toString(),
table: "table",
column: "column",
data
});
}
async getAllData() {
this.requestContext.logger.info(`[getAllData] - Get all data`); (5)
try {
const { result: resultFromDataBase } = await this.gda.list({
table: "table"
});
if (resultFromDataBase && resultFromDataBase.content) {
this.requestContext.logger.info( (6)
`[getAllData] - content : ${resultFromDataBase.content}`
);
return resultFromDataBase.content;
} else {
this.requestContext.logger.warn( (7)
"[getAllData] - No content in database"
);
}
} catch (err) {
this.requestContext.logger.error( (8)
"[getAllData] - Failed to access to database"
);
}
}
}
1 | Optional implementation of RequestContextAware |
2 | Injected property of requestContext |
3 | Creation of table for database |
4 | Log in info when we add info in database |
5 | Log in info when we try to get data |
6 | Log in info if content exists |
7 | Log in warn if no content in database |
8 | Log in error if it failed to access to database |
The result in the database if the following:

More Log levels
If you need to have more log levels, you can contact us. |
View logs in web console
The utilization of logs in the web console will soon be documented.
Tests
HTTP server
ZetaPush provides you the possibility to add a HTTP server in your application to handle HTTP requests. A HTTP server is already used in one Cloud Service, the Standard User Workflow (To handle confirmation account registration for example).
You have two possibilities:
Custom HTTP server
ZetaPush provides you the possibility to create your own HTTP server.
Here is an example using the express
library:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import express from 'express'; (1)
export default class {
onApplicationBootstrap() { (2)
const app = express();
const port = process.env.HTTP_PORT || 3001; (3)
app.get('/', (req: any, res: any) => { (4)
res.send(`Hello World from ${port}`);
});
app.listen(port, () => { (5)
console.log(`HTTP server is listening on ${port}`);
});
}
}
1 | In this case we use the express library |
2 | To initialize our application we use the onApplicationBootstrap() method |
3 | The HTTP_PORT environment variable is provided to use a custom HTTP server when the application is running on the ZetaPush cloud. Locally we use the port 3001 (in this case, you can use any port). |
4 | We handle the request |
5 | We launch the HTTP server |
HTTP_PORT environment variable
The |
When your application is running on the ZetaPush cloud, you can access to HTTP server via the URL given at the end of the deployment:
[INFO] Bundle your application components
[INFO] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ Upload code
[INFO] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ Prepare environment
[INFO] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ Publish web application
[INFO] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ Preparing custom cloud services for deployment
[INFO] ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ Publish custom cloud service on worker instance
[INFO] Web application front is available at http://front-1ceugbp7ci.celtia.zetapush.app:80/
[INFO] Worker application queue_0 is available at http://queuea0-1ceugbp7ci.test.zetapush.app (1)
[INFO] Worker applications works only if you listen process.env.HTTP_PORT
1 | URL of your HTTP server |
You can use your http server library like you would do without ZetaPush.