# Announcing MCP Authentication: secure your MCP servers with PropelAuth

Recently, we were adding some new functionality to our [dashboard](https://docs.propelauth.com/overview/dashboard?ref=propelauth.mymidnight.blog), and we wanted an experience like this:

The basic features are:

- The toggle should make an external request when clicked to change the setting
- While the request is being made, a loading spinner should appear next to the toggle
- If the request succeeds, a check mark is displayed
- The toggle should update **optimistically**, meaning it assumes the request will succeed
- If the request fails, a red X is displayed and the toggle switches back to the current state

## Using useQuery and useMutation

If our whole dashboard was just this one toggle, this would be a simpler challenge. However, we also fetch and update other values.

To manage our state, we use [React Query](https://tanstack.com/query/latest?ref=propelauth.mymidnight.blog), specifically `useQuery` and `useMutation`.

If you haven’t used it before, `useQuery` enables a straightforward interface for fetching data:

```jsx
const {isLoading, error, data} = useQuery("config", fetchConfig)
```

and it comes with caching, re-fetching options, synchronizing state across your application, and [more](https://tanstack.com/query/latest/docs/react/overview?ref=propelauth.mymidnight.blog).

`useMutation` , as you probably expect, is the **write** to `useQuery`'s **read**. The “Hello, World” of `useMutation` looks like this:

```tsx
const { mutate } = useMutation({
    mutationFn: (partialConfigUpdate: Partial<Config>) => {
        return axios.patch('/config', partialConfigUpdate)
    },
})

// later on
const handleSubmit = (e) => {
    e.preventDefault();
    mutate({new_setting_value: e.target.checked});
}
```

In this UI, we are using a `patch` request to update some subset of our `Config`. The only problem is that with that code snippet alone, our UI won’t immediately update to reflect the new state.

## Optimistic Updates

`useMutation` has a few lifecycle hooks that we can use to update our data:

```jsx
useMutation({
    mutationFn: updateConfig,

onMutate: (partialConfigUpdate) => {
        return { foo: "bar" }
    },
    onSuccess: (mutationResponse, partialConfigUpdate, context) => {
    },
    onError: (err, partialConfigUpdate, context) => {
    },
    onSettled: (mutationResponse, err, partialConfigUpdate, context) => {
    },
})
```

You can combine this with a [QueryClient](https://tanstack.com/query/latest/docs/react/reference/QueryClient?ref=propelauth.mymidnight.blog), which lets you interact with cached data.

To solve our issue where the UI wasn’t updating to reflect the new state, we can just invalidate the cache after it succeeds:

```jsx
useMutation({
    mutationFn: updateConfig,
    onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['config'] })
    },
})
```

While this does technically work, it relies on us making an additional request after the mutation succeeded. If that request is slow, our UI might be slow to update.

If the mutation request returns the updated config, we have another option:

```jsx
useMutation({
    mutationFn: updateConfig,
    onSuccess: (mutationResponse) => {
        queryClient.setQueryData(['config'], mutationResponse)
        queryClient.invalidateQueries({ queryKey: ['config'] })
    },
})
```

where we just set the data in the cache directly with our response.

One thing to note here though, is we do actually know the change we are making. If our config was: `{a: 1, b: 2, c: 3}` and we wanted to update `a`'s value to be 5, we don’t really need to wait for the mutation response. The thing to be careful about, however, is we need to make sure to undo our change if the mutation fails.

```jsx
useMutation({
    mutationFn: updateConfig,

onMutate: async (partialConfigUpdate) => {
        await queryClient.cancelQueries({ queryKey: ['config'] })
        const previousConfig = queryClient.getQueryData(['config'])
        queryClient.setQueryData(['config'], (oldConfig) => {
            ...oldConfig,
            ...partialConfigUpdate,
        })
        return { previousConfig }
    },
    onError: (err, partialConfigUpdate, context) => {
        queryClient.setQueryData(['config'], context?.previousConfig)
    },
    onSettled: (mutationResponse, err, partialConfigUpdate, context) => {
        queryClient.invalidateQueries({ queryKey: ['config'] })
    },
})
```

This is a little more involved, but it does update immediately and this doesn’t depend on the mutation’s response. Next, let’s add Loading, Success, and Error icons.

## Adding Loading/Success/Error Icons with useTimeout

`useMutation` does actually come with status information that we could just use directly, however, we want to control how long the ✅ and ❌ icons stay on the screen for.

We use this pattern enough times that we’ve turned it into a hook - let’s first look at the version with no timers:

```tsx
type FeedbackIndicatorStatus =
    | "loading"
    | "success"
    | "error"
    | undefined;

export const useFeedbackIndicator = () => {
    const [status, setStatus] = useState<FeedbackIndicatorStatus>();

let indicator = null;
    if (status === "loading") {
        indicator = <Loading />;
    } else if (status === "success") {
        indicator = <IconCheck />;
    } else if (status === "error") {
        indicator = <IconX />;
    }

const setLoading = () => setStatus("loading");
    const setSuccess = () => {
        setStatus("success");
        setupTimerToClearStatus();
    };
    const setError = () => {
        setStatus("error");
        setupTimerToClearStatus();
    };
    return { indicator, setLoading, setSuccess, setError };
}
```

`setTimeout` can be a little tricky to use in React because you have to make sure to clear the timeout if the component unmounts. Luckily, we don’t have to worry about any of that as there are many implementations of [useTimeout](https://mantine.dev/hooks/use-timeout/?ref=propelauth.mymidnight.blog)

```tsx
const { start: setupTimerToClearStatus } = useTimeout(
    () => setStatus(undefined),
    1000
);
```

And now, when `setupTimerToClearStatus` is called, after a second, the status is cleared and `indicator` will be null.

## Combining it into a re-usable React hook

We now have all the pieces that we need. We can use `useQuery` to fetch data. We have a version of `useMutation` that lets us optimistically update the data that `useQuery` returns. And we have a hook that displays Loading, Success, and Error indicators.

Let’s put all of that together in a single hook:

```tsx
import { useState } from "react";
import { QueryKey, useMutation, useQueryClient } from "react-query";

export function useAutoUpdatingMutation<T, M>(
    mutationKey: QueryKey,
    mutationFn: (value: M) => Promise<void>,
    updater: (oldData: T, value: M) => T
) {
    const { indicator, setLoading, setSuccess, setError } = useFeedbackIndicator();
    const queryClient = useQueryClient();

const mutation = useMutation(mutationFn,
        {
            onMutate: async (value: M) => {
                setLoading();
                await queryClient.cancelQueries(mutationKey);
                const previousData = queryClient.getQueryData<T>(mutationKey);
                if (previousData) {
                    queryClient.setQueryData(
                        mutationKey,
                        updater(previousData, value)
                    );
                }
                return { previousData };
            },
            onSuccess: () => {
                setSuccess();
            },
            onError: (err, _, context) => {
                setError();
                queryClient.setQueryData(mutationKey, context?.previousData);
            },
            onSettled: async () => {
                await queryClient.invalidateQueries(mutationKey);
            },
        }
    );

return { indicator, mutation };
}
```

That’s a lot of code, but let’s see what its like to use it:

```tsx
const { isLoading, error, data } = useQuery("config", fetchConfig)

const updater = (existingConfig: Config, partialConfigUpdate: Partial<Config>) => {
  return { ...existingConfig, ...partialConfigUpdate }
}

const { indicator, mutation } = useAutoUpdatingMutation("config", updateConfig, updater)

<div>
    {indicator}
    <Toggle type="checkbox"
            checked={data.mySetting}
            onChange={e => mutation.mutate({
                mySetting: e.target.checked
            })}
            disabled={!!indicator} />
</div>
```

Pretty straightforward, we just supply our API call and our update function and we are done. When we click the toggle, it will:

- Update the toggle’s checked state
- Disable the toggle until the request is complete
- Make a request to update `mySetting`
- If it fails, revert the toggle back to it’s original state

## Wrapping up

React Query provides some powerful abstractions for fetching and modifying data. In this example, we wanted a version of `useMutation` that both updates the server state immediately and provides a status indicator for the request itself. By using `useMutation`'s hooks, we were able to make a hook specific to our use case that can be reused for all our config updates.
