Skip to content
ChatfuelSDK
Reference

The module contract

The four interfaces a module implements — ModuleDescriptor, ModuleAppProps, HostIntegration, HostRuntime — field by field, and what each one is for.

A module contributes apps/shell/src/modules/<id>/ with an index.tsx exporting moduleDescriptor under that exact name, because the wizard regenerates the registry from it at scaffold time.

ModuleDescriptor

Prop

Type

Component has to be React.lazy. Ten modules imported eagerly put ten modules' code — and ten modules' generated GraphQL documents — into one seven-megabyte first load for a person who opened one of them. The shell mounts it under a Suspense whose fallback is the same spinner a module shows on its own first load, so a cold visit is one spinner and not two, and keys it on (module, bot) so a bot switch remounts with fresh subscriptions and fresh state.

The two invisibility flags are applied in one place, railModules, and they are not the same thing. hidden takes the route away too; railHidden keeps it. The descriptor's hidden is checked against the manifest's, so the two cannot drift.

ModuleAppProps

Everything a module gets. It imports React, ~ui, ~api, the contract files and its own subtree, and receives the rest through these props — it never touches window.location, because routing is the shell's.

Prop

Type

selectBot ignores a bot the shell does not know about rather than half-opening it, and says nothing when it does. The workspace argument matters without the auth module, where a bot lives in one of several workspaces the account owns and moving to it has to move both levels at once.

What a module may import

The boundary is enforced, not conventional. Inside apps/shell/src/modules/<id>/: react and react-dom, ~ui, ~api — with generated documents from its own namespace or core and no other — its own files, and exactly three files outside its subtree.

types.ts
shellApi.ts
testClient.ts

types.ts is this page. shellApi.ts is the runtime half — the shell API. testClient.ts is an inert client the render smoke tests mount over: a request made from it never answers, rather than answering with a lie.

The rule runs the other way too. A shell-level file may not reach into a module subtree; it goes through the registry.

HostIntegration

One module may wrap the shell instead of being a page in it. Only auth does.

Prop

Type

create is given the env bag, basePath (where the app is mounted, for the absolute links the integration mails out) and appLogo — a URL the host has already resolved, because a name is a string and arrives in env while a logo is a location, and an embed host's assets are not in our public/.

HostRuntime

What create returns. Its components close over their own adapter, so the shell never passes one around.

Prop

Type

getAccessToken and onSessionLost are what the shell builds its API client from, which is why the runtime is created first. What onSessionLost receives, and which rejections the auth module decides are not a lapse, is on the errors page.

Supporting types

TypeShapeUsed by
Navigate(url: string, options?: { replace?: boolean }) => voidModuleAppProps.navigate, and every host-integration component.
AppRoute{ moduleId: string | null; path: string; segments: readonly string[]; params: URLSearchParams } — a parsed /<seg>[/rest][?qs] below the app's base path, where path is seg/rest without the query.Gate, TopbarItem, Page.
HostBot{ id: string; botId: string | null; name: string }id is the host's own id for the row, what it is asked to rename or delete by; botId is the Chatfuel bot, null while it is still being created.HostRuntime.getBots.

On this page