TwitterGithub

Installation

Get up and running with the spartan/stack.

Setting up your Nx workspace

The spartan/stack starts with an Nx workspace. Even better, AnalogJs comes with first-class Nx support and a preset, which will set up our favorite meta-framework for you out of the box!

Enter the following command:

npx create-nx-workspace@latest --preset=@analogjs/platform

You will be asked to choose a folder name for your workspace:

Where would you like to create your workspace?

Pick whichever fits your project the best!

Then, you will be prompted to answer the following questions:

What name would you like to use for your AnalogJs app?
Add TailwindCSS for styling?
Add tRPC for typesafe client/server interaction?

Give your application a meaningful name (we will refer to this as [YOUR_APP_NAME] in this guide) and press y/Y to answer both questions about adding TailwindCSS and tRPC to your application.

AnalogJs, Angular, TailwindCSS, and tRPC

With this simple command, you will have a working Nx workspace that includes an example AnalogJs application, with TailwindCSS and tRPC already set up!

Watch the video below to become more familiar with the AnalogJs setup.

Drizzle

Currently, we use an in-memory array to store the notes of our example application. Let's persist our data in an actual database. To interact with our DB, we will use the Drizzle ORM. Let's first install the necessary dependencies:

npm install drizzle-orm postgres

Finally, we need to set up our DB connection and create a typescript schema that matches our database structure. We will add a [YOUR_APP_NAME]/src/db.ts file with the following content:

import { drizzle } from 'drizzle-orm/postgres-js';
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
import { InferInsertModel, InferSelectModel } from 'drizzle-orm';
import postgres from 'postgres';

export const notes = pgTable('note', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content'),
  createdAt: timestamp('created_at').defaultNow().notNull(),
});

export type Note = InferSelectModel<typeof notes>;
export type NewNote = InferInsertModel<typeof notes>;

const client = postgres(process.env['DATABASE_URL'] ?? '');
export const db = drizzle(client);

We first declare our notes table and make Drizzle aware of all its columns and their respective types. We then declare some helper types we will use when retrieving and creating our Notes. Finally, we initialize our Postgres client and pass it to Drizzle

This is where the spartan/stack starts to flex its muscles. We can now use this schema in our component. Go to [YOUR_APP_NAME]/src/app/pages/analog-welcome.component.ts and replace the following line:

import { Note } from '../../note';

with:

import { Note } from '../../db';

Excellent! We are only a few steps away from end-to-end type-safety for our Angular application. We take this opportunity and delete the boilerplate file:[YOUR_APP_NAME]/src/note.ts.

Our types now come directly from our database!

We continue and set up our backend to use Drizzle to read, create, and delete our notes. Adjust the [YOUR_APP_NAME]/src/server/trpc/routers/notes.ts file to get the below output:

import { z } from 'zod';
import { publicProcedure, router } from '../trpc';
import { db, notes } from '../../../db';
import { eq } from 'drizzle-orm';

export const noteRouter = router({
  create: publicProcedure
    .input(
      z.object({
        note: z.string(),
      })
    )
    .mutation(
      async ({ input }) => await db.insert(notes).values({ note: input.note }).returning()
    ),
  list: publicProcedure.query(async () => {
    const selectedNotes = await db.select().from(notes);
    return selectedNotes.map((note) => ({ ...note, id: +note.id }));
  }),
  remove: publicProcedure
    .input(
      z.object({
        id: z.number(),
      })
    )
    .mutation(async ({ input }) => await db.delete(notes).where(eq(notes.id, input.id)).returning()),
});

Awesome! This is all we need to persist our data. Now that we are using type-safe database interactions and also leverage Drizzle's generated schemas in your components only one thing is missing: A database!

Supabase

We will use Supabase as our database infrastructure provider. There are two ways to get up and running with Supabase:

  1. Connecting directly to your managed instance on supabase.com
  2. Locally using Docker

Option 1: Connecting to supabase.com instance

This way is super easy! Simply by creating your account, you will also have set up your first project. This means that you are ready to connect to your projects database already!

Let's connect our application to our Supabase Postgres instance: Add a .env file at the root of your Nx workspace and add the following code snippet:

DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-SUPABASE-REFERENCE-ID].supabase.co:5432/postgres"

Setting up the Schema

While Drizzle is adding support for automatic database migrations, I like to keep them explicit and run the commands directly against the DB. Until spartan/stack comes with an automated solution like liquibase, let's manually run the following command in the SQL-Editor to create our notes table:

create table note (
    id bigint not null default nextval('note_id_seq'::regclass),
    note text not null,
    created_at timestamp with time zone null default current_timestamp,
    constraint notes_pkey primary key (id)
);

Local Development

npx nx serve [YOUR_APP_NAME]

You can now serve the local development server by running the above command.

Build for Production

npx nx build [YOUR_APP_NAME]

Finally, let's build a production bundle of our application. Run the command above. By default, AnalogJs will build a NodeJs-compatible output, which you can run with the following command:

node dist/[YOUR_APP_NAME]/analog/index.mjs

AnalogJs also supports multiple build presets, which makes it easy to deploy your application to most of the major cloud providers. This includes Vercel, Cloudflare, Azure, AWS, and more.

Next Steps

Now that you know how to develop and build your application for production, you are ready to take it to the next level. Why don't you make it beautiful and accessible for everyone with spartan/ui?

spartan/ui Technologies