Deno Postgres: Error Handling
Deprecated content
The course materials used to rely on the deno-postgres driver. These materials are here for reference, but will be removed in subsequent versions.
With Deno Postgres, wrapping indivdual database queries into a try-catch
statement will become relatively verbose.
const deleteById = async (id) => {
try {
await client.connect();
await client.queryObject(
"DELETE FROM addresses WHERE id = $id;",
{ id: id },
);
} catch (e) {
console.log(e);
} finally {
try {
await client.end();
} catch (e) {
console.log(e);
}
}
};
One possibility would be to address this by moving the basic database query functionality to a separate file. Let's create a folder called database
and add a file database.js
to the folder. After creating the file and folder, the folder structure is as follows.
tree --dirsfirst
.
├── database
│ └── database.js
├── services
│ └── addressService.js
├── views
│ └── index.eta
└── app.js
3 directories, 4 files
Next, let's add functionality to the database.js
file. First, we move the database configuration into that file and create a executeQuery
function that can be used for executing database queries into the file.
import { Client } from "https://deno.land/x/postgres@v0.17.0/mod.ts";
const client = new Client();
const executeQuery = async (query, params) => {
};
export { executeQuery };
Let's now implement the executeQuery
function. The function opens up a database connection, executes the query, and closes the connection. The function checks whether the query returned a result and whether the result had rows in it -- if there are rows, they are added to the response. Similarly, the function also wraps the database functionality in a try-catch
statement to handle errors; errors, if such exist, are also returned from the function.
This behavior is added to the function below.
const executeQuery = async (query, params) => {
const response = {};
try {
await client.connect();
const result = await client.queryObject(query, params);
if (result && result.rows) {
response.rows = result.rows;
}
} catch (e) {
response.error = e;
} finally {
try {
await client.end();
} catch (e) {
console.log(e);
}
}
return response;
};
Now that we have the basic database functionality in place, we adjust the addressService.js
to use the executeQuery
function from the database.js
file. After modifications, the addressService.js
looks as follows.
import { executeQuery } from "../database/database.js";
const create = async (name, address) => {
await executeQuery(
"INSERT INTO addresses (name, address) VALUES ($name, $address);",
{ name: name, address: address },
);
};
const deleteById = async (id) => {
await executeQuery("DELETE FROM addresses WHERE id = $id;", { id: id });
};
const findAll = async () => {
let result = await executeQuery("SELECT * FROM addresses;");
return result.rows;
};
const findByNameOrAddressLike = async (nameOrAddress) => {
const likePart = `%${nameOrAddress}%`;
let result = await executeQuery(
"SELECT * FROM addresses WHERE name ILIKE $namePart OR address ILIKE $addressPart;",
{ namePart: likePart, addressPart: likePart },
);
return result.rows;
};
export { create, deleteById, findAll, findByNameOrAddressLike };
Note that in the above example, we do not do anything with the errors, even though they would be available. In practice, one could, for example, respond with the error if one would exist, but we will omit such behavior from the applications for now.