Advanced module patterns
Some use cases require approaches beyond the standard module workflow. This page covers three advanced patterns: defining a new resource API, deploying custom components as remote parts, and packaging modules with Docker.
Define a new resource API
You can define a new, custom resource API if:
- You have a resource that does not fit into any of the existing component or service APIs.
- You have a resource that could fit into an existing API, but you want different methods and messages.
Tip
Defining a new resource API is significantly more complex than using an existing API. In most cases, use an existing API instead.
If you want to use most of an existing API but need a few additional functions, use the DoCommand endpoint with extra parameters to add custom functionality.
If your use case uses only DoCommand and no other API methods, define a new model of generic component or generic service.
Steps to define a new API
Viam uses protocol buffers for API definition. To define a new API:
Decide whether your custom API is a component (interfaces with hardware) or a service (provides higher-level functionality).
Choose a name for your API (called the subtype). Determine a valid API namespace triplet. For example,
your-org-namespace:component:gizmo.Create a directory for your module with a
srcsubdirectory.Tip
If you are writing your module in Python, you can use this module generator tool to generate stub files for the new API and a module that implements it.
Write the proto methods in a
<API name>.protofile insidesrc/proto/. For reference:Define the proto methods in Python or Go in a file called
api.pyorapi.go:Generate the required configuration files (
buf.yaml,buf.gen.yaml,buf.lock). See the Buf documentation.Use the protobuf compiler to generate all other necessary protocol buffer code from your
.protofile.
After defining your API
Once your API is defined, create a model that implements it.
Keep in mind:
- You cannot use SDKs to call your new API unless you build out the client to support it. Write code against the API in the language you used to define it.
- You need a local copy of the module code on whatever machine runs client code against it.
- Import the API definition from the module directory. For example, in Python:
from path.to.module.src.gizmo import Gizmo.
Custom components as remote parts
Running modular resources on the computer directly connected to your components is the preferred approach. However, if you cannot use modular resources because you need to host viam-server on a non-Linux system or have a compilation issue, you can code a custom resource implementation, host it on a server, and add it as a remote part of your machine.
Once configured, you control the custom component with the Viam SDKs like any other component.
Steps
- Code a new model of a built-in resource type by creating a new interface that implements the required methods from its API definition.
- Register the custom component on a new gRPC server instance and start the server.
- Add the server as a remote part of your machine.
- (Optional) Ensure the remote server automatically starts when the machine boots.
Each remote server can host one or many custom components.
Tip
For detailed instructions, see the full example in the Python SDK documentation.
- Code a new model of a built-in resource type by subclassing it (for example,
sensororarm). Implement any required methods from its API definition. - Register the custom component on a new gRPC server instance using the
viam.rpclibrary. - Add the server as a remote part of your machine.
- (Optional) Ensure the remote server automatically starts when the machine boots.
Each remote server can host one or many custom components.
Important
You must define all methods belonging to a built-in resource type when defining a new model. Otherwise, the class will not instantiate.
- In Python, raise
NotImplementedError()or usepassin methods you do not want to implement. - In Go, return
errUnimplemented.
Deploy a module using Docker
In rare cases, you may need to package and deploy a module using Docker. Use cases include:
- Your module has complex system dependencies that cannot be easily installed on a machine.
- You use a large container image and some layers are already cached on the machine.
- You have specific security requirements.
If you deploy using Docker, create a “first run” script to handle setup. This is not recommended for modules that do not use Docker.
Use a first_run script
Create a tarball containing:
The module’s entrypoint script or binary:
#!/bin/bash exec docker run <YOUR_CONTAINER_IMAGE> <YOUR_CONTAINER_OPTIONS>A meta.json file.
A first-run script that runs during setup:
#!/usr/bin/env bash docker pull mongo:6 echo "Setup complete." exit 0
This example Makefile builds a module binary and bundles it with a meta.json and first-run script.
Edit meta.json to include the
first_runfield:{ "first_run": "first_run.sh" }Configure the module on your machine normally. The first-run script executes once when
viam-serverreceives a new configuration, and once per version update.(Optional) To force the first-run script to run again without changing the module version, delete the marker file at
.viam/packages/data/module/<id>/bin.first_run_succeeded.(Optional) The default first-run timeout is 1 hour. Adjust with
"first_run_timeout": "5m"in the module configuration.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!