Back

Creating a Node API with Knex and PostgreSQL

Creating a Node API with Knex and PostgreSQL

One of the challenges of working with databases like PostgreSQL is managing the complexity of SQL queries required to interact with the database. With Knex.js, you can easily create complex queries to select, insert, update, and delete data from a database and create and modify database tables and indexes. In this article, you will explore how to use Knex.js to build applications that leverage PostgreSQL without writing raw SQL queries.

Knex.js is a JavaScript query builder for relational databases that can be used with Node.js. It provides a straightforward interface for building and executing SQL queries in JavaScript, supporting multiple database systems, such as PostgreSQL. PostgreSQL is a popular relational database system widely used in modern web applications and other software systems.

Setting up your Development Environment

To use PostgreSQL, you must first install it on your system. Navigate to the PostgreSQL website and select your operating system to download PostgreSQL. Alternatively, you can use PostgreSQL on the cloud by leveraging platforms such as ElephantSQL, which offers PostgreSQL as a service.

After setting up your PostgreSQL database, create a new project directory by running the command below:

mkdir blog-knex-tutorial

Next, cd into your newly created project directory and run the command below to initialize npm in your project directory:

npm init -y

The -y flag initializes npm with all its defaults.

To use Knex with PostgreSQL, you need to install some dependencies. Install the required dependencies by running the command below:

npm install -g knex
npm install pg express dotenv

Next, create a .env file in your project’s root directory and store your database credentials.

For example:

//.env
//URI
DATABASE_URI =

//Credentials
DATABASE_NAME =
DATABASE_PASSWORD = 

Setting up an Express Server

This tutorial will feature a simple blog API to demonstrate combining Knex and PostgreSQL with Node.js to build a Node.js application.

First, create an index.js file in your project’s root directory and add the code block to it:

//index.js
const express = require("express");
const app = express();
const port = 3000;

app.use(express.json());

app.listen(port, () => {
  console.log(`App listening on port:${port}`);
});

The code block above creates a simple express server. It listens on port 3000 and uses the express.json() middleware to parse incoming JSON requests.

Configuring Knex with PostgreSQL in your Application

Before using Knex in your application, you have to initialize and configure it with the database driver of your choice, in this case, PostgreSQL.

Run the command below to initialize Knex in your application.

knex init

The command above creates a knexfile.js configuration file containing settings for connecting to a database, such as a database type, host, port, username, password, and other configuration options.

By default, the knexfile.js file this command generates will have sqlite3 as its development database. So to use PostgreSQL, replace your current knexfile.js with the code block below:

// Update with your config settings.
require("dotenv").config();

/**
 * @type { Object.<string, import("knex").Knex.Config> }
 */
module.exports = {
  development: {
    client: "pg",
    connection: process.env.DATABASE_URI,
    migrations: {
      directory: "./db/migrations",
    }
  }
};

The code block above configures Knex to use PostgreSQL as its database client. It also specifies the database connection with environmental variables and the file path where your migration files will be stored.

Next, create a db folder in your project directory by running the command below:

mkdir db

Then, create a db.js file in your db folder and import knex and your knexFile.js file.

Like so:

//db/db.js
const knex = require("knex");
const knexFile = require("../knexfile.js");

Next, add the code block below to your db.js file:

//db/db.js
const environment = process.env.NODE_ENV || "development";

module.exports = knex(knexFile[environment]);

The code block above sets the environment variable to either the NODE_ENV environment variable or development if NODE_ENV is not set. This lets you specify different configurations for different environments, such as development, production, or testing.

Finally, the module.exports statement exports a configured Knex.js instance using the configuration settings from knexFile[environment]. This instance can create database tables, insert data, run queries, and perform other database-related operations in JavaScript code.

Creating Migration Files

Migration files are files you can use to manage changes to the database schema. When you create a new table, modify an existing table, or add new columns, you must modify the database schema to reflect these changes. However, modifying a database schema can be complex and error-prone, especially when working with large or complex databases.

By using migration files, you can define the changes you want to make in a migration file instead of manually modifying the database schema. When you run the migration file using Knex, it automatically applies the changes to the database schema, ensuring that the changes are made consistently and correctly.

To create a migration file, run the command below:

knex migrate:make blog

The command above creates a “blog” (you can replace the blog argument with your preferred migration name) migration file in the file path specified in your knexFile.js file (db/migrations).

Next, open your migration file and replace the up function with the code block below:

exports.up = function (knex) {
  //Create a table called "blog" with the following columns: id, title, content, author, created_at, updated_at
  return knex.schema.createTable("blog", (table) => {
    table.increments("id").primary(); //id column with auto-incrementing primary key
    table.string("title").notNullable(); //title column with type string
    table.text("content").notNullable(); //content column with type text
    table.string("author").notNullable(); //author column with type string
    table.timestamps(true, true); //created_at and updated_at columns with type timestamp
  });
};

The code block above, when executed, creates a blog table in your PostgreSQL database with the tables specified above.

Next, replace the down function with the code block below:

exports.down = function (knex) {
  // Drop the "blog" table if it exists
  return knex.schema.dropTableIfExists("blog");
};

The code block above, when executed, drops the blog table in your PostgreSQL database, which is the opposite of what the up function does.

To run the migrations, run the command below on your terminal:

knex migrate:latest

The command above goes through all your migration files and runs the up function.

To undo the migrations, run the command below:

knex migrate:rollback

The command above goes through all your migration files and runs the down function.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

Creating CRUD Endpoints

To better organize your code, create a routes folder in your project’s root directory and create a blog.js file.

Next, in your blog.js file, import express, your Knex configuration, and set up the Express Router. Like so:

const express = require("express");
const db = require("../db/db.js");

const router = express.Router();

In your blog.js file, you can add CRUD endpoints to interact with your database. The tutorial will feature queries that get all the blogs, get a blog(s) based on a condition, update a blog, and delete a blog from the database.

Getting all Blogs

To implement the logic for getting all blog instances from your database, add the code block below to your blog.js file:

router.get("/blog", async (req, res) => {
  try {
    const blogs = await db.select("*").from("blog");
    res.send(blogs);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

The code block above returns all the blog instances in your database.

Getting Blogs Conditionally

To implement the logic for getting a blog instance conditionally from your database, add the code block below to your blog.js file:

router.get("/blog/:id", async (req, res) => {
  const { id } = req.params;
  try {
    const blogs = await db("blog").where({ id });
    if (blogs.length !== 0) {
      res.send(blogs);
    } else {
      res.status(404).json({ error: "Blog not found" });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

The code block above returns a blog instance based on a given id.

Adding a new Blog to your Database

To implement the logic for adding a new blog instance to your database, add the code block below to your blog.js file:

router.post("/blog/new", async (req, res) => {
  const { title, content, author } = req.body;
  try {
    const blog = await db("blog").insert({ title, content, author });
    res.status(201).send(blog);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

The code block above adds a new blog to your database.

Updating an Existing Blog

To implement the logic that updates a blog in your database, add the code block below to your blog.js file:

router.put("/blog/:id", async (req, res) => {
  const { id } = req.params;
  const { title, content, author } = req.body;

  try {
    const blog = await db("blog")
      .where({ id })
      .update({ title, content, author }, ["id", "title", "content", "author"]);
    if (blog.length !== 0) {
      res.status(201).send(blog);
    } else {
      res.status(404).json({ error: "Blog not found" });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

The code block above updates an existing blog based on a given id.

Deleting a Blog

To implement the logic that deletes a blog from your database, add the code block below to your blog.js file:

router.delete("/blog/:id", async (req, res) => {
  const { id } = req.params;

  try {
    const blog = await db("blog").where({ id }).del();
    if (blog) {
      res.status(204).send();
    } else {
      res.status(404).json({ error: "Blog not found" });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

The code block above deletes a blog based on a given id.

Finally, add the code block below to your blog.js file to export your router.

module.exports = router;

Testing your Application

First, navigate to your index.js, import your router, and add it as a middleware. Like so:

//index.js
const blogRouter = require("./routes/blog.js");

app.use(blogRouter);

Next, start up your application by running the command below:

node index.js

To test your application, you can use tools such as Postman to make HTTP requests to your API and verify that it returns the expected results.

For example, you can make a POST request to http://localhost:3000/blog/new with the data below:

{
    "title": "Paragraph",
    "content": "His mother had always taught him not to ever think of himself as better than others. He'd tried to live by this motto. He never looked down on those who were less fortunate or who had less money than him. But the stupidity of the group of people he was talking to made him change his mind.",
    "author": "Van Gough"
}

If you make the request on Postman, your results should be similar to the image below.

--

Conclusion

In this article, you learned how to use Knex.js with PostgreSQL to build a Node.js API. You also learned how to configure Knex to use PostgreSQL as its database client, connect to a PostgreSQL database locally and remotely, and make queries using Knex.

With the knowledge gained in this article, you can now build robust and scalable Node.js applications that leverage the power of PostgreSQL and the simplicity of Knex.js.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay