Build a Stripe App with Node.js and Typescript - Part 2

Build a Stripe App with Node.js and Typescript - Part 2

This is Part 2 in the series of guides on building a Stripe App.

Introduction

Stripe Apps extend a Stripe Business Owner's capabilities by sharing screen real estate with Stripe Dashboard. Here's a look at a Stripe App we will be building.

In this guide, we will build a Stripe app called CRM Buddy that helps business owners leave notes on Customer profiles.

In Part 2, we will build the backend API service for the Stripe App using Node.js and Express. Here are the endpoints:

  • /addNote: Add a new note for the customer
  • /notes: Get all added notes
  • /notes/:customerId: Get all notes for a given customer

Step 1: Create a new Node.js project

Create a new Node.js project with Typescript and Express.js. We've created a detailed post on this here. Follow Step 1 from there and come back.

Step 2: Set up Prisma DB

We will use Postgres as our DB and Prisma will help us interact with it using Node.js.

First, make sure you have a Postgres server running locally. I use Postgres.app. Verify that it is running on localhost:5432 and create a new table crmbuddy . I use TablePlus as my client.

Install and initialize prisma with:

npm i prisma
npx prisma init

This will create a new folder called prisma and a .env

Add the local DB url to the .env:

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/crmbuddy

Now verify that the server does not throw a DB connection error when we start up the development server with:

npm run dev

Step 3: Create a Schema

Our schema is going to be very simple - just a Note with these properties:

  1. Id - Unique ID of the note (Auto-generated)
  2. AgentId - Stripe ID of the user writing the note
  3. CustomerId - Stripe ID of the customer the note is about
  4. Message - Actual message on the note

We can create a schema by editing our prisma/schema.prisma file like so:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Note {
  id Int @id @default(autoincrement())
  agentId String
  customerId String
  message String
}

We can now apply the schema by running:

npx prisma db push

Note: This will reset the DB and apply these new changes. In a PROD environment, you probably want to create a migration file. Prisma has support for this here.

Step 4 - Create the Note service

We will need service functions that interact with the DB to create and read data. We will use them in our API functions in the next step.

Let's start by adding a Prisma Client in a file called src/lib/prisma.ts :

import { PrismaClient } from ".prisma/client";

declare global {
  var prisma: PrismaClient | undefined;
}

export const prisma = global.prisma || new PrismaClient();

if (process.env.NODE_ENV !== 'production') { global.prisma = prisma }

This will let us not create multiple connections when the development server restarts on new changes.

Create a new file called src/services/index.tsx :

import { Prisma } from ".prisma/client";
import { prisma } from "../lib/prisma";

export async function createNote(data: Prisma.NoteUncheckedCreateInput) {
  const { agentId, customerId, message } = data;
  const user = await prisma.note.create({
    data: {
      agentId,
      customerId,
      message
    }
  })

  return user;
}

export async function getAllNotes() {
  const allUsers = await prisma.note.findMany();
  return allUsers;
}

export async function getNotesByCustomerId(customerId: string) {
  const allUsers = await prisma.note.findMany({ where: { customerId } });
  return allUsers;
}

Step 5 - Create the API endpoints

Update your src/app.ts

import { Prisma } from ".prisma/client";
import cors from "cors";
import express from "express";
import helmet from "helmet";
import { createNote, getAllNotes, getNotesByCustomerId } from "./services";

const app = express();

app.use(helmet());
app.use(cors());
cors({ credentials: true, origin: true })

app.use(express.json());

app.post("/note", async (req, res) => {
  const { agentId, customerId, message } = req.body;

  const newNote: Prisma.NoteUncheckedCreateInput = { agentId, customerId, message }

  await createNote(newNote);
  res.json({ error: false, data: {} })
})

app.get("/notes", async (req, res) => {
  const notes = await getAllNotes();
  res.json({ error: false, data: { notes } })
})

app.get("/notes/:customerId", async (req, res) => {
  const customerId = req.params.customerId;

  const notes = await getNotesByCustomerId(customerId);
  res.json({ error: false, data: { notes } })
})

export default app;

We can now start our API with:

npm run dev

Step 6: Connect Stripe App with Node.js Backend

Currently, our Stripe App is using mock data as the backend. We can easily replace that and connect it our running Node.js server by updating the src/api/index.ts in the Stripe App project:

import axios from "axios";
import { APIResponse } from "../types";

export async function addNoteAPI({ customerId, message, agentId }: { customerId: string, message: string, agentId: string }) {

  const response = await axios.post("http://localhost:3000/note", { agentId, customerId, message })

  return response.data;
}

export async function getAllNotesAPI(): Promise<APIResponse> {
  const response = await axios.get("http://localhost:3000/notes")
  return response.data;
}

export async function getNotesForCustomerAPI({ customerId }: { customerId: string }): Promise<APIResponse> {
  const response = await axios.get(`http://localhost:3000/notes/${customerId}`)

  return response.data;
}

That's it - everything else stays the same.

Restart your Stripe App by running:

stripe apps start

Try creating a note, you can see new entries being created in your DB like so:

0:00
/

Excellent - You just created a Full Stack Stripe App!