Cloud service
Calljmp provides a cloud service that allows mobile developers to create, deploy, and manage backend services on the cloud. This documentation will guide you through the process of developing your cloud service locally, managing secrets, working with the database, deploying the service, and calling it from your mobile app.
Getting started
To get started with the Calljmp cloud service, you need to install the Calljmp CLI. The CLI is a command-line tool that allows you to create, deploy, and manage your cloud services. You can install the CLI using npm:
npm install -g @calljmp/cli
Learn more about setup and usage of the CLI in the CLI documentation.
Create a service
Calljmp allows you to write cloud functions that can be triggered by your mobile app or other backend systems. Here’s an example of creating a basic cloud function to fetch user data.
import { Service } from './service'
const service = Service()
service.get('/hello', async c => {
return c.json({
message: 'Hello, world!',
})
})
export default service
Develop locally
To run your service locally, you can use the Calljmp CLI. The CLI provides a command to start your service in development mode. This allows you to test your service locally before deploying it to the cloud.
- Name
project
- Type
- string
- Default value
- .
- Description
Project directory. The property can be set using the
CALLJMP_PROJECT
environment variable.
- Name
module
- Type
- string
- Default value
- ./src/service
- Description
Path to the module directory containing your service code in
main.ts
. The property can be set using theCALLJMP_MODULE
environment variable.
- Name
port
- Type
- number
- Default value
- 8787
- Description
Port to run the service on.
calljmp start
After running the command, you should see output indicating that your service is running locally.
[calljmp] Ready on http://0.0.0.0:8787
[calljmp] - http://127.0.0.1:8787
[calljmp] - http://192.168.0.0:8787
[calljmp] Press Ctrl+C to stop the server
Test the service
You can test the function by sending a GET request to the /hello
endpoint. You can use a tool like Postman or curl to test the endpoint.
curl -X GET http://127.0.0.1:8787/hello
The response should be a JSON object with a message:
{
"message": "Hello, world!"
}
Automatic reload
When you run your service locally, you can make changes to your code and see the results immediately. The Calljmp CLI provides a development mode that automatically reloads your service when you make changes to the code. This allows you to develop and test your service without needing to restart it manually.
After making changes to your code, you should see output indicating that the service has been reloaded:
[calljmp] Detected changes, restarting...
[calljmp] Ready on http://127.0.0.1:8787
[calljmp] Press Ctrl+C to stop the server
Deployment
Once your cloud service is ready and fully tested locally, you can deploy it to the Calljmp cloud.
To deploy your service to the cloud, run the following command:
calljmp deploy
This will deploy your local service, environment variables, to the cloud. Once deployed, you can access your service using the URL provided in the deployment output.
✔ Build completed
✔ Deployment completed
[calljmp] Service deployed with UUID: 3b6fa7dc-b458-4855-bf76-d034c7576693
[calljmp] Access your service at: https://api.calljmp.com/target/v1/service/3b6fa7dc-b458-4855-bf76-d034c7576693
It is important to take a note of the UUID, as it is used to identify your service in the Calljmp cloud.
Environment variables
Environment variables are used to store sensitive information, such as API keys and database connection strings, that should not be hardcoded in your service code. Calljmp provides two ways to manage environment variables: variables and secrets. Secrets are encrypted and stored securely, while variables are stored in plain text.
Environment variables are managed in the .env
and .service.env
files. You can add your environment variables there, and they will be available in your code.
CALLJMP_SOME_TOKEN = "not a secret token"
CALLJMP_SECRET_ENCRYPTED_TOKEN = "encrypted secret token"
To add a secret prepend SECRET_
to a variable name. This will encrypt the variable and make it available in your code without the prefix.
Upon each deployment, the variables and secrets are automatically added to the cloud service. Only the variables are synchronized with the cloud service. The secrets are added only if they are not already present in the cloud service. If you want to delete a secret, you can do so using the CLI or the dashboard.
Code generation
Environment variables are automatically loaded from local files. But in order to use them in your code, you need to generate a file with the types of the variables:
calljmp generate
import { Service } from './service'
const service = Service()
service.get('/hello', async c => {
return c.json({
development: c.env.DEVELOPMENT,
variables: c.env.SOME_TOKEN,
secrets: c.env.ENCRYPTED_TOKEN,
})
})
export default service
Security and trust
The service can be called from any client, including mobile apps and web apps. To ensure that only authorized clients can call your service, you can use arguments that are accessible through incoming http request.
- Name
trusted
- Type
- boolean
- Description
Indicates if the request comes from a client that has passed the integrity check. Set to
true
when Calljmp has verified the client's integrity. Note thatfalse
doesn't necessarily mean the client is malicious - it may be that the integrity check hasn't completed yet or the user isn't authenticated. Always verify this flag for sensitive operations.
- Name
userId
- Type
- string | undefined
- Description
The user ID of the authenticated user. This is set to the user ID of the authenticated user, or
undefined
if the user isn't authenticated yet.
- Name
platform
- Type
- Platform
- Description
Identifies the client platform (e.g., iOS, Android). This enum value indicates which platform is making the request.
- Name
serviceId
- Type
- string
- Description
A unique identifier for your service. This string helps identify which specific service is being accessed.
import { Service } from './service'
const service = Service()
service.get('/hello', async c => {
if (!c.var.trusted) {
// Calljmp was not able to confirm the device and app integrity when the service was called.
return c.json({ error: 'Unauthorized' }, 401)
}
if (!c.var.userId) {
// User is not authenticated.
return c.json({ error: 'Unathenticated' }, 401)
}
// User is authenticated and the device and app integrity is confirmed.
return c.json({
variables: c.env.SOME_TOKEN,
secrets: c.env.ENCRYPTED_TOKEN,
})
})
export default service
Database
Calljmp provides a database that is automatically created and managed for you. You can use the database to store and retrieve data from your service. The database is a SQLite database that is optimized for performance and scalability.
import { Service } from './service'
const service = Service()
service.get('/', async c => {
const result = await c.env.db.prepare('PRAGMA table_info(users)').run()
return c.json(result)
})
export default service
You can also integrate custom ORMs or wrapper libraries to enhance your database experience. For example, you can use Kysely or Drizzle to create a type-safe database layer on top of the Calljmp SQLite database. You find more options in the Cloudflare D1 community projects.
Synchronizing database
Calljmp provides a built-in database synchronization feature that allows you to synchronize your local database with the cloud database. This is useful for testing and development purposes.
To synchronize your local database with the cloud database, you can reset your local database and pull the cloud database to your local machine. This will ensure that your local database is in sync with the cloud database.
calljmp reset
The reset
command will delete your local database and create a new one. This
is useful for testing and development purposes.
- Name
project
- Type
- string
- Default value
- .
- Description
Project directory. The property can be set using the
CALLJMP_PROJECT
environment variable.
- Name
migrations-table
- Type
- string
- Description
Name of the table used to store the migration history. Useful if you are using ORM library that manages migrations for you.
- Name
table-data
- Type
- string
- Description
Name of table to pull data from. It can be specified multiple times to pull from multiple tables.
calljmp pull
Migrations
Calljmp provides a built-in migration system that allows you to manage your database schema changes. You can create and run migrations using the Calljmp CLI.
Create a new migration .sql
file in the migrations
directory:
mkdir -p ./src/service/migrations
touch ./src/service/migrations/0001-create-products.sql
You can modify migrations folder structure to your liking. The only requirement is that the migration files must be named in the format 0001-create-products.sql
, where 0001
is the migration number and create-products
is a descriptive name for the migration.
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL
);
To run the migration, you can use the Calljmp CLI:
- Name
project
- Type
- string
- Default value
- .
- Description
Project directory. The property can be set using the
CALLJMP_PROJECT
environment variable.
- Name
migrations
- Type
- string
- Default value
- ./src/service/migrations
- Description
Path to the migrations directory. The property can be set using the
CALLJMP_MIGRATIONS
environment variable.
- Name
remote
- Type
- boolean
- Default value
- false
- Description
Indicates if the migration should be run on the remote service.
calljmp migrate
By default migrations are run locally. To run them on the remote service, use
the --remote
flag. If you like to build fresh local database similar to the
remote service, you can use combination of reset
, pull
, and migrate
commands.
Barebones
Calljmp uses the Hono framework to build cloud services. Hono is a fast and lightweight web framework for building APIs and microservices. It provides a simple and intuitive API for defining routes, middleware, and error handling. However, if you prefer to use another framework or no framework at all, you can do so by creating a custom service with fetch
listener.
First regenerate the code to remove the Hono types:
calljmp generate --no-hono
Then create a custom service similar to the one above:
import { ServiceArgs, Env } from './service'
export default {
async fetch(request: Request, env: Env) {
// Arguments are only available in production.
const { args } = ServiceArgs.tryFrom(request)
const url = new URL(request.url)
const path = url.pathname
// Simulate a service response
if (path === '/hello') {
if (!args || !args.trusted) {
// Calljmp was not able to confirm the device and app integrity when the service was called.
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
})
}
if (!args || !args.userId) {
// User is not authenticated.
return new Response(JSON.stringify({ error: 'Unathenticated' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
})
}
// User is authenticated and the device and app integrity is confirmed.
return new Response(
JSON.stringify({
variables: env.SOME_TOKEN,
secrets: env.ENCRYPTED_TOKEN,
}),
{
headers: { 'Content-Type': 'application/json' },
}
)
}
// Fallback for other paths
return new Response('Not Found', { status: 404 })
},
}