1
00:00:00,000 --> 00:00:02,000
Now the image should be stored

2
00:00:02,000 --> 00:00:05,000
on the file system, not in the database

3
00:00:05,000 --> 00:00:08,000
because storing files in databases is a bad idea.

4
00:00:08,000 --> 00:00:09,000
It's bad for performance

5
00:00:09,000 --> 00:00:12,000
because databases simply aren't built for that.

6
00:00:13,000 --> 00:00:15,000
Instead, I wanna store the uploaded files

7
00:00:15,000 --> 00:00:19,000
in that public folder because any images stored there

8
00:00:19,000 --> 00:00:23,000
in that images folder will be publicly available

9
00:00:23,000 --> 00:00:26,000
so they can be rendered on the screen without problems.

10
00:00:27,000 --> 00:00:32,000
And therefore we'll now need to perform a couple of steps.

11
00:00:32,000 --> 00:00:35,000
First, I'll start by getting the extension

12
00:00:35,000 --> 00:00:36,000
of the uploaded image

13
00:00:36,000 --> 00:00:40,000
because it could be a JPEG or PNG file.

14
00:00:40,000 --> 00:00:43,000
So I'll dig into the meal image.

15
00:00:43,000 --> 00:00:45,000
Keep in mind that we're storing that image,

16
00:00:45,000 --> 00:00:49,000
which we got from the form under that image key.

17
00:00:52,000 --> 00:00:54,000
So I'm getting access to that here.

18
00:00:56,000 --> 00:01:00,000
And then on that I'll call split to split it on the dot

19
00:01:00,000 --> 00:01:03,000
and pop the last element, which will be the file extension.

20
00:01:04,000 --> 00:01:09,000
Next, I also want to generate a unique file name

21
00:01:09,000 --> 00:01:12,000
and not use the file name of the user.

22
00:01:12,000 --> 00:01:14,000
So therefore here what I'll do

23
00:01:14,000 --> 00:01:17,000
is I'll generate a string with those tactics

24
00:01:17,000 --> 00:01:20,000
using dot string template literal notation,

25
00:01:20,000 --> 00:01:21,000
which we can use in JavaScript

26
00:01:23,000 --> 00:01:28,000
to create a file name that uses .slug here.

27
00:01:29,000 --> 00:01:33,000
So meal.slug actually, and then a dot,

28
00:01:33,000 --> 00:01:35,000
and then the extension.

29
00:01:37,000 --> 00:01:39,000
So now with that we got the file name.

30
00:01:39,000 --> 00:01:42,000
Now we need to write that to a file in that public folder.

31
00:01:43,000 --> 00:01:46,000
And we can do that with help of an API

32
00:01:46,000 --> 00:01:48,000
provided by node JS.

33
00:01:50,000 --> 00:01:53,000
To be precise, the file system API.

34
00:01:53,000 --> 00:01:57,000
And for that I'll import fs from node:fs.

35
00:01:59,000 --> 00:02:03,000
This allows us to work with the file system.

36
00:02:03,000 --> 00:02:08,000
We can then use this fs module to call createWriteStream,

37
00:02:08,000 --> 00:02:10,000
which will create a stream that allows us

38
00:02:10,000 --> 00:02:12,000
to write data to a certain file.

39
00:02:13,000 --> 00:02:17,000
Now createWriteStream then needs a path to default,

40
00:02:17,000 --> 00:02:18,000
which you wanna write.

41
00:02:18,000 --> 00:02:20,000
And it'll then return a stream object

42
00:02:20,000 --> 00:02:23,000
which you can then use to write to that path.

43
00:02:24,000 --> 00:02:28,000
So regarding that path, I'll create a dynamic string here

44
00:02:28,000 --> 00:02:31,000
and I'll target public/images.

45
00:02:31,000 --> 00:02:35,000
So this images folder here in this public folder,

46
00:02:38,000 --> 00:02:41,000
/ and then the fileName

47
00:02:41,000 --> 00:02:44,000
to which you wanna write must be included in that path.

48
00:02:45,000 --> 00:02:47,000
So we can inject that fileName here,

49
00:02:47,000 --> 00:02:50,000
this fileName which we constructed earlier.

50
00:02:51,000 --> 00:02:54,000
With that, we then get a stream which we can store

51
00:02:54,000 --> 00:02:56,000
in a constant or variable

52
00:02:56,000 --> 00:02:58,000
in which we can use to write to that path.

53
00:02:58,000 --> 00:03:01,000
So to that image in that path.

54
00:03:01,000 --> 00:03:03,000
Now we can write to that stream

55
00:03:03,000 --> 00:03:06,000
by calling the write method on it.

56
00:03:07,000 --> 00:03:11,000
And this write method then wants a chunk.

57
00:03:11,000 --> 00:03:13,000
Now what's that chunk?

58
00:03:13,000 --> 00:03:15,000
In case of our image here,

59
00:03:15,000 --> 00:03:17,000
it means that we should convert the image

60
00:03:17,000 --> 00:03:21,000
to a so-called buffer to a bufferedImage you could say,

61
00:03:22,000 --> 00:03:24,000
which might sound fancy,

62
00:03:24,000 --> 00:03:27,000
but which thankfully isn't too difficult to do.

63
00:03:27,000 --> 00:03:31,000
You can use that image object, which we have here,

64
00:03:31,000 --> 00:03:34,000
which we're getting from our form after all.

65
00:03:34,000 --> 00:03:37,000
So that image that's being stored under that image key.

66
00:03:38,000 --> 00:03:43,000
And that object has an arrayBuffer method you can call,

67
00:03:43,000 --> 00:03:44,000
which will give you such a buffer,

68
00:03:44,000 --> 00:03:46,000
which we need here for the write method.

69
00:03:47,000 --> 00:03:50,000
The only tricky thing here is that arrayBuffer

70
00:03:50,000 --> 00:03:52,000
actually will give you a promise

71
00:03:52,000 --> 00:03:54,000
that eventually resolves to that buffer

72
00:03:54,000 --> 00:03:56,000
and therefore we must await this here.

73
00:03:57,000 --> 00:04:00,000
And in order to use await here, we should add async

74
00:04:00,000 --> 00:04:02,000
in front of the saveMeal function here.

75
00:04:04,000 --> 00:04:07,000
And with that, we then got this bufferedImage.

76
00:04:07,000 --> 00:04:09,000
Now this is of type arrayBuffer

77
00:04:09,000 --> 00:04:12,000
and write actually wants a regular buffer.

78
00:04:12,000 --> 00:04:15,000
So therefore here we have to call Buffer from

79
00:04:15,000 --> 00:04:17,000
and past this arrayBuffer

80
00:04:17,000 --> 00:04:20,000
so this bufferedImage to it, like this.

81
00:04:22,000 --> 00:04:26,000
By the way, one other little error I just spotted is here

82
00:04:26,000 --> 00:04:30,000
where I split my image name to get the extension.

83
00:04:30,000 --> 00:04:35,000
Here, we have to access meal.image.name.split

84
00:04:36,000 --> 00:04:39,000
because meal.image is simply that image

85
00:04:39,000 --> 00:04:43,000
which we get from the form so that image object

86
00:04:43,000 --> 00:04:46,000
that's automatically generated by the browser,

87
00:04:46,000 --> 00:04:48,000
and that object will indeed

88
00:04:48,000 --> 00:04:51,000
have a couple of helpful properties.

89
00:04:51,000 --> 00:04:52,000
One of them is the name property,

90
00:04:52,000 --> 00:04:54,000
which carries the name of the image file

91
00:04:54,000 --> 00:04:56,000
that was uploaded.

92
00:04:57,000 --> 00:05:00,000
So that's something we should change here.

93
00:05:00,000 --> 00:05:02,000
But with that back to the stream,

94
00:05:02,000 --> 00:05:07,000
now we're writing this buffer to this path,

95
00:05:07,000 --> 00:05:09,000
including this file

96
00:05:09,000 --> 00:05:13,000
and this write method now also takes a second argument.

97
00:05:13,000 --> 00:05:16,000
The first argument is the thing you wanna write.

98
00:05:16,000 --> 00:05:19,000
The second argument is a function that will be executed

99
00:05:19,000 --> 00:05:21,000
once it's done writing.

100
00:05:22,000 --> 00:05:24,000
And here we get an error argument,

101
00:05:24,000 --> 00:05:26,000
which is null if everything worked,

102
00:05:26,000 --> 00:05:30,000
but which will hold some error information

103
00:05:30,000 --> 00:05:31,000
if something went wrong.

104
00:05:33,000 --> 00:05:38,000
So for here, we should check if error is truthy.

105
00:05:38,000 --> 00:05:39,000
So if we have an error,

106
00:05:39,000 --> 00:05:42,000
and in that case I wanna throw a new Error

107
00:05:42,000 --> 00:05:47,000
where I say, Saving image failed, or something like this.

108
00:05:48,000 --> 00:05:53,000
And with that we wrote the image to this file.

109
00:05:53,000 --> 00:05:57,000
Now we just need to store the overall data in the database.

110
00:05:57,000 --> 00:06:01,000
And for that I'll start by overriding the image

111
00:06:01,000 --> 00:06:06,000
that's stored in my meal object with a path to the image

112
00:06:06,000 --> 00:06:07,000
where we stored it.

113
00:06:07,000 --> 00:06:11,000
Because I don't wanna store the image file itself

114
00:06:11,000 --> 00:06:14,000
in a database, databases are not built for that,

115
00:06:14,000 --> 00:06:17,000
instead I just wanna store the path.

116
00:06:17,000 --> 00:06:20,000
Hence I'll overwride the image object in my meal

117
00:06:20,000 --> 00:06:22,000
with that path here,

118
00:06:22,000 --> 00:06:26,000
though there you should actually remove this public segment

119
00:06:27,000 --> 00:06:30,000
because all requests for images will be sent

120
00:06:30,000 --> 00:06:33,000
to the public folder automatically anyways.

121
00:06:33,000 --> 00:06:37,000
Or put in other words, the content of the public folder

122
00:06:37,000 --> 00:06:41,000
will be served as if it were served on the root level

123
00:06:41,000 --> 00:06:43,000
of your Server anyways

124
00:06:43,000 --> 00:06:45,000
and therefore public shouldn't be included here

125
00:06:45,000 --> 00:06:48,000
so that it won't be included later

126
00:06:48,000 --> 00:06:50,000
when requests for that image will be sent.

127
00:06:53,000 --> 00:06:55,000
But with that, we now finished the preparations

128
00:06:55,000 --> 00:06:58,000
for this meal object

129
00:06:58,000 --> 00:07:00,000
and we can now save it in a database.

130
00:07:01,000 --> 00:07:04,000
And to save it, I'll use my db object

131
00:07:04,000 --> 00:07:06,000
and then prepare another statement.

132
00:07:08,000 --> 00:07:11,000
And here I'll prepare a statement

133
00:07:11,000 --> 00:07:14,000
and I'll use this template literal notation

134
00:07:14,000 --> 00:07:16,000
to split it across multiple lines

135
00:07:16,000 --> 00:07:19,000
where I wanna insert some data into the meals table

136
00:07:21,000 --> 00:07:25,000
and I wanna insert data into the following fields,

137
00:07:25,000 --> 00:07:28,000
into the title, into the summary field,

138
00:07:28,000 --> 00:07:33,000
into the instructions field, into the creator field

139
00:07:33,000 --> 00:07:35,000
and the creator email field

140
00:07:37,000 --> 00:07:39,000
and into the image and slug fields.

141
00:07:39,000 --> 00:07:42,000
So basically into all fields that we configured

142
00:07:42,000 --> 00:07:45,000
when we created the table except for the id,

143
00:07:45,000 --> 00:07:48,000
because that will be populated automatically.

144
00:07:50,000 --> 00:07:53,000
Now we must specify the values

145
00:07:53,000 --> 00:07:56,000
that should be inserted into those fields.

146
00:07:56,000 --> 00:08:00,000
And here you could directly inject those values,

147
00:08:00,000 --> 00:08:02,000
but this is not recommended

148
00:08:02,000 --> 00:08:04,000
because that approach would be vulnerable

149
00:08:04,000 --> 00:08:06,000
to SQL injection attacks.

150
00:08:07,000 --> 00:08:09,000
Instead, you should use those placeholders

151
00:08:09,000 --> 00:08:14,000
one for every field though to make this a bit easier,

152
00:08:14,000 --> 00:08:16,000
you can also use another syntax here

153
00:08:16,000 --> 00:08:18,000
supported by better-sqlite,

154
00:08:18,000 --> 00:08:23,000
which I also used in the initdb.js file, this syntax here.

155
00:08:24,000 --> 00:08:29,000
You can target specific fields by their name, like this.

156
00:08:30,000 --> 00:08:34,000
And then later just pass an object to the run function,

157
00:08:34,000 --> 00:08:37,000
which we will call on this prepared statement

158
00:08:37,000 --> 00:08:39,000
and better-sqlite.

159
00:08:39,000 --> 00:08:43,000
The package we're using to execute this command here

160
00:08:43,000 --> 00:08:45,000
will then look at those property names

161
00:08:45,000 --> 00:08:47,000
in that object you're passing to it

162
00:08:47,000 --> 00:08:51,000
to extract the values stored under those property names

163
00:08:51,000 --> 00:08:53,000
and it will then use those values

164
00:08:53,000 --> 00:08:55,000
to store them in those fields.

165
00:08:57,000 --> 00:09:00,000
However, you must make sure that the order you got here

166
00:09:00,000 --> 00:09:02,000
is the same as you have it here

167
00:09:02,000 --> 00:09:04,000
and therefore I'll reorder that

168
00:09:04,000 --> 00:09:08,000
and put title first, then the summary,

169
00:09:08,000 --> 00:09:12,000
then the instructions, then the creator,

170
00:09:12,000 --> 00:09:16,000
then the creator email, and then the image and the slug.

171
00:09:16,000 --> 00:09:19,000
I also will remove that trailing comma

172
00:09:19,000 --> 00:09:21,000
and instead add one here.

173
00:09:21,000 --> 00:09:24,000
Getting those commas right is really important.

174
00:09:24,000 --> 00:09:26,000
You will get errors if you don't do it.

175
00:09:28,000 --> 00:09:31,000
Well, and then we can call run on that statement

176
00:09:31,000 --> 00:09:34,000
and pass our meal object to it.

177
00:09:34,000 --> 00:09:36,000
And thanks to this syntax we're using here,

178
00:09:36,000 --> 00:09:40,000
the data for those fields will automatically be extracted.

179
00:09:42,000 --> 00:09:43,000
And that's it then.

180
00:09:43,000 --> 00:09:46,000
This is a saveMeal function

181
00:09:46,000 --> 00:09:48,000
that will store both a file

182
00:09:48,000 --> 00:09:50,000
and the data in the database.

183
00:09:52,000 --> 00:09:56,000
With that done, we can go back to our Server Action

184
00:09:57,000 --> 00:10:00,000
and there, instead of console logging that meal,

185
00:10:00,000 --> 00:10:01,000
we can call saveMeal.

186
00:10:02,000 --> 00:10:05,000
So we can call this function on which we just worked.

187
00:10:06,000 --> 00:10:09,000
Now saveMeal will return a promise,

188
00:10:09,000 --> 00:10:12,000
so we can also add async here

189
00:10:12,000 --> 00:10:14,000
and then use a wait here.

190
00:10:14,000 --> 00:10:17,000
Though at the moment, I then don't have any follow-up action

191
00:10:17,000 --> 00:10:20,000
that should be executed here, but that will come later.

192
00:10:22,000 --> 00:10:25,000
With that though, we're making sure to call saVeMeal here

193
00:10:25,000 --> 00:10:27,000
in the Server Action.

194
00:10:27,000 --> 00:10:30,000
Therefore, now we should pass the meal to saveMeal.

195
00:10:30,000 --> 00:10:32,000
And if we now save everything

196
00:10:32,000 --> 00:10:35,000
and you got that development server up and running,

197
00:10:35,000 --> 00:10:38,000
if you go to that share meal page,

198
00:10:38,000 --> 00:10:41,000
you should be able to enter some data here,

199
00:10:43,000 --> 00:10:47,000
like this and then pick an image

200
00:10:49,000 --> 00:10:50,000
and click Share Meal.

201
00:10:51,000 --> 00:10:56,000
And with that, nothing happens here, which is a good sign.

202
00:10:56,000 --> 00:10:57,000
At least we got no error.

203
00:10:57,000 --> 00:11:01,000
But of course we also don't know whether it worked.

204
00:11:01,000 --> 00:11:03,000
Now we'll take care of that in a second.

205
00:11:03,000 --> 00:11:07,000
For the moment, we can take a look at the public folder,

206
00:11:07,000 --> 00:11:11,000
and there indeed, I find my uploaded image.

207
00:11:11,000 --> 00:11:14,000
So that's a good hint that it seemed to have worked.

208
00:11:15,000 --> 00:11:19,000
I also got no error here in my terminal.

209
00:11:19,000 --> 00:11:21,000
And if we go back to the page

210
00:11:21,000 --> 00:11:23,000
and browse our meals again,

211
00:11:23,000 --> 00:11:25,000
we should also find our meal here.

212
00:11:26,000 --> 00:11:28,000
And that therefore proves that it did work.

213
00:11:29,000 --> 00:11:32,000
Now to provide a better user experience,

214
00:11:32,000 --> 00:11:34,000
we might want to redirect the user

215
00:11:34,000 --> 00:11:36,000
once the meal data has been submitted.

216
00:11:37,000 --> 00:11:40,000
And for that we can go back to our actions

217
00:11:40,000 --> 00:11:42,000
and hereafter a saveMeal.

218
00:11:42,000 --> 00:11:44,000
We can call redirect

219
00:11:45,000 --> 00:11:49,000
and import that from next navigation.

220
00:11:50,000 --> 00:11:54,000
This redirect function will do what its name implies.

221
00:11:54,000 --> 00:11:57,000
It will redirect the user to a different page.

222
00:11:58,000 --> 00:12:01,000
For that, you have to pass a path to redirect,

223
00:12:01,000 --> 00:12:03,000
and that of course should be the path

224
00:12:03,000 --> 00:12:06,000
of the page you wanna redirect the user to.

225
00:12:06,000 --> 00:12:08,000
So for example, to /meals.

226
00:12:10,000 --> 00:12:15,000
With that added, if we reload our page here maybe

227
00:12:15,000 --> 00:12:20,000
and browse all those meals that we got there,

228
00:12:20,000 --> 00:12:22,000
and I then add a new meal.

229
00:12:26,000 --> 00:12:30,000
And here I'll just quickly enter some dummy data

230
00:12:30,000 --> 00:12:32,000
to save some time.

231
00:12:32,000 --> 00:12:33,000
I'll use the same image though

232
00:12:33,000 --> 00:12:35,000
because we'll use the title as a slug

233
00:12:35,000 --> 00:12:38,000
for the image name on the Server anyways,

234
00:12:38,000 --> 00:12:40,000
so we won't override the old image.

235
00:12:41,000 --> 00:12:44,000
But if I do all that and I then click Share Meal,

236
00:12:44,000 --> 00:12:46,000
initially, still nothing happens.

237
00:12:46,000 --> 00:12:47,000
We'll take care of that soon.

238
00:12:47,000 --> 00:12:50,000
But after a short while, we are redirected

239
00:12:50,000 --> 00:12:54,000
and I see my new meal here, so that all works.

