Handle command replies

Handle command replies with hiredis.

The redisCommand() and redisCommandArgv() functions return a pointer to a redisReply object when you issue a command (see Issue commands for more information). This type supports all reply formats defined in the RESP2 and RESP3 protocols, so its content varies greatly between calls.

A simple example is the status response returned by the SET command. The code below shows how to get this from the redisReply object:

redisReply *reply = redisCommand(c, "SET greeting Hello");

// Check and free the reply.
if (reply != NULL) {
    printf("Reply: %s\n", reply->str);
    freeReplyObject(reply);
    reply = NULL;
}

A null reply indicates an error, so you should always check for this. If an error does occur, then the redisContext object will have a non-zero error number in its integer err field and a textual description of the error in its errstr field.

For SET, a successful call will simply return an "OK" string that you can access with the reply->str field. The code in the example prints this to the console, but you should check for the specific value to ensure the command executed correctly.

The redisCommand() call allocates memory for the reply, so you should always free it using freeReplyObject() when you have finished using the reply. If you want to reuse the reply variable then it is wise to set it to NULL after you free it, so that you don't accidentally use the stale pointer later.

Reply formats

The Redis RESP protocols support several different reply formats for commands.

You can find the reply format for a command at the end of its reference page in the RESP2/RESP3 Reply section (for example, the INCRBY page shows that the command has an integer result). You can also determine the format using the type field of the reply object. This contains a different integer value for each type. The hiredis.h header file defines constants for all of these integer values (for example REDIS_REPLY_STRING).

The redisReply struct has several fields to contain different types of replies, with different fields being set depending on the value of the type field. The table below shows the type constants, the corresponding reply type, and the fields you can use to access the reply value:

Constant Type Relevant fields of redisReply RESP protocol
REDIS_REPLY_STATUS Simple string reply->str: the string value (char*)
reply->len: the string length (size_t)
2, 3
REDIS_REPLY_ERROR Simple error reply->str: the string value (char*)
reply->len: the string length (size_t)
2, 3
REDIS_REPLY_INTEGER Integer reply->integer: the integer value (long long) 2, 3
REDIS_REPLY_NIL Null No data 2, 3
REDIS_REPLY_STRING Bulk string reply->str: the string value (char*)
reply->len: the string length (size_t)
2, 3
REDIS_REPLY_ARRAY Array reply->elements: number of elements (size_t)
reply->element: array elements (redisReply)
2, 3
REDIS_REPLY_DOUBLE Double reply->str: double value as string (char*)
reply->len: the string length (size_t)
3
REDIS_REPLY_BOOL Boolean reply->integer: the boolean value, 0 or 1 (long long) 3
REDIS_REPLY_MAP Map reply->elements: number of elements (size_t)
reply->element: array elements (redisReply)
3
REDIS_REPLY_SET Set reply->elements: number of elements (size_t)
reply->element: array elements (redisReply)
3
REDIS_REPLY_PUSH Push reply->elements: number of elements (size_t)
reply->element: array elements (redisReply)
3
REDIS_REPLY_BIGNUM Big number reply->str: number value as string (char*)
reply->len: the string length (size_t)
3
REDIS_REPLY_VERB Verbatim string reply->str: the string value (char*)
reply->len: the string length (size_t)
reply->vtype: content type (char[3])
3

Reply format processing examples

The sections below explain how to process specific reply types in more detail.

Integers

The REDIS_REPLY_INTEGER and REDIS_REPLY_BOOL reply types both contain values in reply->integer. However, REDIS_REPLY_BOOL is rarely used. Even when the command essentially returns a boolean value, the reply is usually reported as an integer.

// Add some values to a set.
redisReply *reply = redisCommand(c, "SADD items bread milk peas");

if (reply->type == REDIS_REPLY_INTEGER) {
    // Report status.
    printf("Integer reply\n");
    printf("Number added: %lld\n", reply->integer);
    // >>> Number added: 3
}

freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "SISMEMBER items bread");

// This also gives an integer reply but you should interpret
// it as a boolean value.
if (reply->type == REDIS_REPLY_INTEGER) {
    // Respond to boolean integer value.
    printf("Integer reply\n");
    
    if (reply->integer == 0) {
        printf("Items set has no member 'bread'\n");
    } else {
        printf("'Bread' is a member of items set\n");
    }
    // >>> 'Bread' is a member of items set
}

freeReplyObject(reply);
reply = NULL;

Strings

The REDIS_REPLY_STATUS, REDIS_REPLY_ERROR, REDIS_REPLY_STRING, REDIS_REPLY_DOUBLE, REDIS_REPLY_BIGNUM, and REDIS_REPLY_VERB are all returned as strings, with the main difference lying in how you interpret them. For all these types, the string value is returned in reply->str and the length of the string is in reply->len. The example below shows some of the possibilities.

// Set a numeric value in a string.
reply = redisCommand(c, "SET number 1.5");

// This gives a status reply.
if (reply->type == REDIS_REPLY_STATUS) {
    // Report status.
    printf("Status reply\n");
    printf("Reply: %s\n", reply->str); // >>> Reply: OK
}

freeReplyObject(reply);
reply = NULL;


// Attempt to interpret the key as a hash.
reply = redisCommand(c, "HGET number field1");

// This gives an error reply.
if (reply->type == REDIS_REPLY_ERROR) {
    // Report the error.
    printf("Error reply\n");
    printf("Reply: %s\n", reply->str);
    // >>> Reply: WRONGTYPE Operation against a key holding the wrong kind of value
}

freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "GET number");

// This gives a simple string reply.
if (reply->type == REDIS_REPLY_STRING) {
    // Display the string.
    printf("Simple string reply\n");
    printf("Reply: %s\n", reply->str); // >>> Reply: 1.5
}

freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "ZADD prices 1.75 bread 5.99 beer");

// This gives an integer reply.
if (reply->type == REDIS_REPLY_INTEGER) {
    // Display the integer.
    printf("Integer reply\n");
    printf("Number added: %lld\n", reply->integer);
    // >>> Number added: 2
}

freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "ZSCORE prices bread");

// This gives a string reply with RESP2 and a double reply
// with RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_STRING) {
    printf("String reply\n");
    
    char *endptr; // Not used.
    double price = strtod(reply->str, &endptr);
    double discounted = price * 0.75;
    printf("Discounted price: %.2f\n", discounted);
    // >>> Discounted price: 1.31
}

freeReplyObject(reply);
reply = NULL;

Arrays and maps

Arrays (reply type REDIS_REPLY_ARRAY) and maps (reply type REDIS_REPLY_MAP) are returned by commands that retrieve several values at the same time. For both types, the number of elements in the reply is contained in reply->elements and the pointer to the array itself is is reply->element. Each item in the array is of type redisReply. The array elements are typically simple types rather than arrays or maps.

The example below shows how to get the items from a list:

reply = redisCommand(c, "RPUSH things thing0 thing1 thing2 thing3");

printf("Added %lld items\n", reply->integer);
// >>> Added 4 items
freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "LRANGE things 0 -1");

for (int i = 0; i < reply->elements; ++i) {
    if (reply->element[i]->type == REDIS_REPLY_STRING) {
        printf("List item %d: %s\n", i, reply->element[i]->str);
    }
}
// >>> List item 0: thing0
// >>> List item 1: thing1
// >>> List item 2: thing2
// >>> List item 3: thing3

A map is essentially the same as an array but it has the extra guarantee that the items will be listed in key-value pairs. The example below shows how to get all the fields from a hash using HGETALL:

const char *hashCommand[] = {
    "HSET", "details",
    "name", "Mr Benn",
    "address", "52 Festive Road",
    "hobbies", "Cosplay"
};

reply = redisCommandArgv(c, 8, hashCommand, NULL);

printf("Added %lld fields\n", reply->integer);
// >>> Added 3 fields

freeReplyObject(reply);
reply = NULL;


reply = redisCommand(c, "HGETALL details");

// This gives an array reply with RESP2 and a map reply with
// RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_ARRAY) {        
    for (int i = 0; i < reply->elements; i += 2) {
        char *key = reply->element[i]->str;
        char *value = reply->element[i + 1]->str;
        printf("Key: %s, value: %s\n", key, value);
    }
    // >>> Key: name, value: Mr Benn
    // >>> Key: address, value: 52 Festive Road
    // >>> Key: hobbies, value: Cosplay
}

Handling errors

When a command executes successfully, the err field of the context object will be set to zero. If a command fails, it will return either NULL or REDIS_ERR, depending on which function and command you used. When this happens, context->err will contain an error code

  • REDIS_ERR_IO: There was an I/O error while creating the connection, or while trying to write or read data. Whenever context->err contains REDIS_ERR_IO, you can use the features of the standard library file errno.h to find out more information about the error.
  • REDIS_ERR_EOF: The server closed the connection which resulted in an empty read.
  • REDIS_ERR_PROTOCOL: There was an error while parsing the RESP protocol.
  • REDIS_ERR_OTHER: Any other error. Currently, it is only used when the connection hostname can't be resolved.

The context object also has an errstr field that contains a descriptive error message.

RATE THIS PAGE
Back to top ↑