Skip to main content

Write plugins

SocketDB can be extended using plugins.

See a list of already available plugins here.

Plugins can tap into exposed hooks to extend the functionality of SocketDB:

const client = SocketDBClient({
plugins: [
{
name: 'myplugin',
hooks: {
'client:set': ({ path, value, meta }) => {
meta.updated = new Date().getTime();
return {
path,
value,
meta,
};
},
},
},
],
});
Plugin implementation

Plugins are implemented using the krog library. Check out its documentation to learn more.

If your plugins middleware returns a value, this value will be passed to the next plugin and finally processed internally instead of the initial arguments.

Extending Types (typescript)

You might want to extend the internal meta data type definition with your plugin meta definitions to provide type checking.

For example:

extension.d.ts

declare module '@socketdb/core' {
export interface Meta {
updated: Date;
}
}

Available hooks

Client

type ClientHooks = {
'client:set'?: Hook<{
path: string;
value: SchemaDefinition;
meta?: Meta;
}>;
'client:delete'?: Hook<{ path: string }>;
'client:firstConnect'?: Hook;
'client:reconnect'?: Hook;
/**
* Called when the client is disconnected from the server.
*
* If the connection is closed by the server,
* "client:serverDisconnectMessage" will also be called before this hook.
*/
'client:disconnect'?: Hook;
/**
* Called when the client receives a goodbye message from the server,
* before the server closes the connection.
*
* It includes the reason why the server disconnected the client.
*/
'client:serverDisconnectMessage'?: Hook<GoodbyeMessage>;
/**
* Called before the client sends a heartbeat to the server.
*
* The payload coming from the server will be passed as a argument.
* Returned value will be sent to the server as heartbeat payload.
*/
'client:heartbeat'?: Hook<Record<string, unknown>>;
};
type Hook<Arguments = void, Context = void> = (args: Arguments, cxt: Context) => void | Arguments | Promise<void | Arguments>;

Server

type ServerHooks = {
/**
* Called when the server is initialized.
*/
'server:init'?: Hook<
Record<string, never>,
{
api: SocketDBServerDataAPI<Schema>;
}
>;
'server:clientConnect'?: Hook<
{ id: string },
{
client: Client;
api: SocketDBServerDataAPI<Schema>;
}
>;
/**
* Allows you to intercept the keep-alive check for a client.
*
* Throwing an error will skip the check and disconnect the client.
*
* Returned arguments will be sent to the client as a payload.
*
*/
'server:keepAliveCheck'?: Hook<
Record<string, unknown>,
{
client: Client;
api: SocketDBServerDataAPI<Schema>;
}
>;
/**
* Allows you to verify the pong (heartbeat) response from the client.
*
* If you throw an error, the client will be disconnected. Otherwise, the client will be considered connected.
*
* You can use this to add additional checks, e.g. verify the client token expiration.
*
* Any payload sent by the client will be passed as arguments.
*/
'server:heartbeat'?: Hook<
Record<string, unknown>,
{
client: Client;
api: SocketDBServerDataAPI<Schema>;
}
>;
'server:clientDisconnect'?: Hook<
{ id: string },
{
client: Client;
api: SocketDBServerDataAPI<Schema>;
}
>;
'server:update'?: Hook<
{ data: Node<RecursivePartial<Schema>> },
{
// if the update has been triggered by the server, the client will have null values
// This will change in the future to be something like Client | undefined
client: Client | Nullified<Client>;
api: SocketDBServerDataAPI<Schema>;
}
>;
'server:delete'?: Hook<
{ path: string },
{
// if the update has been triggered by the server, the client will have null values
// This will change in the future to be something like Client | undefined
client: Client | Nullified<Client>;
api: SocketDBServerDataAPI<Schema>;
}
>;
};
type Hook<Arguments = void, Context = void> = (args: Arguments, cxt: Context) => void | Arguments | Promise<void | Arguments>;

type Client = {
id: string;
context: SessionContext;
/**
* Closes the connection to the client.
*/
close: () => void;
};