--- title: JSON API Client Features group: Library --- [useClient()](https://github.com/ServiceStack/servicestack-vue/blob/main/src/api.ts) provides managed APIs around the `JsonServiceClient` instance registered in Vue App's with: ```js app.provide('client', client) ``` Which maintains contextual information around your API calls like **loading** and **error** states, used by `@servicestack/vue` components to enable its auto validation binding. Other functionality in this provider include: ```js let { api, // Send a typed API request and return results in an ApiResult apiVoid, // Send a typed API request and return empty response in a void ApiResult apiForm, // Send a FormData API request and return results in an ApiResult apiFormVoid, // Send a FormData API request and return empty response in a void ApiResult loading, // Maintain loading state whilst API Request is in transit error, // Maintain API Error response in reactive Ref setError, // Set API error state with summary or field validation error addFieldError, // Add field error to API error state unRefs // Returns a dto with all Refs unwrapped } = useClient() ``` Typically you would need to unwrap `ref` values when calling APIs, i.e: ```js let client = new JsonServiceClient() let api = await client.api(new Hello({ name:name.value })) ``` ### api This is unnecessary in useClient `api*` methods which automatically unwraps ref values, allowing for the more pleasant API call: ```js let api = await client.api(new Hello({ name })) ``` ### unRefs But as DTOs are typed, passing reference values will report a type annotation warning in IDEs with type-checking enabled, which can be avoided by explicitly unwrapping DTO ref values with `unRefs`: ```js let api = await client.api(new Hello(unRefs({ name }))) ``` ### setError `setError` can be used to populate client-side validation errors which the [SignUp.mjs](https://github.com/LegacyTemplates/vue-mjs/blob/main/MyApp/wwwroot/Pages/SignUp.mjs) component uses to report an invalid submissions when passwords don't match: ```js const { api, setError } = useClient() async function onSubmit() { if (password.value !== confirmPassword.value) { setError({ fieldName:'confirmPassword', message:'Passwords do not match' }) return } //... } ``` ## Form Validation All `@servicestack/vue` Input Components support contextual validation binding that's typically populated from API [Error Response DTOs](/error-handling) but can also be populated from client-side validation as done above. ### Explicit Error Handling This populated `ResponseStatus` DTO can either be manually passed into each component's **status** property as done in [/Todos](https://vue-mjs.web-templates.io/TodoMvc): ```html ``` Where if you try adding an empty Todo the `CreateTodo` API will fail and populate its `store.error` reactive property with the APIs Error Response DTO which the `` component checks for to display any field validation errors matching the field in `id` adjacent to the HTML Input: ```js let store = { /** @type {Todo[]} */ todos: [], newTodo:'', error:null, async refreshTodos(errorStatus) { this.error = errorStatus let api = await client.api(new QueryTodos()) if (api.succeeded) this.todos = api.response.results }, async addTodo() { this.todos.push(new Todo({ text:this.newTodo })) let api = await client.api(new CreateTodo({ text:this.newTodo })) if (api.succeeded) this.newTodo = '' return this.refreshTodos(api.error) }, //... } ``` ### Implicit Error Handling More often you'll want to take advantage of the implicit validation support in `useClient()` which makes its state available to child components, alleviating the need to explicitly pass it in each component as seen in razor tailwind's [Contacts.mjs](https://github.com/NetCoreTemplates/razor/blob/net6/MyApp/wwwroot/Pages/Contacts.mjs) `Edit` component for its [/Contacts](https://vue-mjs.web-templates.io/Contacts) page which doesn't do any manual error handling: ```js const Edit = { template:/*html*/`
`, props:['contact'], emits:['done'], setup(props, { emit }) { const client = useClient() const request = ref(new UpdateContact(props.contact)) const colorOptions = propertyOptions(getProperty('UpdateContact','Color')) async function submit() { const api = await client.api(request.value) if (api.succeeded) close() } async function onDelete () { const api = await client.apiVoid(new DeleteContact({ id:props.id })) if (api.succeeded) close() } const close = () => emit('done') return { request, enumOptions, colorOptions, submit, onDelete, close } } } ``` This effectively makes form validation binding a transparent detail where all `@servicestack/vue` Input Components are able to automatically apply contextual validation errors next to the fields they apply to:
## Example using apiForm An alternative method of invoking APIs is to submit a HTML Form Post which can be achieved with Ajax by sending a populated `FormData` with `client.apiForm()` as done in vue-mjs's [SignUp.mjs](https://github.com/LegacyTemplates/vue-mjs/blob/main/MyApp/wwwroot/Pages/SignUp.mjs) for its [/signup](https://vue-mjs.web-templates.io/signup) page: ```js import { ref } from "vue" import { leftPart, rightPart, toPascalCase } from "@servicestack/client" import { useClient } from "@servicestack/vue" import { Register } from "../mjs/dtos.mjs" export default { template:/*html*/`
Sign Up
`, props: { returnUrl:String }, setup(props) { const client = useClient() const { setError, loading } = client const request = ref(new Register({ autoLogin:true })) /** @param email {string} */ function setUser(email) { let first = leftPart(email, '@') let last = rightPart(leftPart(email, '.'), '@') const dto = request.value dto.displayName = toPascalCase(first) + ' ' + toPascalCase(last) dto.userName = email dto.confirmPassword = dto.password = 'p@55wOrd' } /** @param {Event} e */ async function submit(e) { if (request.value.password !== request.value.confirmPassword) { setError({ fieldName: 'confirmPassword', message: 'Passwords do not match' }) return } // Example using client.apiForm() const api = await client.apiForm(new Register(), new FormData(e.target)) if (api.succeeded) { location.href = props.returnUrl || '/signin' } } return { loading, request, setUser, submit } } } ``` Which method to use is largely a matter of preference except if your form needs to upload a file in which case using `apiForm` is required. ## AutoForm Components We can elevate our productivity even further with [Auto Form Components](/vue/autoform) that can automatically generate an instant API-enabled form with validation binding by just specifying the Request DTO to create the form for, e.g: ```html ```
The AutoForm components are powered by your [App Metadata](/vue/use-metadata) which allows creating highly customized UIs from [declarative C# attributes](/locode/declarative) whose customizations are reused across all ServiceStack Auto UIs. ## Stale-While-Revalidate APIs A popular performance enhancing technique you can use to improve perceived performance between pages are to use State-While-Revalidate (SWR) APIs which can deliver just as good UX as complex SPAs with stateless full page reloads of pre-rendered HTML pages if we use SWR to fetch all the API data needed to render the page on first load: [![](/img/pages/release-notes/v6.9/diffusion-swr.gif)](https://diffusion.works) This is easily achieved in reactive Vue.js UIs by invoking API requests with the new `swr()` client API where if the same API request had been run before it will execute the callback immediately with its "stale" cached results in `localStorage` first, before invoking the callback again after receiving the API response with the latest data: ```ts import { useClient } from "@servicestack/vue" const client = useClient() const results = ref([]) const topAlbums = ref([]) //... onMounted(async () => { await Promise.all([ client.swr(request.value, api => { results.value = api.response?.results || [] //... }), client.swr(new AnonData(), async api => { topAlbums.value = api.response?.topAlbums || [] //... }), ]) }) ``` This results in UIs being immediately rendered on load and if the API response has changed, the updated reactive collections will re-render the UI with the updated data. ### swrEffect The built-in `swrEffect()` API uses Vue's `watchEffect` to detect property changes to trigger invoking the API request and returning API responses in an idiomatic `ApiResult` with a similarly pleasant declarative API without the unnecessary boilerplate: ```ts const client = useClient() //... const api = client.swrEffect(() => new Hello({ name: props.name })) ``` It also includes a built-in [debounce feature](https://www.freecodecamp.org/news/javascript-debounce-example/) where you can collapse multiple event triggers within a specified duration (like input events when a user is typing), e.g. we can initiate an API request when a user has paused briefly after 50ms with: ```ts const api = client.swrEffect(() => new Hello({ name: props.name }), { delayMs:50 }) ``` ## TypeScript Definition TypeScript definition of the API surface area and type information for correct usage of `useClient()` ```ts /** Maintain loading state whilst API Request is in transit */ const loading: Ref /** Maintain API Error in reactive Ref */ const error: Ref /** Set error state with summary or field validation error */ function setError({ message, errorCode, fieldName, errors }: IResponseStatus); /** Add field error to API error state */ function addFieldError({ fieldName, message, errorCode }: IResponseError); /** Send a typed API request and return results in an ApiResult */ async function api(request:IReturn | ApiRequest, args?:any, method?:string); /** Send a typed API request and return empty response in a void ApiResult */ async function apiVoid(request:IReturnVoid | ApiRequest, args?:any, method?:string); /** Send a FormData API request and return results in an ApiResult */ async function apiForm(request:IReturn | ApiRequest, body:FormData, args?:any, method?:string); /** Send a FormData API request and return empty response in a void ApiResult */ async function apiFormVoid(request: IReturnVoid | ApiRequest, body: FormData, args?: any, method?: string); ```