Skip to main content
Memory actions allow you to store and retrieve data in temporary memory storage. This is useful for caching frequently accessed data, passing state between workflow executions, and implementing cache-aside patterns.
Memory storage persists across workflow executions but is cleared when the server restarts. For persistent storage, use Data Operations.

memory_store

Stores data in temporary memory using a key-value pattern.

Key

The unique identifier for storing the data. Use template syntax to create dynamic keys.
YAML Keykey
Typestring
RequiredYes
Include unique identifiers in your keys to avoid collisions. For example: user_{{ param "user_id" }} or cache_{{ .request_hash }}.

Contents

The data to store. Can be any value, including JSON objects.
YAML Keycontents
Typestring
RequiredYes
Use {{ jsonout .action_result }} to store complex objects from previous actions.

Example

Store a user object in memory:
actions:
  cache_user:
    type: memory_store
    config:
      key: "user_{{ param \"user_id\" }}"
      contents: "{{ jsonout .fetch_user }}"
    next: response.success
Store a computed value:
actions:
  store_token:
    type: memory_store
    config:
      key: "auth_token_{{ .user.id }}"
      contents: "{{ .generate_token }}"
    next: action.use_token

memory_fetch

Retrieves data from temporary memory storage using a key.

Key

The key to look up in memory storage.
YAML Keykey
Typestring
RequiredYes

Example

Retrieve a cached user:
actions:
  get_cached_user:
    type: memory_fetch
    config:
      key: "user_{{ param \"user_id\" }}"
    next: conditional.cache_hit
The fetched value is available via {{ .get_cached_user }}. If the key doesn’t exist, the result will be empty.

Common Patterns

Cache-Aside Pattern

Check the cache first, fetch from database if not found, then update the cache:
actions:
  check_cache:
    type: memory_fetch
    config:
      key: "data_{{ param \"id\" }}"
    next: conditional.is_cached

conditionals:
  is_cached:
    expression: "{{ notempty .check_cache \"\" }}"
    onTrue: response.from_cache
    onFalse: action.fetch_from_db

actions:
  fetch_from_db:
    type: fetch
    config:
      integrationID: my_database
      table: data
      filters:
        - field: id
          operator: eq
          value: "{{ param \"id\" }}"
      single: true
    next: action.update_cache

  update_cache:
    type: memory_store
    config:
      key: "data_{{ param \"id\" }}"
      contents: "{{ jsonout .fetch_from_db }}"
    next: response.success

responses:
  from_cache:
    statusCode: 200
    body:
      source: "cache"
      data: "{{ .check_cache }}"

  success:
    statusCode: 200
    body:
      source: "database"
      data: "{{ .fetch_from_db }}"

Session Storage

Store and retrieve user session data:
actions:
  store_session:
    type: memory_store
    config:
      key: "session_{{ param \"session_id\" }}"
      contents: |
        {
          "user_id": "{{ .authenticated_user.id }}",
          "login_time": "{{ now }}",
          "preferences": {{ jsonout .user_preferences }}
        }
    next: response.session_created
actions:
  get_session:
    type: memory_fetch
    config:
      key: "session_{{ param \"session_id\" }}"
    next: conditional.session_valid

conditionals:
  session_valid:
    expression: "{{ notempty .get_session \"\" }}"
    onTrue: action.process_request
    onFalse: response.session_expired

Rate Limiting Counter

Track request counts for rate limiting:
actions:
  get_request_count:
    type: memory_fetch
    config:
      key: "rate_{{ param \"api_key\" }}"
    next: action.increment_count

  increment_count:
    type: javascript
    config:
      script: |
        function servflowRun(vars) {
          const current = parseInt(vars.get_request_count) || 0;
          return current + 1;
        }
    next: action.store_count

  store_count:
    type: memory_store
    config:
      key: "rate_{{ param \"api_key\" }}"
      contents: "{{ .increment_count }}"
    next: conditional.check_limit

conditionals:
  check_limit:
    expression: "{{ lt .increment_count 100 }}"
    onTrue: action.process_request
    onFalse: response.rate_limited

Temporary Token Storage

Store and validate temporary tokens:
actions:
  generate_reset_token:
    type: javascript
    config:
      script: |
        function servflowRun(vars) {
          return Math.random().toString(36).substring(2, 15);
        }
    next: action.store_reset_token

  store_reset_token:
    type: memory_store
    config:
      key: "reset_{{ .generate_reset_token }}"
      contents: |
        {
          "user_id": "{{ .fetch_user.id }}",
          "email": "{{ .fetch_user.email }}",
          "created_at": "{{ now }}"
        }
    next: action.send_reset_email
actions:
  validate_reset_token:
    type: memory_fetch
    config:
      key: "reset_{{ param \"token\" }}"
    next: conditional.token_valid

conditionals:
  token_valid:
    expression: "{{ notempty .validate_reset_token \"\" }}"
    onTrue: action.allow_password_reset
    onFalse: response.invalid_token

Best Practices

Key Naming: Use consistent key naming conventions with prefixes to organize your cached data. For example: user_, session_, cache_, rate_.
Data Size: Keep stored values reasonably sized. For large datasets, consider storing only essential fields or IDs and fetching full data when needed.
No TTL: Memory storage doesn’t support automatic expiration. Implement your own expiration logic by storing timestamps and checking them during retrieval.

Next Steps

Data Operations

Use persistent database storage for long-term data.

Transformation

Process and transform cached data with JavaScript.

Flow Control

Combine caching with parallel data fetching.

Actions Overview

Learn the fundamentals of ServFlow actions.