As our new infrastructure nears completion, here's a small introduction to some of the main changes.
Not everything has changed. Our schema and query language are exactly the same.
A new version of the @based/client
accompanies this release. Please check npm for what the latest is, it changes often.
The API of the client has changed compared to the previous version, but the core concepts remain the same:
import { BasedClient } from '@based/client'
const based = new BasedClient({
cluster: 'production',
org: 'your-organization',
project: 'your-project',
env: 'your-environment',
})
This will create a client object and connect to your environment. The cluster
field is optional and will default to production
, but during development you might be instructed to connect to a different cluster.
const id = await client.call('db:set', {
type: 'user',
name: 'hello',
})
To invoke a based server-function, you use the .call
method. This method takes the name of the function as first argument, and optionally a payload as second argument.
This is the method used to execute functions of type function
. See later for other types of functions available.
const data = await based.query('db', queryObject).get()
const specificData = await based
.query('db', queryObject)
.getWhen((data) => data === desiredCondition)
const unsubscribe = based
.query('db', queryObject, { persistent: true })
.subscribe((data) => {
console.log('received data!', data)
})
// ...
unsubscribe()
This method is used to get or observe a query
type function. It'll return an object with get
, subscribe
, and getWhen
methods.
get
is used get the current state of the query function, only once.getWhen
takes as argument a function to which it'll pass data every time the subscription fires. This function must return a boolean. When it returns true, the Promise will resolve with the latest sub data.subscribe
is used to subscribe to the query function and receive all subsequent updates. This method returns the function which must be called to unsubscribe.
Much like call
, it takes the name of the function and an optional payload.
This method however has an extra optional argument, where one can decide to mark the subscription as persistent
, which means that the latest data received from it will be stored in the browser's local storage, or for Node environments it'll save it to a folder which must be specified when creating the client instance, like so:
const client = new BasedClient(opts, {
persistentStorage: 'path/to/directory',
})
This data will be used as placeholder the next time the client is started, allowing for snappy-feeling applications, since data will be showed before the connection is established.
based.stream(name: string, stream: StreamFunctionOpts, progressListener?: (progress: number) => void): Promise<any>
await based.stream('db:file-upload', {
payload: { hello: true },
file: 'path/to/file',
mimeType: 'text/typescript',
})
await based.stream('db:file-upload', {
payload: { hello: true },
contents: 'look text content',
})
This endpoint is used to stream data to a stream
type based functions. These are useful for things like uploading files.
// TODO: list all options for this method
client
.channel('events', { type: 'pageview' })
.subscribe((event) => console.info(event))
client.channel('events', { type: 'pageview' }).publish({ path: '/home' })
Channels are stateless event streams that users can use to send and receive real-time updates from the Based API. You can create a channel by calling the channel method on the client and passing a channel name and options.
// TODO: add more info on channels/examples
await based.setAuthState({
token: 'mock_token',
renewToken: 'mock_renew_token',
userId: 'usUser',
})
This endpoint is used to set an authState for this client's session. On the server, this will trigger a call to verifyAuthState
.
A client's authState may be modified by the server.
Most of the changes with this new infrastructure happened on the server.
Please make sure to include the @based/functions
package in your project, where you can find function signatures, types, and helper functions useful for development.
Users will now be able to deploy several types of functions:
authorize
: specific type used for Authorize functions, either global or function-specificfunction
: these are pieces of code which have side-effects, ie:db:set
query
: functions used to observe and retrieve data, ie:db
stream
: functions used to stream data to, for example to upload fileschannel
: stateless event streams, ie analyticsjob
: functions that are kept alive, useful for cronjobs and action triggered by a subscription or timeapp
: to serve front-end applications
Many of these functions have among their arguments a BasedFunctionClient
instance, a Payload
, and a Context
.
BasedFunctionClient
is similar to a normal BasedClient, but has access to functions marked asinternalOnly
.Payload
is the payload sent by the BasedClient that invoked that function.Context
is an object containing session information. It contains the connection's information, such as IP, protocol context (WebSocket or HTTP), authState, User Agent, and more.
This type of server function has the following signature:
type Authorize = (
based: BasedFunctionClient,
context: Context<HttpSession | WebSocketSession>,
name: string,
payload?: any
) => Promise<boolean>
This function is used to check whether or not the request is authorized, by being executed right before the function being requested. The request is authorized if the function returns true, in which case the actual request is eecuted, and if the authorize
returns false, the request doesn't move forward.
This function can be exported as a selfstanding function, in which case it'll be called before every other function that isn't public, or can be exported with the name authorize
from within another function module, in which case it'll overwrite the generic authorize
in favour of the specific one.
This type of server function has the following signature:
type BasedFunction<P = any, K = any> = (
based: BasedFunctionClient,
payload: P,
ctx: Context
) => Promise<K>
These are the simplest type of function, they are called with the user payload, and their return value is sent to the user as result.
This type of server function has the following signature:
type BasedQueryFunction<P = any, K = any> =
| ((
based: BasedFunctionClient,
payload: P,
update: ObservableUpdateFunction<K>,
error: ObserveErrorListener
) => Promise<() => void>)
| ((
based: BasedFunctionClient,
payload: P,
update: ObservableUpdateFunction<K>,
error: ObserveErrorListener
) => () => void)
A user can subscribe to this function using based.query()
, and they'll receive an update every time the update
function is called, same as in the old system. Since this kind of function is shared, it has no access to a Context
object.
Example:
const queryFunction = async (based, payload, update) => {
let cnt = 0
update(cnt)
const counter = setInterval(() => {
update(++cnt)
}, 1000)
return () => {
clearInterval(counter)
}
}
This type of server function has the following signature:
type BasedStreamFunction<P = any, K = any> = BasedFunction<StreamPayload<P>, K>
The StreamPayload
object contains, in addition to a regular payload, information about the incoming stream, such as mimeType
, size
in bytes, fileName
, extension
, and of course the stream
itself.
This type of server function has the following signature:
type BasedChannelFunction<P = any, K = any> = (
based: BasedFunctionClient,
payload: P,
id: number,
update: ChannelMessageFunction<K>,
error: (err?: any) => void
) => () => void
TODO
TODO
Based provides some default functions to allow the user to interact with our database, as well as all the extra features Based provides.
Currently, these are the defaults we provide:
Name | Type | Description | Payload | Available on |
---|---|---|---|---|
db |
query | Get and observe data from the database. | A Based Query | @based/env-hub , @based/env-admin-hub |
db:set |
function | Set data to the database. | A Based Query | @based/env-hub , @based/env-admin-hub |
db:schema |
query | Get and observe the schema of a database. | { db?: string } Optional, defaults to default |
@based/env-hub , @based/env-admin-hub |
db:set-schema |
function | Set schema of a database | A valid Schema object | @based/env-hub , @based/env-admin-hub |
db:delete |
function | Delete node from database | { $id: string } The id of the node to delete |
@based/env-hub , @based/env-admin-hub |
db:origins |
query | List active origins | void | @based/env-hub , @based/env-admin-hub |
analytics:track |
channel | Publish analytics event. Cannot be listened to | any | @based/env-hub , @based/env-admin-hub |
db:file-upload |
stream | Upload a file, store entry in the database. | void | @based/env-hub , @based/env-admin-hub |
db:events |
channel | Listen to a specific db event | { type: 'created' | 'updated' | 'deleted'; filter: DbGetOptions } |
@based/env-hub , @based/env-admin-hub |