Angular Resource API - Everything You Have To Know (so far)

14.66k views3947 WordsCopy TextShare
Decoded Frontend
🔥 Advanced Angular Courses - https://bit.ly/df-courses Get Courses Now With -25% off during the Bla...
Video Transcript:
Hi. Everything you have to know, about the  brand new resource() function, in Angular 19, you can find, in this video. In this tutorial  we will build from scratch a little Angular app, where you will be able, to load data from  the server.
You will be able to reload it, or change it afterwards by adding a new user to  the list, or clear the list up. Besides that we will Implement a user search feature, where I  will demonstrate how you can handle concurrency requests, and cancel the pending request when  the new one arrives. And of course, while the data is being loaded, user will see a progress  bar making the user experience much better.
And this everything we will achieve only by using  the resource function and we will not write any single line of RxJS code. Sometimes you might  feel that things are working like magic, but no worries, I will be diving into the source code  and demystify things that might not be obvious for you. If it is a first time on my channel and  you like deep dives into Angular my name is Dmytro Mezhenskyi and on my channel you can find a lot of  advanced and unique Angular tutorials, that you can check out later, but now let's try to learn more  about this new reactive primitive called resource().
First of all, let's try to figure out why this  resource() function exist. Right after signals were introduced in Angular, a few things became  very clear immediately. Signals are reactive primitives that will be used by the Angular core  making their dependency on RxJS optional.
Another thing that we realized about Angular signals quite  quickly, is that they're very good for synchronous reactivity, but when it comes to asynchronous code  they perform not so well, and RxJS much better in those cases. And this was actually a problem in  making RxJS fully optional, because we cannot simply avoid the asynchronous code, but at the same  time, we don't have any signal based alternative that would nicely work with asynchronous code  and could handle the most popular use cases. So that we could include RxJS only when its power is  justified.
And this new reactive primitive resource(), supposed to fill this gap. So let's have a look  how it looks in practice. Before we get started, I just wanted to mention that everything you will  see in this video is related to Angular 19, so make sure that you updated your project to this  exact version and once you have done it, you will be able to import the brand new function called  resource() from the 'angular/core'.
Please also keep in mind that, in Angular 19 the resource() function,  is in developer preview mode, which means, that it is not ready for production. It might change in  the future, and it was exposed just to let you play with it, and provide feedback to Angular core  Team. All right, so let's go and create our first resource() and I'm going to name it 'users' resource()  and the resource() function itself requires a certain configuration object.
And there you can see  a bunch of properties that you can configure, there is only one required, called loader, so let's  implement it first. And the loader is a function that's supposed to asynchronously load the data  into this 'users' resource(). And this function should return a promise, or if to be more precise it  has to be something PromiseLike.
So it means that, it should not be necessary a promise, but it  should be something that has an API, similar to what promise has. I will be using a normal promise  that will be returned by the calling the fetch() function. Fetch() function it is the, part of the web  platform API, and it allows me to perform http calls to a certain server endpoint.
And then  the data, the endpoint returns, I can resolve by calling the json() method, and then the data will be  available for further usage. Alternatively you can also use the 'async/await' syntax, it is absolutely  okay. Just choose whatever style you like more.
I personally prefer this 'async/await' syntax, because  I find it more cleaner. But whatever syntax you choose, if you look up into Chrome Dev Tools inside  the network tab, you will see that the call to the users's endpoint has been executed. Which proves  that the loader() function of the user resource() has been invoked.
But how can we read the data  returned by the resource() loader? For that we have to take a closer look at what resource() function  returns. And it returns a special data structure called ResourceRef.
And you can think of it, as a  representation of the current state of a certain source, and also besides the reading the state, it  provides you with a certain API, that allows you to manipulate this state and a little bit later I  will show you how to do that, but so far let's try to read the current value from the user's resource().  And I'm going to do that inside the component template, there I have a dedicated list and here  I'm going to use the 'For Loop' and iterate through the 'users' source values, right. So there will be the  user that server returns, and I'm going to track them by using userID which is not highlighted by  the way, because we don't have strict typing so far, for our source, so let's use this chance to improve  it, and actually the type of resource() function is a generic that has two values.
The first value this  is what resource loader returns. And in my case it is array of users, and the second parameter it is  the request type. We will come to request feature a little bit later, so far let's define it as  a unknown type.
All right so now typings for our 'users resource' are much stricter. And we can get  back to the component template and now you can see that the available users properties are shown  up, and if we make any mistake, and try to access the property that doesn't exist, we will get the  compilation error. Which is actually cool.
And now inside the 'for' block I can already define a new <li>  element and there let's display maybe username. Here we go. And also the no data <li> element let's  show, when the users value is empty.
And there is nothing to show. And now if I save my change, and  reload the application, you can see the list of users, I get from the server is rendered right on  the page. But what if something goes wrong and we encounter an error during the loader() function call? 
How we can handle the error use case inside the resource()? And actually it is pretty easy to do,  because the ResourceRef provides a dedicated property called 'error' that contains an error, if  it happens inside the loader() function, or it is undefined if everything went well. So to notify  user about the error, we can go to the component template, and somewhere here, I can create dedicated  block that will be visible if our users's resource() contains the error.
And there inside I'm going  to display what exactly went wrong. And to test this scenario we can scroll a little bit down, and  let's try maybe remove entirely the endpoint URL from the fetch() function. And in such a way, we can  explicitly trigger the error.
So you can see that the error message appeared right there, exactly as  I expected. And by the way, since the usage of the fetch() function in Angular becomes kind of usual thing,  you have to keep in mind one important pitfall, when it comes to error catching. For example let's  try to simulate the use case, when we call the endpoint that doesn't exist.
So if we look up into  the console, you can see that the request failed with the status code 404, but we don't see the  corresponding error message. The thing is that when the server responses with the status code like 404 or 500 the fetch function doesn't consider it as an error. So the promise returned by the fetch()  function, will not be rejected.
And this is the reason why we don't see the error message there.  To change this behavior you can explicitly check if the server response is not okay, in this case  you simply throw an error with the corresponding message. So now if I reload the application you can  see that the error message has appeared, because server responded with the status 404.
So let's very  quickly revert this change, and use the real API URL, and let's move forward. And the next thing  I would like to discuss with you, are resource statuses. The ResourceRef has a corresponding  signal() called status, and let's try to subscribe to the changes of this signal() by using the effect()  function.
And simply console. log() the values of it. So if you save this change, and open the Chrome  Dev tools and go to the console tab, you can see resource statuses console.
loged and they are  represented by some magical numbers. Each of these numbers represent a certain resource state like  loading, reloading, resolved etc. And we can easily decode those numbers, by using a dedicated enum,  called ResourceStatuses.
That you can again import from the 'angular/core' package. This enum provides  all the possible resource statuses, so you can use it in your code in order to react to certain  ResourceStatus change and reflect this, on the UI. For example you might create a computed property, where you just track the changes inside the resource status, and check if it is in loading  state, or not.
However you should not do this for exactly loading status, because resourceRef out  of the box comes with the property isLoading(), that handles exactly this use case for you, and  if you have a look at the source code, and exact implementation of it, you can see that this is  still the same computed(), that checks if the status is 'in loading' or 'reloading' state. So I don't need  this computed() I created, and I can use isLoading() property, so I can go to component template and  show some spinner or progress bar in my template and for that, I will be using the Angular Material  progress-bar component, that I have to import and somewhere, somewhere here, I can create a dedicated  'if' block, where I will display the progress-bar component in case the source, the user source, is  in the loading status. All right, now let's try to save our change and test if everything works.
So I'm going to reload my page, and you probably you could see the progress-bar appeared, but it  happened so fast, so that's why let's open their Dev tools and try to simulate the very slow 3G  network, and yeah, you could see that the progress bar was visible for a second, and then when the  data was resolved, it disappeared so everything works as we expect. And now let's talk how we  can manipulate the state of the resource() and what API do we have for that. First of all, we can  reload our resource(), meaning that we can retrigger the loader() function and grab the latest  data from the server.
It is pretty easy to do that, because the ResourceRef has a dedicated reload()  method that you can call anytime, for example I have here a dedicated button reload, so I can add  the click event listener to this button, and when user clicks it we can reload the resource. So now  if I save my change, and try to click the reload button, you can see that my resource was reloaded,  and you could see the dedicated progress-bar here at the top. We can also change the value of the  resource locally at any time, and we have two options to do that.
We can set a new value based  on the previous value of the resource, or we can set the new value directly. So let's have a look at  them one by one. For example let's imagine that in addition to already fetched users from the server,  I want to add a new one in the beginning of this list.
So in this case I would need to add another  click event handler to the add user button, and the handler of it will be addUser() method. Here  you can already see prepared dummy user that I'm going to add to this list, and we can add this  user by using the update() method that works the same way as with normal WritableSignal(), there you  have a callback, where you take as argument the previous value of the signal, or resource. And from  there you return the new state, the new value for this signal, and in my case I have to also check  if the previous value is not undefined, because it could be undefined, right.
And if it is the case  then we return just simply array with this new user, and this is pretty much it. So we can try to  click the add user button, and we see that the new user has been added in the beginning of the user  list. And as I said before, we can also set the new value for the resource directly, and for that we  are going to use a dedicated set() method, that again works absolutely the same as for any WriteableSignal, so when we call the clear button, let's say that we set value to the empty array, and in  such a way we clear the list completely.
And now if I try to click the clear button, you can see  that all users has been removed from the list. And this is cool and I personally find this pretty  impressive what we learned so far, even though we didn't cover everything yet, so let's continue  continue and let's have a look what other API the resource function provides. And how it can help  us to implement feature like user search, so that when user types anything into the search field,  we should request server with the right searching criteria, and get back users, name of which ones  starts with the search string.
For that, we would need to create another signal() inside our user-search component, and the value of it will be an empty string by default. Also we have to bind  this signal() with the corresponding search-input and for that let's go to the component template,  find this input and there I'm going to attach the input event listener, and once user enters  a new value, we will take it from the event. target and set as a new value, for the corresponding query  signal().
You can see currently an issue with typings and I'm going to fix it the way you should never  use in your production code, and I'm going to cast it to any, inside template, just to avoid creating  a separate method, only for target type casting. I hope you will forgive me my laziness. Anyway now  the question is how we can wire up this new query signal() with our users's resource.
Because if  you try to use the query signal() directly inside the loader() function it will not work. And the  loader() function will not be re-triggered every time when query signal() changes. And to solve this  issue resource has another property called 'request'.
Where we define a function that returns a signal()  or group of signals() changes of which one will call the re-execution of the loader() functions, and  their new values will be provided to the loader() function as an argument. So you can destructure  the loader config, and take the property request from there, that contains the value the request  function returns. And then you can use it anywhere inside your loader() function.
And if we save this  change, and try to type any string into the user search, you can see that every time the search  query changes, we re-execute the HTTP call, with a proper name like parameter, and get the proper  values from backend. If your loader() function depends on values from multiple signals(), then you  can return from the request() function, not a single signal(), but you can return an object of signals(). So  in my case it could be an object that has property 'query' and the value of it is a query signal(). 
And then I could access this query, by using again the request object, and they're just using the  query key. Again, you can see some typings issues this is because earlier in this video we defined  the request type as an unknown, but now we know what the type is, and this is the object with a  key 'query' and the value type of string. So we can define it here, and the error has disappeared. 
All right now let's talk about concurrency request. For example what will happen if I fire a new HTTP  call, while the previous one is pending? And as you can see by default, all those requests will be  executed, which might remind you the behavior of the RxJS operator called mergeMap().
This however  might not be the behavior you expect. And you would like to cancel the pending request when  the new one arrives, and in this case you have to utilize a thing, called abortSignal(). That you can  destructure from the loader() function argument, and use it in order to abort asynchronous operations  like the fetch() function call.
You simply have to provide an additional config for it, and there for  the parameter signal() you provide the AbortSignal. And after that, when you save this change, and try  to perform multiple HTTP calls in a row, you can see that the pending request has been cancelled,  and completely executed only the latest request. Which is actually very similar to the behavior  of the switchMap() RxJS operator.
I'm pretty sure that for many of you, this everything looks like  magic, and it brings a lot of confusion, like 'What this AbortSignal is? How it prevents the fetch call  etc? '.
So let me take another couple of minutes, to demystify all this things for you. The first thing  you have to understand, is that the AbortSignal as well as the signal() property in the fetch() function  config. They have nothing to do with Angular signals().
This is completely different concept.  The AbortSignal allows you to abort execution of asynchronous operations in JavaScript. And  it is a part of AbortController class, that is built-in into JavaScript, so you can instantiate  your own instance, of the AbortController, and the AbortSignal from this class instance, you can  provide instead of the AbortSignal I received in the reload() function.
And whenever I call the  abort() method of my custom AbortController, it will notify the fetch() function to cancel these  asynchronous operations. We could even wrap this abort() method call in some setTimeout() and call  it after, I don't know 500 milliseconds, and define the reason that, the request takes too much time, a  bit silly example, yeah. But it demonstrates the idea how it works.
So now if the request takes more  than 500 milliseconds, then we will see an error, because after 500 milliseconds, the abort() method  involved, and it triggers the AbortSignal, that eventually cancels the HTTP request. So as you can  see there is no magic here at all. And under the hood of the resource() function happens pretty much  the same thing, and we can make sure it by diving into the source code of the resource() function.
All  right, so this is the source code of the resource() function and what it does, it returns a new  instance of the WritableResourceImplementation class, which is right here, and you can see it has a  private property called pendingController, that's supposed to be an instance of the AbortController.  And here's a method called loadEffect() and this is the method that actually involves our loader()  function, right. And somewhere here, there is new instance of the AbortController is created,  and from there, they structured the signal() property.
Renamed to the AbortSignal and it is provided  as an argument for the loader() function. This is exactly this function and this is exactly that  AbortSignal and then, at some point, the abort() method of the AbortController is called which  fires the AbortSignal. We can, by the way, have a look when it actually happens.
So let's try to  find where the abort() is called, so it is inside, okay, this is the private method that actually  calls the abort() method, and we can have a look at which places it was called. So the first  place this is on local value, and this method is executed when we locally update the value  of the resource. For example when we call set() method or update(), so in this case the AbortSignal  will be fired, then it happens when the writable resource is destroyed.
Which is pretty obvious.  And then it happens also you can see, in the beginning of the calling loadEffect() method, and  exactly this causes this switchMap() effect when we use the AbortSignal with our fetch() function.  You might be also curious 'why do we get an error when we abort the request by using our custom  AbortController and why doesn't happen when we use the AbortController that we destructure  from the loader() function?
'. And this is because under the hood the function is called inside a try/catch  block, and when the error goes to the catch block. Here happens the check if the operation caused by  abortion, then happens simple return, and we simply do not reach this step, when the error status  is setup, and by the way, this is something you could Implement in your custom abortion logic.
If  you need it of course. But I must also say that I'm not sure that this will be the recommended  way to implement the custom abortions logic. It is just too early state to say something certain,  maybe in the future we will see a dedicated API, I don't know, I did it just to demonstrate what  is hidden behind the AbortSignal, so it doesn't look like a magic for you.
And if you don't mind  I will just revert things back, a little bit. So far this was everything I would like to mention  about the resource() function, and please remember that this is still an experimental feature. And I  think it's just the beginning of the new entire massive Angular chapter, and later we will see more  features and more patterns built on top of this reactive primitive.
But maybe you have different  opinion, so let's discuss it in the comment section below, or you can join our cozy Discord community of  more than 1,000 Angular developers, around the world. And we are looking forward meeting exactly you, as well.  If you like the way how I explain stuff by diving deep into the Angular source code, and if you would  like to support the release of new such videos, right here, you can see all available options how  you could do that, and whatever option you choose I am so grateful for that, and I appreciate your  support a lot.
Copyright © 2025. Made with ♥ in London by YTScribe.com