Membrane Driver Guide
1. Understanding the Driver’s Role
A driver serves as a bridge between Membrane and external APIs. Drivers expose API functionality as nodes in your Membrane graph, making it easy for other programs to interact with external services.
We have drivers for popular APIs like GitHub and Slack. Check out the full list here. Drivers are open source and anyone can create and publish one. Once you get the hang of it, you won’t have to worry much about the quirks of individual APIs since all drivers follow the same structure.
Drivers are regular Membrane programs, so if you’ve written some code on Membrane you’re already halfway there!
For this guide, we’ll walk through the
We’ll demonstrate how to:
- Create an interface for the API that uses Membrane state to store the API key
- Structure your code using common Membrane patterns (Collections, Resources, grefs)
- Follow consistent driver design principles and best practices
2. Core Components
File Organization
A typical Membrane driver should have this structure:
Schema
Before we dive into the code, let’s take a look at the driver schema.
The schema is defined in memconfig.json
but won’t generally edit it by hand
(but you can!). Instead you use the Schema Editor on the right sidebar.
The schema for a programs defines the “shape” of its graph.
When writing drivers, the schema should match the API as closely as possible. The list you see above includes:
Configuration
Every driver needs basic configuration and status checks. We add those in the
helpers.ts
file. Here’s how we implement this for Resend:
helper.ts
As a convention, utility functions live in helpers.ts
. For this driver, we
have an api
function that calls fetch
with the appropriate headers, query
and authentication, as well as parsing the response as JSON when appropriate.
Most drivers will need a similar function to make the actual HTTP requests:
Root Object
In all programs (drivers included) the Root
object serves as the entry point
to all of its functionality. Every program has a Root
type in its schema which
defines the top-level structure of the program’s graph.
Resend’s API exposes two types of resources through their /emails
and
/domains
endpoints.
To expose that functionality via the Membrane graph, the driver’s Root
type
provide corresponding field resolvers:
Note that the resolvers don’t do much, they simply return an empty object. This
tells the GraphQL executor to continue the query by invoking the resolvers in
EmailCollection
and DomainCollection
respectively (more on that below).
Resources, Collections, and Pages
In Membrane drivers, we organize our code around key resources.
The pattern is to create pairs of Collection and Resource objects in the
index.ts
file, where collections handle operations like listing and creation,
while resources handle item-specific operations.
This Resource-Collection-Page pattern in Membrane provides a consistent interface where:
- Collections handle
one()
for getting single resources,page()
for listing resources, and other collection-level operations likecreate()
orsearch()
- Resources implement
gref
for referencing and item-specific operations likeupdate()
anddelete()
- Collection operations go on collections (listing, creating)
- Resource-specific operations go on the resource object (updating, deleting)
Let’s look at this pattern using Resend’s Emails and Domains as examples.
Email Collection
Collections typically implement one()
for getting single resources and methods
for creating new resources:
Email Resource
Resources handle operations specific to a single item and must implement a
gref
for referencing:
Domain Collection (Pagination Example)
For resources that support listing, collections should implement a page()
function for pagination:
Graph References (grefs)
In Membrane, grefs (graph references) create a way to reference and track
specific resources across your graph. Every resource should implement a gref
function that returns a unique, consistent path to that resource.
Think of a gref like a URL - it’s a way to point to a specific resource that can be stored, passed around, and used later. For example:
Grefs serve several important purposes:
- Resource Identity: They provide a standardized way to reference specific resources
- Resource Lookups: Programs can use grefs to consistently find and access resources
- Cross-Program Communication: Programs can pass grefs to each other to reference the same resource
- Action Context: When performing actions on a resource, the gref provides
context about which resource to act on using
self.$argsAt()
Testing Your Driver
Create a separate tests.ts
file to organize your tests. Here’s an example from
the Resend driver:
These tests are then imported and exposed in your graph through the Root object
in index.ts
, making them invoke-able in the Membrane Navigator:
A Note on Types in Membrane
Driver types are defined using the Schema Editor. There you’ll define:
- Fields on the Root type (like
domains: DomainCollection
) - Collection types with their operations (
one
,page
,create
) - Resource types with their fields
- Page types for pagination results
For example, the Resend driver’s types are defined in the Schema Editor to establish:
- Collection type
DomainCollection
with:one
: returns typeDomain
page
: returns typeDomainPage
create
: returns typeDomain
DomainPage
type with fielditems
of typeList<Domain>
- Resource type
Domain
with its fields
The Schema Editor generates your memconfig.json
based on these type
definitions.
3. Best Practices
Collection Methods
Always implement these core methods on collections:
Error Handling
Provide clear, actionable error messages:
Resource References
Every resource must implement gref
for consistent referencing:
Testing
- Write tests that combine multiple actions (like sending an email then checking its status)
- Test all exposed operations
Code Organization
- Keep collection operations on collections (listing, creating, searching)
- Keep resource operations on resources (updating, deleting)
- Use descriptive names for actions
- Consider adding JSDoc comments for complex operations
4. Publishing Your Driver
-
Include a README.md with:
- Configuration steps
- Basic usage examples
- Available methods
-
Test before publishing:
-
Share your driver in discord!
Remember: The best drivers make complex APIs feel simple and intuitive to use within the Membrane ecosystem.
As you build more drivers and connect more services, you’ll unlock increasingly powerful automation and integration possibilities.
5. Troubleshooting
Get started with our [driver-template]
6. Missing a Driver?
Missing a driver for one of your favorite APIs?
- Request it in the community
- Contribute it! You can get started with our [template] or try out the [driver-generator].