You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: server-database/README.md
+312Lines changed: 312 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,3 +10,315 @@ Learning objectives:
10
10
- Understand how a server and a database work together
11
11
- Use SQL to read data from a database
12
12
- Accept data over a POST request and write it to the database
13
+
14
+
## Steps
15
+
16
+
`go mod init server-database`
17
+
18
+
[intro to Go and JSON](https://go.dev/blog/json)
19
+
20
+
Create a struct that represents the data:
21
+
22
+
```go
23
+
typeImagestruct {
24
+
Titlestring
25
+
AltTextstring
26
+
Urlstring
27
+
}
28
+
```
29
+
30
+
Initialise some data:
31
+
32
+
```go
33
+
images:= []Image{
34
+
{"Sunset", "Clouds at sunset", "https://images.unsplash.com/photo-1506815444479-bfdb1e96c566?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80"},
35
+
{"Mountain", "A mountain at sunset", "https://images.unsplash.com/photo-1540979388789-6cee28a1cdc9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80"},
36
+
}
37
+
```
38
+
39
+
Import `"encoding/json"` and `Marshal`:
40
+
41
+
```go
42
+
b, err:= json.Marshal(images)
43
+
```
44
+
45
+
Write this back as the response:
46
+
47
+
```
48
+
> curl 'http://localhost:8080/images.json' -i
49
+
HTTP/1.1 200 OK
50
+
Content-Type: text/json
51
+
Date: Wed, 03 Aug 2022 18:06:34 GMT
52
+
Content-Length: 487
53
+
54
+
[{"Title":"Sunset","AltText":"Clouds at sunset","Url":"https://images.unsplash.com/photo-1506815444479-bfdb1e96c566?ixlib=rb-1.2.1\u0026ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8\u0026auto=format\u0026fit=crop\u0026w=1000\u0026q=80"},{"Title":"Mountain","AltText":"A mountain at sunset","Url":"https://images.unsplash.com/photo-1540979388789-6cee28a1cdc9?ixlib=rb-1.2.1\u0026ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8\u0026auto=format\u0026fit=crop\u0026w=1000\u0026q=80"}]
55
+
```
56
+
57
+
Add a query param `indent` which uses `MarshalIndent` instead (https://pkg.go.dev/encoding/json#MarshalIndent) such that the following snippet works. The indent value should increase the amount of indentation: `?indent=4` should have 4 spaces, but `?indent=2` should have 2.
58
+
59
+
To do this, you'll need to investigate the `strconv` and `strings` packages in the Go standard library.
We've now got a working server that responds to requests with data in JSON format, and can format it.
83
+
84
+
---
85
+
86
+
Next we're going to set up a database to store data that our server will use.
87
+
88
+
We'll use [Postgres](https://www.postgresql.org/), which is an open source relational database. Don't worry if that doesn't mean anything right now. Read the Postgres website to find out the core ideas.
89
+
90
+
First, install Postgres. You may have been provided with access to Amazon Web Services, which provides Postgres for you via their Relational Database Service. If not, you can also run it on your computer: follow the [instructions on the Postgres website](https://www.postgresql.org/download/).
91
+
92
+
Your goal is to have a database running that you can connect to using a connection string, which will look something like this: `postgres://user:secret@localhost:5432/mydatabasename`
93
+
94
+
For easy demoing, we'll assume you have Postgres running locally and connect with `postgresql://localhost`.
95
+
96
+
Next install [pgAdmin](https://www.pgadmin.org/) (if you haven't already). This is a useful user interface that we'll use to set up the Postgres database.
97
+
98
+
Open up pgAdmin and add a server that connects to your instance of Postgres.
99
+
100
+
Then add a database by right-clicking on Databases.
101
+
102
+

103
+
104
+
The SQL generated (see the SQL tab) should read as follows:
105
+
106
+
```sql
107
+
CREATE DATABASE "go-server-database"
108
+
WITH
109
+
OWNER = postgres
110
+
ENCODING ='UTF8'
111
+
LC_COLLATE ='en_US.UTF-8'
112
+
LC_CTYPE ='en_US.UTF-8'
113
+
CONNECTION LIMIT=-1
114
+
IS_TEMPLATE = False;
115
+
```
116
+
117
+
TODO: note here introducing SQL.
118
+
119
+
Data in Postgres is arranged in tables with columns, like a spreadsheet.
120
+
121
+
Next create a table that will store our image data. Within the `go-server-database` database, open `schemas`, `public`, and then create a new table.
122
+
123
+

124
+
125
+
Add four columns:
126
+
127
+
-`id`: type `serial` with "Primary key?" and "Not null?" turned on
128
+
-`title`: type `text` with "Not null?" turned on
129
+
-`url`: type `text` with "Not null?" turned on
130
+
-`alt_text`: type `text`
131
+
132
+
The SQL tab should look like the following:
133
+
134
+
```sql
135
+
CREATETABLEpublic.images
136
+
(
137
+
id serialNOT NULL,
138
+
title textNOT NULL,
139
+
url textNOT NULL,
140
+
alt_text text,
141
+
PRIMARY KEY (id)
142
+
);
143
+
144
+
ALTERTABLE IF EXISTS public.images
145
+
OWNER to postgres;
146
+
```
147
+
148
+
Once the table is created, we can insert some data.
149
+
150
+
Right click on the table and open `Scripts > INSERT script`.
151
+
152
+

153
+
154
+
This will open the Query tool, which will allow us to add some data, and give some basic SQL to start with.
155
+
156
+
```sql
157
+
INSERT INTOpublic.images(
158
+
id, title, url, alt_text)
159
+
VALUES (?, ?, ?, ?);
160
+
```
161
+
162
+
If you run this ("play" button at the top) you will get an error, because we haven't provided any data.
163
+
164
+
Update the SQL to look like this. We don't need to specify an ID: Postgres will do this.
165
+
166
+
```sql
167
+
INSERT INTOpublic.images(title, url, alt_text)
168
+
VALUES ('Sunset', 'https://images.unsplash.com/photo-1506815444479-bfdb1e96c566?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80', 'Clouds at sunset');
169
+
```
170
+
171
+
This should insert some data:
172
+
173
+
```
174
+
INSERT 0 1
175
+
176
+
Query returned successfully in 132 msec.
177
+
```
178
+
179
+
We can check by deleting this query and running a different one:
180
+
181
+
```sql
182
+
SELECT*from images;
183
+
```
184
+
185
+
You should see the row you added, and a value in the ID column.
186
+
187
+
Now write a new `INSERT` query with the other image file from our code.
188
+
189
+
When this is done, the `SELECT` query should return two rows with different IDs and data. Nice.
190
+
191
+
---
192
+
193
+
We can now connect our server to the database.
194
+
195
+
The server and the database are two separate systems that communicate over a network, like our client (`curl`) and server did before. This time, the server will be acting as the "client" by making requests to the database, and the database will responding with data it has stored.
196
+
197
+
We will be sending requests from our server to the database using SQL: from now on we'll call them queries. At the most basic level, queries can read, write, update and delete data.
198
+
199
+
Let's get going.
200
+
201
+
Install pgx: `go get github.com/jackc/pgx/v4`
202
+
203
+
`pgx` is the library we will use to connect to and query the database.
204
+
205
+
The first step is to connect. Our server needs to know which database to connect to, and we'll do that by running the server like this:
206
+
207
+
```
208
+
> DATABASE_URL='postgres://localhost:5432/go-server-database' go run .
209
+
```
210
+
211
+
`DATABASE_URL` is an environment variable, and our server can access the value of this variable like this:
212
+
213
+
```go
214
+
os.Getenv("DATABASE_URL")
215
+
```
216
+
217
+
Before going any further, write some code that checks if `DATABASE_URL` is set. If it's not: write a helpful error to `os.Stderr` and exit the process with a non-zero code, to indicate failure (`os.Exit(1)`).
218
+
219
+
Next, create a connection using `pgx.Connect`. You will need to supply a `context`, which can simply be `context.Background()`. You can [find out about contexts in the Go documentation](https://pkg.go.dev/context), but they are not an important concept for now.
220
+
221
+
Remember to handle errors in connecting to the database gracefully: handle the error and output a useful error message. You can check this is working by turning off Postgres and starting your server:
222
+
223
+
```
224
+
> DATABASE_URL='postgres://localhost:5432/go-server-database' go run .
225
+
unable to connect to database: failed to connect to `host=localhost user=tom database=go-server-database`: dial error (dial tcp 127.0.0.1:5432: connect: connection refused)
226
+
exit status 1
227
+
```
228
+
229
+
Once connected to the database, you can look in pgAdmin at the Dashboard for your server: it will show an active session.
230
+
231
+
It's important to gracefully close our connection to the database, so we need to add `defer conn.Close(context.Background())` immediately after we successfully connect. The `defer` keyword means that this line of code is delayed until the nearby function returns. [Defer is really useful!](https://go.dev/blog/defer-panic-and-recover)
232
+
233
+
Now we're connected we can fetch some data.
234
+
235
+
To get data we'll use `conn.Query`, which is a [method](https://gobyexample.com/methods) defined for the connection (`pgx.Conn`) to the Postgres database.
236
+
237
+
The SQL will be the same as above, to fetch all the images:
238
+
239
+
```sql
240
+
"SELECT title, url, alt_text FROM public.images"
241
+
```
242
+
243
+
Remember to handle the error from `conn.Query` gracefully.
244
+
245
+
`conn.Query` returns some `Rows` which we can use to `Scan` for the actual data:
246
+
247
+
We need a container [slice](https://gobyexample.com/slices) for the image: `var images []Image`
248
+
249
+
We can iterate over the rows: `for rows.Next() { ... }`
250
+
251
+
Using `Scan` to extract the data from each row means [passing a pointer to the variables](https://gobyexample.com/pointers) you want to fill, so they can be modified:
252
+
253
+
```go
254
+
vartitle, url, altTextstring
255
+
err = rows.Scan(&title, &url, &altText)
256
+
```
257
+
258
+
After handling the possible error, you can add an image to the list:
0 commit comments