1 00:00:00,730 --> 00:00:04,170 One of the most commonly overlooked parts of designing an API is 2 00:00:04,170 --> 00:00:05,650 proper error messaging. 3 00:00:05,650 --> 00:00:08,880 We all get excited to get our API out the door and we forget to take 4 00:00:08,880 --> 00:00:12,390 care of informing callers of our API about errors that they might encounter. 5 00:00:13,810 --> 00:00:17,610 One of the reasons for this is because we as JAVA developers get so 6 00:00:17,610 --> 00:00:20,820 used to letting exceptions take care of the, well, 7 00:00:20,820 --> 00:00:23,020 exceptional things that could happen in our code. 8 00:00:23,020 --> 00:00:27,030 Now the problem is, clients don't get the benefit of reading our stack trace or 9 00:00:27,030 --> 00:00:30,070 the error messages, they make no sense out of context. 10 00:00:30,070 --> 00:00:32,840 Let's see what we can do about better communicating when things go south. 11 00:00:34,155 --> 00:00:39,335 Okay, so remember Spark has a pretty handy way to handle specific exceptions. 12 00:00:39,335 --> 00:00:43,025 So let's go ahead and create our own exception and have Spark capture it and 13 00:00:43,025 --> 00:00:45,315 send out a client-friendly error message. 14 00:00:45,315 --> 00:00:48,435 It's kind of the best practice here, right? 15 00:00:48,435 --> 00:00:51,195 First, let's go ahead and make a new exception class. 16 00:00:51,195 --> 00:00:57,785 So in this exc folder here, which is for exception, let's make one called ApiError. 17 00:00:57,785 --> 00:01:00,270 It'll kinda be the parent class for all this. 18 00:01:00,270 --> 00:01:05,300 So ApiError is going to extend a Runtime exception. 19 00:01:05,300 --> 00:01:09,510 And what that's gonna allow us to do is make sure that we don't need to 20 00:01:09,510 --> 00:01:12,720 declare it on places where the ApiError might be thrown. 21 00:01:12,720 --> 00:01:15,100 We can throw it wherever because it really is a Runtime error. 22 00:01:16,550 --> 00:01:20,710 So one of the things we want to make sure is that a status is stored. 23 00:01:20,710 --> 00:01:26,580 So, we'll do a final int status and we're going to 24 00:01:26,580 --> 00:01:31,960 make a new ApiError here and we'll pass in a status and 25 00:01:31,960 --> 00:01:37,080 we'll also pass in a message and that will be the message from API. 26 00:01:37,080 --> 00:01:40,320 So first things first, we're gonna call upCall 27 00:01:40,320 --> 00:01:44,110 to the RuntimeExceptions constructor, and it only takes one message and 28 00:01:44,110 --> 00:01:48,220 that's msg so we'll do super(msg) and that will setup everything. 29 00:01:48,220 --> 00:01:53,470 And then we'll say this.status = status. 30 00:01:53,470 --> 00:01:55,330 Okay, and then let's go ahead and add a getter. 31 00:01:56,750 --> 00:01:58,720 For that one status bit. 32 00:01:58,720 --> 00:02:03,250 So now we have an exception here that we can throw wherever we want. 33 00:02:04,670 --> 00:02:09,320 So over in our API, let's scroll down here to the bottom and right before after, 34 00:02:09,320 --> 00:02:11,420 let's make one for exception. 35 00:02:11,420 --> 00:02:12,330 Order doesn't really matter. 36 00:02:12,330 --> 00:02:14,270 I just think it's kinda nice to try to find it there. 37 00:02:14,270 --> 00:02:19,670 So remember the way that this works is you say when this error happens, 38 00:02:19,670 --> 00:02:24,120 something of this ApiError.class happens, 39 00:02:24,120 --> 00:02:29,820 we are going to get back an exception, a request and a response. 40 00:02:31,600 --> 00:02:38,980 And so that lambda there is going to here, adhere to this. 41 00:02:38,980 --> 00:02:42,060 So what we're gonna want to do is we're going to 42 00:02:43,210 --> 00:02:47,370 cast that exception that comes in here to an ApiError. 43 00:02:48,440 --> 00:02:51,580 By default, it's just an exception but because we've extended it we can do this. 44 00:02:51,580 --> 00:02:55,100 So we're gonna cast it so that we can get to the meat of the stuff we want there. 45 00:02:55,100 --> 00:02:58,140 So let's go ahead, and we'll build a map just like we did before. 46 00:02:58,140 --> 00:03:02,750 We're gonna build a map of string and object this time. 47 00:03:02,750 --> 00:03:07,010 Because one's gonna be a string, and one's gonna be an integer, right? 48 00:03:07,010 --> 00:03:12,010 So we're gonna make a jsonMap, and we're gonna call it new HashMap. 49 00:03:12,010 --> 00:03:15,570 We're gonna use the diamond method there to not redeclare. 50 00:03:17,500 --> 00:03:19,540 And that's what we have. 51 00:03:19,540 --> 00:03:20,650 Okay. 52 00:03:20,650 --> 00:03:23,000 So inside that map, we want to put a couple of things, right? 53 00:03:23,000 --> 00:03:25,380 We want to put the status, 54 00:03:26,520 --> 00:03:30,995 which we can get from our getter because our error has a get status. 55 00:03:30,995 --> 00:03:33,050 Right. 56 00:03:33,050 --> 00:03:36,700 And we also want to put in here our message. 57 00:03:37,900 --> 00:03:39,070 And we will put that in. 58 00:03:40,180 --> 00:03:42,990 Let's call the field errorMessage in the json, right? 59 00:03:42,990 --> 00:03:45,870 And we'll put err.getMessage, and 60 00:03:45,870 --> 00:03:48,650 that was inherited, of course, from runtime exception. 61 00:03:51,380 --> 00:03:56,270 Now unfortunately, here in the exception handler the after doesn't run. 62 00:03:56,270 --> 00:04:02,290 So we should probably make sure that we set the type to be application, 63 00:04:02,290 --> 00:04:03,420 except with two p's. 64 00:04:03,420 --> 00:04:09,020 Application json and also we should set the status. 65 00:04:09,020 --> 00:04:11,220 That way they can not only use the message, but 66 00:04:11,220 --> 00:04:13,330 they could also just use the status codes. 67 00:04:13,330 --> 00:04:16,500 One more time we're gonna use that to get status. 68 00:04:17,940 --> 00:04:18,440 Cool. 69 00:04:20,140 --> 00:04:23,750 And then remember we're gonna need to return this out but 70 00:04:23,750 --> 00:04:26,760 it doesn't have a response transformer the exception handler doesn't. 71 00:04:26,760 --> 00:04:32,530 So we're gonna say res.body and we're gonna call our gsn.toJson and 72 00:04:32,530 --> 00:04:36,390 we're gonna pass in our json/Map so 73 00:04:36,390 --> 00:04:41,620 that will return a json string to the body and it knows that it's application json. 74 00:04:41,620 --> 00:04:44,650 Okay, so, we have an API error class, 75 00:04:44,650 --> 00:04:47,880 every time we throw it and we pass it a status code and 76 00:04:47,880 --> 00:04:52,270 with a message it's gonna get caught here and return and pretty json. 77 00:04:52,270 --> 00:04:54,830 Let's add one, so this to do here. 78 00:04:54,830 --> 00:04:56,920 What if this is not found? 79 00:04:56,920 --> 00:05:00,070 Let's go ahead and let's take care of that cause, what if, that's not found, so 80 00:05:00,070 --> 00:05:03,300 we'll do that, so let's say if Course is null. 81 00:05:05,970 --> 00:05:10,610 You want to throw a new ApiError. 82 00:05:10,610 --> 00:05:11,480 And that's a 404, right? 83 00:05:11,480 --> 00:05:17,490 We can say, Could not find course with id. 84 00:05:19,260 --> 00:05:21,180 Very nice message, right? 85 00:05:21,180 --> 00:05:22,580 Whatever they passed in there. 86 00:05:22,580 --> 00:05:25,720 Cool. So let's flip over to postman. 87 00:05:27,980 --> 00:05:29,310 Cool. So 88 00:05:29,310 --> 00:05:33,640 we actually have left up here before this course for before it was returning a null. 89 00:05:33,640 --> 00:05:35,600 I'm gonna go ahead. I'm gonna send that again. 90 00:05:36,720 --> 00:05:40,630 And remember the thing about the server. 91 00:05:40,630 --> 00:05:46,580 You need to restart the server, facepalm, so I am going to stop the server here. 92 00:05:48,060 --> 00:05:54,890 Close All and I am going to run API here. 93 00:05:57,230 --> 00:05:58,370 Okay so the server's running. 94 00:05:58,370 --> 00:06:01,550 Let's go ahead and click send and boom here we go. 95 00:06:01,550 --> 00:06:03,160 We got a 404. 96 00:06:03,160 --> 00:06:06,150 It couldn't be found and that's very pretty. 97 00:06:06,150 --> 00:06:09,680 Could not find course with id of 42, status 404. 98 00:06:09,680 --> 00:06:12,370 Excellent, alright, you know what else? 99 00:06:12,370 --> 00:06:14,990 Our test should pass too, right? 100 00:06:14,990 --> 00:06:18,290 So, let's flip back over to our tests. 101 00:06:18,290 --> 00:06:23,500 We'll say run all tests, all in course reviews. 102 00:06:23,500 --> 00:06:25,400 We'll run it, and bam. 103 00:06:25,400 --> 00:06:29,410 Missing course returns not found status is passing. 104 00:06:29,410 --> 00:06:30,160 Awesome. 105 00:06:30,160 --> 00:06:32,780 Now we know that whenever we see an exceptional bit, 106 00:06:32,780 --> 00:06:36,710 we can just toss an API error, and they will be sent to the API user. 107 00:06:36,710 --> 00:06:38,790 We can also properly test them. 108 00:06:38,790 --> 00:06:42,510 Wouldn't it be so great to have one of the exception handlers in real life? 109 00:06:42,510 --> 00:06:44,856 Like you could make a mistake and something would catch it and 110 00:06:44,856 --> 00:06:46,240 explain why the mistake happened. 111 00:06:46,240 --> 00:06:48,360 It would solve so many arguments. 112 00:06:48,360 --> 00:06:49,180 You know what? 113 00:06:49,180 --> 00:06:52,140 Why don't we practice a little recall by building out the reviews 114 00:06:52,140 --> 00:06:53,340 portion of the API. 115 00:06:53,340 --> 00:06:57,020 If you can't locate the knowledge, let's just have you throw out a 404 error. 116 00:06:57,020 --> 00:06:57,850 Knowledge not found