This quick start guide is designed to get the Node.js developer up and running with TimescaleDB as their database.
To complete this tutorial, you will need a cursory knowledge of the Structured Query Language (SQL). The tutorial will walk you through each SQL command, but it will be helpful if you've seen SQL before.
To start, install TimescaleDB. Once your installation is complete, we can proceed to ingesting or creating sample data and finishing the tutorial.
Obviously, you will need to install Node and the Node Package Manager (npm) as well.
TimescaleDB is based on PostgreSQL and we can use common PostgreSQL tools to connect your Node app to the database. In this example, we will use a Database Client for TypeScript and Node.js called Prisma Client.
Let's initialize a new Node app. From your command line, type the following:
npm init -yThis will create a package.json file in your directory, which contains all
of the depenencies for your project:
{
"name": "node-sample",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}Now, let's install Express.js by running the following command:
npm install expressFinally, let's create a simple web page to display a greeting. Open your
code editor, and add the following to a file called index.js:
const express = require('express')
const app = express()
const port = 3000;
app.use(express.json());
app.get('/', (req, res) => { res.send('Hello World!') })
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))You can test your simple application by running the following from your command line and using your browser to view http://localhost:3000:
node index.jsYou should get a "Hello World!" greeting.
Now, let's add Prisma Client to our project by first installing it (and its command line interface) from the command line:
npm install -D prisma
npm install @prisma/clientWith the CLI installed, you can initiate Prisma in your project which auto generated some project configuration:
npx prisma initYou should get output similar to the following:
✔ Your Prisma schema was created at prisma/schema.prisma.
You can now open it in your favorite editor.
Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql or sqlite.
3. Run prisma db pull to turn your database schema into a Prisma data model.
4. Run prisma generate to install Prisma Client. You can then start querying your database.
More information in our documentation:
https://pris.ly/d/getting-started
Locate your TimescaleDB credentials in order to connect to your TimescaleDB instance.
You’ll need the following credentials:
- password
- username
- host URL
- port
- database name
Combine them into a database connection URI (or service connection URI): postgres://username:password@host_url:port/database_name?sslmode=require and modify the generated .env file so DATABASE_URL is set to that value.
Prisma Client currently does not let you generate a database client without a model defined, so add this to the generated prisma/schema.prisma file:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
Then you can run npx prisma generate in your command line. The output should look like this:
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
✔ Generated Prisma Client (2.20.1) to .\node_modules\@prisma\client in 115ms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client
```
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
```
As you are using plain Javascript in your project, add the following to the top of the index.js file:
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()We can test this connection by adding the following to index.js before the res.send() statement:
prisma.$connect().then(() => {
console.log('Connection has been established successfully.');
}).catch(err => {
console.error('Unable to connect to the database:', err);
});Once again, start the application on the command line:
node index.jsAnd you should get the following results:
Example app listening at http://localhost:3000
Connection has been established successfully.Until now, we worked with the default database name. As we want the database we work with to actually be called node_test, replace the default database name in your connection string in your .env file with node_test. This will be used later
TimescaleDB is delivered as a PostgreSQL extension. Some instances and versions of TimescaleDB already have the extension installed. Let's make sure the extesion is installed if it's not.
To start, create a database migration by running the following command:
npx prisma migrate dev --create-onlyYou will see a folder that has the name tsdb appended to it in
your prisma/migrations folder, that contains a migrations.sql file. Let's modify that file to look like this:
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;Now run the migration command from the command-line:
npx prisma migrate devWhen prompted, input tsdb. You should get the following result:
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": PostgreSQL database "node_test", schema "public" at "tsdb-2aa5e70e-prisma-0904.a.timescaledb.io:28514"
The following migration(s) have been applied:
migrations/
└─ 20210404203041_tsdb/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (2.20.1) to .\node_modules\@prisma\client in 132msYou can test and see if the TimescaleDB extension is installed by connecting
to your database using psql and running the \dx
command. You should get a result like this:
List of installed extensions
Name | Version | Schema | Description
-------------+---------+------------+-------------------------------------------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
timescaledb | 1.7.1 | public | Enables scalable inserts and complex queries for time-series data
(2 rows)Now let's create a table and model called page_loads for our database. Replace the placeholder User from your prisma/schema.prisma file with this model:
model page_loads {
userAgent String
time DateTime
@@unique([userAgent, time])
}Let's migrate our change and ensure that it is reflected in the database itself:
npx prisma migrate devWhen prompted input page_loads.
You should get a result that looks like this:
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": PostgreSQL database "node_test", schema "public" at "tsdb-2aa5e70e-prisma-0904.a.timescaledb.io:28514"
√ Name of migration ... page_load
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20210404204150_page_loads/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (2.20.1) to .\node_modules\@prisma\client in 118msIn TimescaleDB, the primary point of interaction with your data is a hypertable, the abstraction of a single continuous table across all space and time intervals, such that one can query it via standard SQL.
Virtually all user interactions with TimescaleDB are with hypertables. Creating tables and indexes, altering tables, inserting data, selecting data, etc. can (and should) all be executed on the hypertable.
A hypertable is defined by a standard schema with column names and types, with at least one column specifying a time value.
:TIP: The TimescaleDB documentation on schema management and indexing explains this in further detail.
Let's create this migration to modify the page_loads table and create a
hypertable.
As Prisma Migrate does not allow the creation of empty migrations yet, create a new folder in prisma/migrations similar to the existing ones but ending with hypertable and add an migration.sql file with the following content:
SELECT create_hypertable('page_loads', 'time');");Now run the migration command from the command-line:
npx prisma migrate devYou should get the following result:
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": PostgreSQL database "node_test", schema "public" at "tsdb-2aa5e70e-prisma-0904.a.timescaledb.io:28514"
The following migration(s) have been applied:
migrations/
└─ 20210404204600_hypertable/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (2.20.1) to .\node_modules\@prisma\client in 118msNow you have a working connection to your database, a table configured with the proper schema, and a hypertable created to more efficiently query data by time. Let's add data to the table.
In the index.js file, we will modify the / route like so to first get the
user-agent from the request object (req) and the current timestamp. Then,
we will save call the create method on our model (page_loads), supplying
the user agent and timestamp parameters. The create call will execute
an INSERT on the database:
app.get('/', async (req, res) => {
// get the user agent and current time
const userAgent = req.get('user-agent');
const time = new Date();
try {
// insert the record
await prisma.page_loads.create({
data: {
userAgent: userAgent,
time: time
}
})
// send response
res.send('Inserted!');
} catch (e) {
console.log('Error inserting data', e)
}
})Each time the page is reloaded, we also want to display all information currently in the table.
To do this, we will once again modify the / route in our index.js file
to call the Prisma findMany function and retrieve all data from the
page_loads table, like so:
app.get('/', async (req, res) => {
// get the user agent and current time
const userAgent = req.get('user-agent');
const time = new Date().getTime();
try {
// insert the record
await PageLoads.create({
userAgent, time
});
// now display everything in the table
const messages = await prisma.page_loads.findMany()
res.send(messages);
} catch (e) {
console.log('Error inserting data', e)
}
})Now, when you reload the page, you should see all of the rows currently in the
page_loads table.
- Use Timescale specific functionality via raw query