{
  "id": "modules-native-types",
  "title": "Modules API for native types",
  "url": "https://un5pn9hmggug.julianrbryant.com/docs/latest/develop/reference/modules/modules-native-types/",
  "summary": "How to use native types in a Redis module",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-04-01T08:10:08-05:00",
  "page_type": "content",
  "content_hash": "0228e8d24d487d783c2814663f64a78367d80392632566b4b68b5021f53e1d53",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Redis modules can access Redis built-in data structures both at high level,\nby calling Redis commands, and at low level, by manipulating the data structures\ndirectly.\n\nBy using these capabilities in order to build new abstractions on top of existing\nRedis data structures, or by using strings DMA in order to encode modules\ndata structures into Redis strings, it is possible to create modules that\n*feel like* they are exporting new data types. However, for more complex\nproblems, this is not enough, and the implementation of new data structures\ninside the module is needed.\n\nWe call the ability of Redis modules to implement new data structures that\nfeel like native Redis ones **native types support**. This document describes\nthe API exported by the Redis modules system in order to create new data\nstructures and handle the serialization in RDB files, the rewriting process\nin AOF, the type reporting via the [`TYPE`]() command, and so forth."
    },
    {
      "id": "overview-of-native-types",
      "title": "Overview of native types",
      "role": "overview",
      "text": "A module exporting a native type is composed of the following main parts:\n\n* The implementation of some kind of new data structure and of commands operating on the new data structure.\n* A set of callbacks that handle: RDB saving, RDB loading, AOF rewriting, releasing of a value associated with a key, calculation of a value digest (hash) to be used with the `DEBUG DIGEST` command.\n* A 9 characters name that is unique to each module native data type.\n* An encoding version, used to persist into RDB files a module-specific data version, so that a module will be able to load older representations from RDB files.\n\nWhile to handle RDB loading, saving and AOF rewriting may look complex as a first glance, the modules API provide very high level function for handling all this, without requiring the user to handle read/write errors, so in practical terms, writing a new data structure for Redis is a simple task.\n\nA **very easy** to understand but complete example of native type implementation\nis available inside the Redis distribution in the `/modules/hellotype.c` file.\nThe reader is encouraged to read the documentation by looking at this example\nimplementation to see how things are applied in the practice."
    },
    {
      "id": "register-a-new-data-type",
      "title": "Register a new data type",
      "role": "content",
      "text": "In order to register a new native type into the Redis core, the module needs\nto declare a global variable that will hold a reference to the data type.\nThe API to register the data type will return a data type reference that will\nbe stored in the global variable.\n\n    static RedisModuleType *MyType;\n    #define MYTYPE_ENCODING_VERSION 0\n\n    int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n\tRedisModuleTypeMethods tm = {\n\t    .version = REDISMODULE_TYPE_METHOD_VERSION,\n\t    .rdb_load = MyTypeRDBLoad,\n\t    .rdb_save = MyTypeRDBSave,\n\t    .aof_rewrite = MyTypeAOFRewrite,\n\t    .free = MyTypeFree\n\t};\n\n        MyType = RedisModule_CreateDataType(ctx, \"MyType-AZ\",\n\t\tMYTYPE_ENCODING_VERSION, &tm);\n        if (MyType == NULL) return REDISMODULE_ERR;\n    }\n\nAs you can see from the example above, a single API call is needed in order to\nregister the new type. However a number of function pointers are passed as\narguments. Certain are optionals while some are mandatory. The above set\nof methods *must* be passed, while `.digest` and `.mem_usage` are optional\nand are currently not actually supported by the modules internals, so for\nnow you can just ignore them.\n\nThe `ctx` argument is the context that we receive in the `OnLoad` function.\nThe type `name` is a 9 character name in the character set that includes\nfrom `A-Z`, `a-z`, `0-9`, plus the underscore `_` and minus `-` characters.\n\nNote that **this name must be unique** for each data type in the Redis\necosystem, so be creative, use both lower-case and upper case if it makes\nsense, and try to use the convention of mixing the type name with the name\nof the author of the module, to create a 9 character unique name.\n\n**NOTE:** It is very important that the name is exactly 9 chars or the\nregistration of the type will fail. Read more to understand why.\n\nFor example if I'm building a *b-tree* data structure and my name is *antirez*\nI'll call my type **btree1-az**. The name, converted to a 64 bit integer,\nis stored inside the RDB file when saving the type, and will be used when the\nRDB data is loaded in order to resolve what module can load the data. If Redis\nfinds no matching module, the integer is converted back to a name in order to\nprovide some clue to the user about what module is missing in order to load\nthe data.\n\nThe type name is also used as a reply for the [`TYPE`]() command when called\nwith a key holding the registered type.\n\nThe `encver` argument is the encoding version used by the module to store data\ninside the RDB file. For example I can start with an encoding version of 0,\nbut later when I release version 2.0 of my module, I can switch encoding to\nsomething better. The new module will register with an encoding version of 1,\nso when it saves new RDB files, the new version will be stored on disk. However\nwhen loading RDB files, the module `rdb_load` method will be called even if\nthere is data found for a different encoding version (and the encoding version\nis passed as argument to `rdb_load`), so that the module can still load old\nRDB files.\n\nThe last argument is a structure used in order to pass the type methods to the\nregistration function: `rdb_load`, `rdb_save`, `aof_rewrite`, `digest` and\n`free` and `mem_usage` are all callbacks with the following prototypes and uses:\n\n    typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);\n    typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);\n    typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);\n    typedef size_t (*RedisModuleTypeMemUsageFunc)(void *value);\n    typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);\n    typedef void (*RedisModuleTypeFreeFunc)(void *value);\n\n* `rdb_load` is called when loading data from the RDB file. It loads data in the same format as `rdb_save` produces.\n* `rdb_save` is called when saving data to the RDB file.\n* `aof_rewrite` is called when the AOF is being rewritten, and the module needs to tell Redis what is the sequence of commands to recreate the content of a given key.\n* `digest` is called when `DEBUG DIGEST` is executed and a key holding this module type is found. Currently this is not yet implemented so the function ca be left empty.\n* `mem_usage` is called when the [`MEMORY`]() command asks for the total memory consumed by a specific key, and is used in order to get the amount of bytes used by the module value.\n* `free` is called when a key with the module native type is deleted via [`DEL`]() or in any other mean, in order to let the module reclaim the memory associated with such a value."
    },
    {
      "id": "why-module-types-require-nine-character-names",
      "title": "Why module types require nine character names",
      "role": "content",
      "text": "When Redis persists to RDB files, modules specific data types require to\nbe persisted as well. Now RDB files are sequences of key-value pairs\nlike the following:\n\n    [1 byte type] [key] [a type specific value]\n\nThe 1 byte type identifies strings, lists, sets, and so forth. In the case\nof modules data, it is set to a special value of `module data`, but of\ncourse this is not enough, we need the information needed to link a specific\nvalue with a specific module type that is able to load and handle it.\n\nSo when we save a `type specific value` about a module, we prefix it with\na 64 bit integer. 64 bits is large enough to store the information needed\nin order to lookup the module that can handle that specific type, but is\nshort enough that we can prefix each module value we store inside the RDB\nwithout making the final RDB file too big. At the same time, this solution\nof prefixing the value with a 64 bit *signature* does not require to do\nstrange things like defining in the RDB header a list of modules specific\ntypes. Everything is pretty simple.\n\nSo, what you can store in 64 bits in order to identify a given module in\na reliable way? Well if you build a character set of 64 symbols, you can\neasily store 9 characters of 6 bits, and you are left with 10 bits, that\nare used in order to store the *encoding version* of the type, so that\nthe same type can evolve in the future and provide a different and more\nefficient or updated serialization format for RDB files.\n\nSo the 64 bit prefix stored before each module value is like the following:\n\n    6|6|6|6|6|6|6|6|6|10\n\nThe first 9 elements are 6-bits characters, the final 10 bits is the\nencoding version.\n\nWhen the RDB file is loaded back, it reads the 64 bit value, masks the final\n10 bits, and searches for a matching module in the modules types cache.\nWhen a matching one is found, the method to load the RDB file value is called\nwith the 10 bits encoding version as argument, so that the module knows\nwhat version of the data layout to load, if it can support multiple versions.\n\nNow the interesting thing about all this is that, if instead the module type\ncannot be resolved, since there is no loaded module having this signature,\nwe can convert back the 64 bit value into a 9 characters name, and print\nan error to the user that includes the module type name! So that she or he\nimmediately realizes what's wrong."
    },
    {
      "id": "set-and-get-keys",
      "title": "Set and get keys",
      "role": "content",
      "text": "After registering our new data type in the `RedisModule_OnLoad()` function,\nwe also need to be able to set Redis keys having as value our native type.\n\nThis normally happens in the context of commands that write data to a key.\nThe native types API allow to set and get keys to module native data types,\nand to test if a given key is already associated to a value of a specific data\ntype.\n\nThe API uses the normal modules `RedisModule_OpenKey()` low level key access\ninterface in order to deal with this. This is an example of setting a\nnative type private data structure to a Redis key:\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_WRITE);\n    struct some_private_struct *data = createMyDataStructure();\n    RedisModule_ModuleTypeSetValue(key,MyType,data);\n\nThe function `RedisModule_ModuleTypeSetValue()` is used with a key handle open\nfor writing, and gets three arguments: the key handle, the reference to the\nnative type, as obtained during the type registration, and finally a `void*`\npointer that contains the private data implementing the module native type.\n\nNote that Redis has no clues at all about what your data contains. It will\njust call the callbacks you provided during the method registration in order\nto perform operations on the type.\n\nSimilarly we can retrieve the private data from a key using this function:\n\n    struct some_private_struct *data;\n    data = RedisModule_ModuleTypeGetValue(key);\n\nWe can also test for a key to have our native type as value:\n\n    if (RedisModule_ModuleTypeGetType(key) == MyType) {\n        /* ... do something ... */\n    }\n\nHowever for the calls to do the right thing, we need to check if the key\nis empty, if it contains a value of the right kind, and so forth. So\nthe idiomatic code to implement a command writing to our native type\nis along these lines:\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != MyType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\nThen if we successfully verified the key is not of the wrong type, and\nwe are going to write to it, we usually want to create a new data structure if\nthe key is empty, or retrieve the reference to the value associated to the\nkey if there is already one:\n\n    /* Create an empty value object if the key is currently empty. */\n    struct some_private_struct *data;\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        data = createMyDataStructure();\n        RedisModule_ModuleTypeSetValue(key,MyTyke,data);\n    } else {\n        data = RedisModule_ModuleTypeGetValue(key);\n    }\n    /* Do something with 'data'... */"
    },
    {
      "id": "free-method",
      "title": "Free method",
      "role": "content",
      "text": "As already mentioned, when Redis needs to free a key holding a native type\nvalue, it needs help from the module in order to release the memory. This\nis the reason why we pass a `free` callback during the type registration:\n\n    typedef void (*RedisModuleTypeFreeFunc)(void *value);\n\nA trivial implementation of the free method can be something like this,\nassuming our data structure is composed of a single allocation:\n\n    void MyTypeFreeCallback(void *value) {\n        RedisModule_Free(value);\n    }\n\nHowever a more real world one will call some function that performs a more\ncomplex memory reclaiming, by casting the void pointer to some structure\nand freeing all the resources composing the value."
    },
    {
      "id": "rdb-load-and-save-methods",
      "title": "RDB load and save methods",
      "role": "content",
      "text": "The RDB saving and loading callbacks need to create (and load back) a\nrepresentation of the data type on disk. Redis offers a high level API\nthat can automatically store inside the RDB file the following types:\n\n* Unsigned 64 bit integers.\n* Signed 64 bit integers.\n* Doubles.\n* Strings.\n\nIt is up to the module to find a viable representation using the above base\ntypes. However note that while the integer and double values are stored\nand loaded in an architecture and *endianness* agnostic way, if you use\nthe raw string saving API to, for example, save a structure on disk, you\nhave to care those details yourself.\n\nThis is the list of functions performing RDB saving and loading:\n\n    void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value);\n    uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io);\n    void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value);\n    int64_t RedisModule_LoadSigned(RedisModuleIO *io);\n    void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s);\n    void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len);\n    RedisModuleString *RedisModule_LoadString(RedisModuleIO *io);\n    char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr);\n    void RedisModule_SaveDouble(RedisModuleIO *io, double value);\n    double RedisModule_LoadDouble(RedisModuleIO *io);\n\nThe functions don't require any error checking from the module, that can\nalways assume calls succeed.\n\nAs an example, imagine I've a native type that implements an array of\ndouble values, with the following structure:\n\n    struct double_array {\n        size_t count;\n        double *values;\n    };\n\nMy `rdb_save` method may look like the following:\n\n    void DoubleArrayRDBSave(RedisModuleIO *io, void *ptr) {\n        struct dobule_array *da = ptr;\n        RedisModule_SaveUnsigned(io,da->count);\n        for (size_t j = 0; j < da->count; j++)\n            RedisModule_SaveDouble(io,da->values[j]);\n    }\n\nWhat we did was to store the number of elements followed by each double\nvalue. So when later we'll have to load the structure in the `rdb_load`\nmethod we'll do something like this:\n\n    void *DoubleArrayRDBLoad(RedisModuleIO *io, int encver) {\n        if (encver != DOUBLE_ARRAY_ENC_VER) {\n            /* We should actually log an error here, or try to implement\n               the ability to load older versions of our data structure. */\n            return NULL;\n        }\n\n        struct double_array *da;\n        da = RedisModule_Alloc(sizeof(*da));\n        da->count = RedisModule_LoadUnsigned(io);\n        da->values = RedisModule_Alloc(da->count * sizeof(double));\n        for (size_t j = 0; j < da->count; j++)\n            da->values[j] = RedisModule_LoadDouble(io);\n        return da;\n    }\n\nThe load callback just reconstruct back the data structure from the data\nwe stored in the RDB file.\n\nNote that while there is no error handling on the API that writes and reads\nfrom disk, still the load callback can return NULL on errors in case what\nit reads does not look correct. Redis will just panic in that case."
    },
    {
      "id": "aof-rewriting",
      "title": "AOF rewriting",
      "role": "content",
      "text": "void RedisModule_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);"
    },
    {
      "id": "allocate-memory",
      "title": "Allocate memory",
      "role": "content",
      "text": "Modules data types should try to use `RedisModule_Alloc()` functions family\nin order to allocate, reallocate and release heap memory used to implement the native data structures (see the other Redis Modules documentation for detailed information).\n\nThis is not just useful in order for Redis to be able to account for the memory used by the module, but there are also more advantages:\n\n* Redis uses the `jemalloc` allocator, that often prevents fragmentation problems that could be caused by using the libc allocator.\n* When loading strings from the RDB file, the native types API is able to return strings allocated directly with `RedisModule_Alloc()`, so that the module can directly link this memory into the data structure representation, avoiding a useless copy of the data.\n\nEven if you are using external libraries implementing your data structures, the\nallocation functions provided by the module API is exactly compatible with\n`malloc()`, `realloc()`, `free()` and `strdup()`, so converting the libraries\nin order to use these functions should be trivial.\n\nIn case you have an external library that uses libc `malloc()`, and you want\nto avoid replacing manually all the calls with the Redis Modules API calls,\nan approach could be to use simple macros in order to replace the libc calls\nwith the Redis API calls. Something like this could work:\n\n    #define malloc RedisModule_Alloc\n    #define realloc RedisModule_Realloc\n    #define free RedisModule_Free\n    #define strdup RedisModule_Strdup\n\nHowever take in mind that mixing libc calls with Redis API calls will result\ninto troubles and crashes, so if you replace calls using macros, you need to\nmake sure that all the calls are correctly replaced, and that the code with\nthe substituted calls will never, for example, attempt to call\n`RedisModule_Free()` with a pointer allocated using libc `malloc()`."
    }
  ],
  "examples": []
}
