Skip to content

[TwigBridge][TwigBundle] Add rate_limit() Twig function #61439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from

Conversation

santysisi
Copy link
Contributor

Q A
Branch? 7.4
Bug fix? no
New feature? yes
Deprecations? no
Issues no
License MIT

Summary

This PR introduces a new rate_limit() Twig function to enable rate limiting logic directly within Twig templates.

Usage Examples

{% set limit = rate_limit('anonymous_api', expression('request.getClientIp()')) %}
{% if limit %}
    <p>You have access to this limited feature.</p>
{% else %}
    <p>Rate limit exceeded.</p>
{% endif %}

You can also use static keys:

{% set limit = rate_limit('anonymous_api', 'custom_key', 5) %}

Motivation

The motivation behind this feature is to provide a simple, declarative way to apply rate limiting to specific sections of a template.

A typical use case is gating a single expensive or high-demand feature (e.g. AI calls, feedback buttons, real-time components), while still rendering the rest of the page.

For example:

<div class="example-wrapper">
    {# Always-visible section #}
    <div class="section">
        <h2>Welcome</h2>
        <p>This content is always visible to all users.</p>
    </div>

    {# Public section #}
    <div class="section">
        <h2>Public News</h2>
        <ul>
            <li>📢 Feature A launched!</li>
            <li>📈 System status: All good.</li>
        </ul>
    </div>

    {# Rate-limited section only #}
    {% set limit = rate_limit('anonymous_api', expression('"ip-" ~ request.getClientIp()'), 100) %}

    <div class="section">
        <h2>Special Feature (Rate Limited)</h2>

        {% if limit %}
            <div class="rate-limited good-view">
                <p>✅ You have access to this limited feature. Enjoy it while it lasts!</p>
            </div>
        {% else %}
            <div class="rate-limited poor-view">
                <p>🚫 Too many requests. This feature is temporarily unavailable for you.</p>
            </div>
        {% endif %}
    </div>

    {# Always-visible contact #}
    <div class="section">
        <h2>Contact Support</h2>
        <p>If you're having trouble, <a href="/contact">reach out to us</a>.</p>
    </div>
</div>

This approach allows template authors to apply rate limiting surgically, exactly where it’s needed, while keeping other parts of the page fully accessible.

@santysisi santysisi requested a review from yceruto as a code owner August 16, 2025 23:22
@carsonbot carsonbot added this to the 7.4 milestone Aug 16, 2025
@santysisi santysisi force-pushed the feature/add-twig-rate-limit-function branch 3 times, most recently from 0a587d7 to 8ec3f64 Compare August 16, 2025 23:57
@santysisi santysisi changed the title [TwigBridge][TwigBundle] Add new rate_limit() Twig function [TwigBridge][TwigBundle] Add rate_limit() Twig function Aug 17, 2025
@santysisi santysisi force-pushed the feature/add-twig-rate-limit-function branch 6 times, most recently from ea4ed11 to a76a0cf Compare August 17, 2025 01:13
@santysisi santysisi force-pushed the feature/add-twig-rate-limit-function branch from a76a0cf to bc4d692 Compare August 17, 2025 15:50
@nicolas-grekas
Copy link
Member

Thoughts: displaying a feature doesn't mean one is going to use it. But using this in template will do this: consume rate-limiter tokens while the feature has not been used yet.

I'd be fine with a helper that decides whether some token is consumed. But consuming should be done when actually consuming a feature (eg in the rate-limited API call / form handler)

@santysisi
Copy link
Contributor Author

Hi 👋 Thanks a lot for your feedback!

I completely agree with your point. Ideally, we should separate the check from the consumption of rate-limiter tokens.

That said, there's currently a small limitation: the LimiterInterface only provides consume(), reserve(), and reset(), but doesn't include a way to check availability without consuming tokens. There is currently an open issue about this limitation

If you're okay with it, I'd be happy to work on a new method (e.g., canAccess() or isAvailable()) for the classes that implement LimiterInterface.

For Symfony 8.0, this could involve updating the interface directly, and for 7.4, I could add the method only to the current implementations (without modifying the interface, to keep it BC).

Once that's in place, I can update this Twig feature to follow your suggestion and support something like:

{% set can_access = rate_limit('anonymous_api', expression('request.getClientIp()')) %}

This would simply check access without consuming any tokens or perhaps we could add an optional parameter to control whether tokens should be consumed. 🤔

What do you think?

Thanks again for the suggestion! ❤️

@nicolas-grekas
Copy link
Member

/cc @wouterj maybe? WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants