Finding a substring match asynchronously. Recursion? Flattening?

I really want to like node.js with redis, but I can't conquer asynchronicity. Again I have what is a simple task in a traditional database and language. My question is more about accomplishing control flow and logic in asynchronous database fetching than whether my problem-solving approach is optimal or not.

Here's what I'm trying to do: I have redis keys made up of words, let's just say car and card. Now, given an input string, I want to know what the longest substring is, that matches a key in redis. I only need to check substrings starting from position 0 of the given string, so the complexity is low.

Example: cardinal has the key card in it, and also car, but card is longer. Cape does not match either key.

My approach is: start with the whole string and check if it matches a key. If yes, return that key. Otherwise, repeat the same process with the string minus the last character.

How can I accomplish this task? Different approaches are welcome.

I know a little about the async library and it looks like waterfall is best for what I'm doing. However, it appears that I need to type all the functions from string.length, string.length-1, etc. until the last single character. What I'm looking for is a good replacement for a for loop with a break.

Below I test with an input I assume is always 3 characters or more (because it's already ugly and more nesting seems pointless for testing). It works, carde resulting in card, and care -> car. Nonsense gives no match.

var http = require("http");
var redis = require("redis");

http.createServer(function(request, response) {
    client = redis.createClient();
    word = "carde";
    client.keys(word, function(err, reply) {
        if(err) { response.end(err); client.end(); }
        else {
          if(reply.length > 0) {
            response.end(word);
            client.end();
          }
          else {
            client.keys(word.slice(0,-1), function(err, reply) {
                if(err) { response.end(err); client.end(); }
                else {
                  if(reply.length > 0) {
                    response.end(word.slice(0, -1));
                    client.end();
                  }
                  else {
                    client.keys(word.slice(0, -2), function(err,reply) {
                        if(err) { response.end(err); client.end(); }
                        else {
                          if(reply.length > 0) {
                            response.end(word.slice(0, -2));
                            client.end();
                          }
                          else {
                            response.end("no match");
                          }
                        }
                      });
                  }
                }
              });
          }
        }
    });
  }).listen(8000);

I also tried recursion and it might be the best method. (Thanks to Timonthy Strimple for correcting a mistake).

http.createServer(function(request, response) {
    client = redis.createClient();
    recursiveKeys("cardinalsin", client, response);
  }).listen(8000);

function recursiveKeys(word, client, response) {
  if (word.length == 0) {
    response.write("0");
    response.end();
    client.end();
  }
  else {
    client.keys(word, function(err, reply) {
        if(err) {
          response.write("0");
          response.end();
          client.end();
        }
        else {
          if(reply.length > 0) {
            response.write(word);
            response.end();
            client.end();
          }
          else {
            return recursiveKeys(word.slice(0,-1), client, response);
          }
        }
      });
  }
}

I agree that a recursive solution is probably the best. I approached this problem before studying your code (so as to not affect the experiment) and arrived at a very similar solution. Search for a word with http://localhost:3000/?word=cardinal

var http  = require('http');
var redis = require('redis');
var url   = require('url');

var findWord = function(needle, client, callback) {
  if (needle.length <= 0) { return callback("Word not found") }

  client.keys(needle, function(err, reply) {
    if (err) { callback("Word not found"); }
    else {
      if (reply.length > 0) {
        callback(needle);
      } else {
        findWord(needle.slice(0, -1), client, callback);
      }
    }
  });
};

var server = http.createServer(function(request, response) {
  var query  = url.parse(request.url, true).query;
  var word   = query.word || "";
  var client = redis.createClient();

  findWord(word, client, function(found) {
    client.end();
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.end(found);
  });
});
server.listen(3000);