Redis data sources
EXTERNAL_REDIS() connects FeatureQL to Redis instances for low-latency feature retrieval. Redis is typically the fastest option for real-time serving — ideal for cached profiles, feature flags, and precomputed aggregates.
Creating a Redis source
Before querying Redis, create a persistent connection with SOURCE_REDIS():
CREATE OR REPLACE FEATURE FM.TUTORIALS.EXTERNAL.REDIS_SRC AS
SOURCE_REDIS(
'redis://host.docker.internal:6380'
WITH (
timeout='500ms',
key_patterns=ARRAY['customer_hash:*']
)
);| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.EXTERNAL.REDIS_SRC | CREATED | Feature created as not exists |
The connection string follows the standard redis://host:port format. The timeout parameter controls both connection and operation timeouts. key_patterns is an optimization hint that tells FeatureMesh which keys this source will access.
JSON key retrieval
The simplest pattern fetches a Redis key containing a JSON object, then parses it into a typed row:
WITH
CUSTOMER_ID := INPUT(BIGINT),
CUSTOMER_DETAILS_JSON := EXTERNAL_REDIS(KEY 'customer:' || CUSTOMER_ID::VARCHAR FROM FM.TUTORIALS.EXTERNAL.REDIS_SRC),
CUSTOMER_DETAILS := JSON_PARSE_AS(CUSTOMER_DETAILS_JSON, TYPE 'ROW(customer_id BIGINT, email VARCHAR, last_order_id BIGINT)'),
SELECT
CUSTOMER_ID := BIND_VALUES(ARRAY[1,3,5,7]),
LAST_ORDER_ID := CUSTOMER_DETAILS[last_order_id]
ORDER BY CUSTOMER_ID;| CUSTOMER_ID BIGINT | LAST_ORDER_ID BIGINT |
|---|---|
| 1 | 1 |
| 3 | 492020 |
| 2 | 271134 |
| 3 | 394992 |
The key expression 'customer:' || CUSTOMER_ID::VARCHAR builds a dynamic key per input. EXTERNAL_REDIS(KEY ...) returns the raw JSON string, and JSON_PARSE_AS() converts it to a structured row with type safety. From there, bracket notation extracts individual fields like CUSTOMER_DETAILS[last_order_id].
This pattern works well for complex objects — customer profiles, product catalogs, configuration records.
Hash field access
When your data lives in Redis hashes, you can skip JSON parsing entirely and fetch individual fields directly:
WITH
CUSTOMER_ID := INPUT(BIGINT)
SELECT
CUSTOMER_ID := BIND_VALUES(ARRAY[1,3,5,7]),
LAST_ORDER_ID := EXTERNAL_REDIS(KEY 'customer_hash:' || CUSTOMER_ID::VARCHAR FIELD 'last_order_id' FROM FM.TUTORIALS.EXTERNAL.REDIS_SRC)
ORDER BY CUSTOMER_ID;| CUSTOMER_ID BIGINT | LAST_ORDER_ID BIGINT |
|---|---|
| 1 | 1 |
| 3 | 492020 |
| 2 | 271134 |
| 3 | 394992 |
The FIELD keyword tells FeatureQL to issue an HGET instead of a GET. This is more efficient than fetching the entire object when you only need one or two fields — less data over the wire, no parsing overhead.
Table-style access with EXTERNAL_VIEW()
For more advanced patterns, EXTERNAL_VIEW() treats Redis keys as rows in a virtual table. This lets you join on the key itself:
WITH
CUSTOMER_ID := INPUT(BIGINT),
REDIS_VIEW := EXTERNAL_VIEW(
`SELECT key, customer_id, last_order_id FROM %FM.TUTORIALS.EXTERNAL.REDIS_SRC[customer_hash:*]` /* We need the "key" here */
ON `SELF.key='customer_hash:' || %CUSTOMER_ID`
AS ROW(key VARCHAR, customer_id BIGINT, last_order_id BIGINT)
),
SELECT
CUSTOMER_ID := BIND_VALUES(ARRAY[1,2,3,4]),
LAST_ORDER_ID := REDIS_VIEW[last_order_id]
ORDER BY CUSTOMER_ID;| CUSTOMER_ID BIGINT | LAST_ORDER_ID BIGINT |
|---|---|
| 1 | 1 |
| 3 | 492020 |
| 2 | 271134 |
| 3 | 394992 |
Or join on field values within the hashes:
WITH
CUSTOMER_ID := INPUT(BIGINT),
REDIS_VIEW := EXTERNAL_VIEW(
`SELECT key, customer_id, last_order_id FROM %FM.TUTORIALS.EXTERNAL.REDIS_SRC[customer_hash:*]` /* We need the "key" here */
ON `SELF.key='customer_hash:' || %CUSTOMER_ID`
AS ROW(key VARCHAR, customer_id BIGINT, last_order_id BIGINT)
),
SELECT
CUSTOMER_ID := BIND_VALUES(ARRAY[1,2,3,4]),
LAST_ORDER_ID := REDIS_VIEW[last_order_id]
ORDER BY CUSTOMER_ID;| CUSTOMER_ID BIGINT | LAST_ORDER_ID BIGINT |
|---|---|
| 1 | 1 |
| 3 | 492020 |
| 2 | 271134 |
| 3 | 394992 |
Table-style access scans all keys matching the pattern. Reserve this for small datasets — it is significantly slower than direct key or hash field lookups.
Error handling
Redis operations return NULL when they fail — whether from network issues, missing keys, timeouts, or JSON parsing errors. Use COALESCE() to provide fallback values:
CUSTOMER_TIER := COALESCE(
EXTERNAL_REDIS(KEY 'customer_hash:' || CUSTOMER_ID::VARCHAR FIELD 'tier' FROM REDIS_SRC),
'standard'
)