DEV Community

Cover image for Architectural choices that scale.
Rak
Rak

Posted on • Edited on

Architectural choices that scale.

TL;DR: By leveraging the auto-scaling capabilities of a correctly configured API Gateway in AWS, GCP, or Azure, along with a Neon Postgres db, we can easily create auto-scaling apps.

When technologies work harmoniously towards a single goal it makes sense to combine them. Let's create a simple application that leverages scale using Neon, API Gateways, and Nitric.

Setup your Neon Postgres DB

If you do not have one already, create a Neon project.

Navigate to the Projects page in the Neon Console -> Click New Project.

Then you can create your table using the SQL Editor. The table is fairly basic. We'll set it up with a primary key, a piece of text describing the task, and a completed status.

CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  description TEXT NOT NULL,
  completed BOOLEAN
);
Enter fullscreen mode Exit fullscreen mode

Head back to the dashboard and take note of the connection string. We'll want the pooled address here, it should look something like this.

postgres://username:password@*********.us-east-2.aws.neon.tech/neondb
Enter fullscreen mode Exit fullscreen mode

Building your task list API

We're going to use the Nitric framework to automatically wire up your API gateway and containerize your images using docker.

Pre-requisites

  • Node.js
  • The Nitric CLI
  • An AWS, GCP, or Azure account (your choice)

Scaffold your project

Let's start with a new nitric project and select the JavaScript starter template:

nitric new task-list
? Choose a template:  [Use arrows to move, type to filter]
> official/TypeScript - Starter
  official/JavaScript - Starter
Enter fullscreen mode Exit fullscreen mode

Install dependencies

cd task-list
yarn install
Enter fullscreen mode Exit fullscreen mode

Review your project

The scaffolded project should have the following structure:

TypeScript
Python
+--functions/
|  +-- hello.ts
+--node_modules/
|  ...
+--nitric.yaml
+--package.json
+--README.md
Enter fullscreen mode Exit fullscreen mode

Create your environments file

In the root directory, create an .env file and update it with the neon pooled database url.

DATABASE_URL=postgres://username:password@******-pooler.us-east-2.aws.neon.tech/neondb``
Enter fullscreen mode Exit fullscreen mode

Set up your database connection

Let's establish a connection to a PostgreSQL database using the pg package, which is a PostgreSQL client for Node.js. We're going to be using "pool" which allows us to manage multiple connections to the database.

Create a folder named resources with a file named 'db.js' containing the following snippet.

import pg from "pg"

const { Pool } = pg;

const { DATABASE_URL } = process.env;
export const conn = new Pool({
    connectionString: DATABASE_URL,
    ssl: {
      rejectUnauthorized: false,
    },
});
Enter fullscreen mode Exit fullscreen mode

Create the tasklist api

Remove the 'hello.js' file from the functions folder and replace it with 'task-api.js'

First define your API and import your database connection pool.

import { api } from "@nitric/sdk";
import { conn } from "../resources/db.js";

const tasks = api('tasks');
Enter fullscreen mode Exit fullscreen mode

Next, we'll need some API methods to access our task list:

Get all tasks

tasks.get('/tasks',  async (ctx) => {
  try {
    const tasks = await conn.query('SELECT * FROM tasks');
    ctx.res.json(tasks.rows);
  } catch (err) {
    ctx.res.status = 500;
    ctx.res.json({ response: 'An error occurred fetching your tasks'});
  } 
});
Enter fullscreen mode Exit fullscreen mode

Create one task

tasks.post('/tasks',  async (ctx) => {
  try {
    const { description } = ctx.req.json();
    await conn.query('INSERT INTO tasks (description, completed) VALUES ($1, $2)', [
      description,
      false,
    ]);
    ctx.res.status = 200
    ctx.res.json({ response: 'Task created successfully'});
  } catch (err) {
    ctx.res.status = 500;
    ctx.res.json({ response: `An error occured creating your task ${err}`});
  } 
});
Enter fullscreen mode Exit fullscreen mode

Update task completion

tasks.put('/tasks/:id',  async (ctx) => {
  try {
    const { id } = ctx.req.params;
    const { completed } = ctx.req.json();
    await conn.query('UPDATE tasks SET completed=$1 WHERE id=$2', [
      completed,
      id,
    ]);
    ctx.res.status = 200
  } catch (err) {
    ctx.res.status = 500;
    ctx.res.json({ response: 'An error occurred updating your task'});
  } 
});
Enter fullscreen mode Exit fullscreen mode

Delete item

tasks.delete('/tasks/:id', async (ctx) => {
  try {
    const { id } = ctx.req.params;
    await conn.query('DELETE FROM tasks WHERE id=$1', [id]);
    ctx.res.status = 200
  } catch (err) {
    ctx.res.status = 500;
    ctx.res.json({ response: 'An error occurred deleting your task'});
  }
});
Enter fullscreen mode Exit fullscreen mode

Testing locally

Now we've got the API established, let's test it out locally.

yarn run dev
Enter fullscreen mode Exit fullscreen mode

Try out the Nitric local dashboard, your URL should look something like this: http://localhost:49153/

Note: Take note of the port because it might vary.

Try fetching and creating new tasks here, the body for creating a new task will look like this

{
  "description": "run a mile"
}
Enter fullscreen mode Exit fullscreen mode

Deploy to the cloud

If you're ready, you can deploy this project to AWS, Azure or Google Cloud. For this example, we'll show the steps for AWS, but they're essentially the same in all cases.

Start by defining a stack. Stacks are essentially named deployment targets, which represent instances of your application running in the cloud.

To create a new stack, run nitric stack new and follow the prompts. For this example, we'll name the stack 'awsdev', select 'aws' as the target cloud, and 'us-east-1' as the target region.

nitric stack new
? What do you want to call your new stack? awsdev
? Which Cloud do you wish to deploy to? aws
? select the region us-east-1
Enter fullscreen mode Exit fullscreen mode

Finally, run the up command to deploy the stack and push your code to the cloud:

nitric up
Enter fullscreen mode Exit fullscreen mode

Recap

When correctly configured API Gateways and Neon automatically scale to handle the volume of traffic your API receives without throttling your backend operations. Consequently, by choosing technologies that scale by default, all you have to do is write code.

Nitric automates the process of creating an open API specifically, containerizing your handler functions into images, and configuring and deploying your API Gateway in whichever cloud you want AWS, GCP, or Azure - all ready to scale to your usage demands.

Top comments (0)