Build an API SaaS using Next.js, Prisma, Stripe in under an hour (Get paid for your API)

Roman @saasplanet
2 Aug 202359:45

Summary

TLDRThe video walks through building a paid API service using Next.js, Tailwind CSS, PlanetScale, Prisma, Stripe, and Vercel. It covers user authentication via NextAuth, restricting access to routes, creating customer profiles and subscriptions in Stripe, building the API endpoint, incrementing usage based on requests, logging requests to display in a dashboard, showing usage graphs and metrics, and deploying the full-stack web application.

Takeaways

  • 😀 Building a full-stack API SaaS using Next.js, Prisma, PlanetScale, Stripe, and Vercel
  • 📝 Using Next.js latest version and app router for easier routing
  • 💡 Leveraging server side rendering for session checks and redirects
  • 🔐 Adding authentication with NextAuth and Discord OAuth
  • 🗄 Setting up PlanetScale MySQL database and Prisma for simplified interactions
  • 💰 Integrating Stripe elements for subscriptions, billing and tracking usage
  • 🌐 Creating API endpoints to return data and handle authentication
  • 📊 Building admin dashboard to display usage graphs, logs and more
  • ✏️ Generating unique API keys for users to access protected endpoints
  • 💡 Challenges: add customer portal and improve graphs of usage logs

Q & A

  • What is the main goal of the video?

    -The main goal is to build an API software-as-a-service that charges users to access data APIs.

  • What technologies are used to build the API SaaS application?

    -The technologies used include Prisma, Tailwind, Stripe, Next.js, Vercel, and PlanetScale.

  • How is user authentication handled?

    -NextAuth is used along with Discord OAuth for user authentication.

  • How are users billed for API usage?

    -Stripe is integrated to bill users $1 for each API request. A metered billing model based on API usage is implemented.

  • How is the API usage tracked?

    -A database log table tracks each API request with details like method, status code, timestamp etc. The top recent logs are displayed in the dashboard.

  • How can users check if they have an active subscription?

    -The dashboard displays a message indicating if the user has an active subscription or needs to check out.

  • What databases are used?

    -PlanetScale MySQL is used along with Prisma as the ORM for simplified interactions.

  • How are new users handled?

    -When a new user logs in, a new Stripe customer is created if one does not exist. This associates the user with a billing account.

  • What additional challenges are proposed?

    -Some proposed challenges are to add Stripe portal for self-service subscription management and visual graphs for API usage logs.

  • Where can I get the full source code?

    -The full source code is available on the presenter's GitHub.

Outlines

00:00

🎧 Introducing the project idea and tech stack

The video will guide viewers through building an API software-as-a-service that charges users per API request. It will use Prisma, Tailwind, Stripe, Next.js, Vercel, and PlanetScale. The presenter explains the dashboard, billing, logs, and other pages that will be built.

05:00

💻 Setting up the Next.js app and database

The presenter initializes a Next.js app with TypeScript. They then create a PlanetScale database using Prisma for the ORM. Next Auth is configured for Discord authentication. Prisma is connected to PlanetScale and synced.

10:03

🔒 Adding authentication for dashboard page

The presenter adds authentication to lock down the /dashboard route. Users are redirected to sign in if not logged in. Session data is checked server-side before rendering dashboard.

15:05

🛒 Integrating Stripe for payments

Stripe is installed and setup using a test API key. Helper functions are created to retrieve the Stripe customer for a user or create one if it doesn't exist yet. A /checkout route is added.

20:06

💰 Creating checkout and subscription workflow

Functions are added to check if a user has an active subscription, and to generate a checkout link if not. The dashboard will now show a call to action to subscribe if needed.

25:06

🪙 Generating API keys and Stripe products

An API key is generated for each user and saved in the database. A Stripe product is created for metered billing per API request. The API key is displayed in the dashboard.

30:06

⚙️ Building the API endpoint

An API endpoint is created at /api/endpoint to return a UUID. It checks the validity of API key and subscription status before responding. Usage records are incremented.

35:07

📈 Logging and displaying API requests

API requests are logged to the database. The dashboard displays graphs and top requests. Current usage metrics are pulled from upcoming invoice to show billing.

40:09

🎉 Completing the web API SaaS

In closing, the presenter summarizes what was built - an API SaaS with billing, logging, authentication, and metrics. Additional challenges are proposed for enhancements.

Mindmap

Keywords

💡API

API stands for Application Programming Interface. It allows two software systems to communicate with each other by calling different functions. In the context of the video, the presenter shows how to create a API endpoint that can be used by others to access data, and charge them for usage.

💡SAS

SAS stands for Software as a Service. It refers to cloud-based software that is licensed on a subscription basis. The presenter builds a full-stack SAS with billing capabilities using Next.js, PlanetScale, Stripe and other technologies.

💡Next.js

Next.js is a popular React framework that enables server-side rendering and static site generation. It provides features like routing and API endpoints which the presenter leverages to build the SAS application.

💡PlanetScale

PlanetScale is a MySQL compatible serverless database platform. The presenter uses it to store user data and integrate it with Stripe for billing.

💡Stripe

Stripe provides payment processing and billing infrastructure. The presenter shows how to use Stripe APIs to create customers, subscriptions, invoices and more for monetizing the SAS app.

💡Prisma

Prisma is an ORM (Object Relational Mapper) that makes it easy to work with databases in programming languages. The presenter uses it to interact with the PlanetScale database from their Next.js app.

💡Authentication

Authentication refers to verifying user identity before allowing access. The presenter uses NextAuth.js to enable Discord OAuth based login in the SAS app.

💡Billing

Billing means charging users on a recurring basis. The presenter demonstrates integrating Stripe Checkout to create subscriptions that bill users based on API usage tracked in the database.

💡Dashboard

The dashboard is the admin interface that gives insights into application usage. The presenter shows building charts, usage stats, logs and more as part of the customer dashboard.

💡Deployment

Deployment means installing and running the application on remote servers accessible to users. The presenter mentions deploying the SAS app on Vercel, a popular platform for Next.js apps.

Highlights

We'll be using Prisma as our orm to easily interact with our database

We'll be using stripe as our payment processor

We'll be deploying this on Vercel and we're using PlanetScale as our MySQL provider

We'll build a dashboard, logs page, billing page, home page with API endpoint

We'll create our first API endpoint to start billing users

If no subscription, return code 403 forbidden

Create usage record to increment usage by 1 for billing

Return special key from API endpoint, can be anything - stock data etc

Log all requests to database for analytics

Show top 10 recent logs on dashboard

Show current month usage for upcoming invoice

Stripe stores currency without decimals, divide by 100 for count

See usage and logs update in real-time on dashboard

Challenges: Customer portal, graph events, move API key to headers

Get full code on GitHub, AI SaaS starter kit linked below

Transcripts

play00:01

[Music]

play00:05

guys today we'll be creating an API SAS

play00:08

so you can get paid for your data so you

play00:11

will be able to charge people for uh API

play00:15

access to your API uh this comes

play00:18

complete with billing and even log

play00:19

records so we will be setting all this

play00:21

up in under an hour

play00:24

um it's gonna be a really good time I

play00:26

hope you guys enjoy it and I'll see you

play00:28

when we start what's up everyone today

play00:30

we'll be building an API software as a

play00:32

service so you can get paid uh for

play00:35

having a bunch of data and selling it to

play00:37

people

play00:38

um so how this will work is when someone

play00:40

makes an API request to your API it'll

play00:43

charge them whatever amount you want so

play00:46

for testing we'll just put it as a

play00:48

dollar

play00:49

so the tech stack for this is going to

play00:50

be Prisma as our orm to easily interact

play00:54

with our database

play00:55

Tailwind for styling we'll be using

play00:59

stripe as our payment processor

play01:01

we'll be using the latest version of

play01:04

nexjs so you can have access to the app

play01:05

router and with that we'll be using next

play01:08

auth

play01:10

for our self-hosted authentication and

play01:13

we'll be deploying this on versel and

play01:15

we're using Planet scale as our uh MySQL

play01:19

provider

play01:21

so you'll basically be building this

play01:24

with me and I'll show you what to do as

play01:27

we go along some of the things we'll be

play01:28

building is on a dashboard a dashboard

play01:31

page which will show the usage with a

play01:33

chart

play01:34

a logs page which will show all of the

play01:37

um requests the user has made

play01:40

to the to your API a billing and

play01:42

settings page which will show their API

play01:44

key and a checkout button so they can

play01:47

paid to get access to it and we'll also

play01:50

build a home page with the API endpoint

play01:52

so by the end of this you should be have

play01:54

a fully production ready application and

play01:58

start making money from your data

play02:00

so the first thing you want to do is

play02:01

open up

play02:03

Visual Studio

play02:04

and we'll create our next app so we'll

play02:07

go into our console

play02:08

Air Terminal and create do create next

play02:11

app

play02:12

my bad MPX

play02:14

create next app at latest

play02:25

we'll give this any name you want so

play02:27

I'll name this cool API we'll use

play02:29

typescript with eslint K1 CSS will use

play02:33

the source directory app router import

play02:36

Alias will be at symbol forward slash

play02:40

and a star so

play02:42

it'll be easier to import everything

play02:48

while that's getting ready go to

play02:50

planetscale.com

play02:54

create an account this will this is a

play02:57

free platform you can use to self-host a

play03:00

database

play03:01

or not self-host but to get in my SQL

play03:03

database uh really easy to set up great

play03:05

developer experience so once you have

play03:07

your plan skill account you're going to

play03:09

click new database

play03:10

create new database

play03:14

we'll do the free hobby tier and for the

play03:17

database name we'll just name this test

play03:21

and click create database

play03:25

I'm going to create a new

play03:28

uh notepad so I can write down

play03:30

everything

play03:33

so it's still initializing and our next

play03:37

project just got done so we're going to

play03:39

enter this

play03:40

open

play03:42

cool API

play03:44

all right so this is our next uh JS

play03:46

project with the app router we will be

play03:49

using server actions so in your next

play03:51

config

play03:52

uh type in

play03:55

experimental and then server actions is

play03:58

true because we want to have access to

play04:00

server actions

play04:01

and just to get things set up we're

play04:02

going to create a few folders in here

play04:04

the first one is going to be components

play04:07

this will house all of our components

play04:09

some people have the components

play04:11

in the app folder next to the page

play04:13

routes but I actually I enjoy my life so

play04:17

I'm not going to do that because I don't

play04:19

hate myself it creates a lot of clutter

play04:21

and if you do that you should rethink

play04:22

that

play04:23

um we're also going to create a folder

play04:24

called actions this will have all our

play04:27

server actions

play04:28

inside of it and then the last one will

play04:30

be a lib so this will be shared

play04:33

utilities

play04:35

between the client server

play04:38

so by now our database is ready so we're

play04:40

going to click connect

play04:42

we're going to create a password

play04:44

we will be using Prisma so it should be

play04:47

the default so just copy this

play04:50

and create a new file called dot in

play04:54

and now we're going to set up our

play04:55

database

play04:57

so what we're going to do is have our

play04:58

database we're going to do npm install

play05:00

Prisma Dash Dash

play05:03

save Dash Dev so this will install the

play05:05

Prisma RM which will basically uh we'll

play05:09

be able to create our schema schema from

play05:11

the project and then push it to the

play05:12

database with easy migrations and stuff

play05:14

like that so now that Prisma is

play05:16

installed we're going to do npx

play05:18

Prisma init which will emit the schema

play05:23

so as you see we have our schema file

play05:26

first thing we need to do in here change

play05:28

the provider to mySQL

play05:30

and leave this and then relation mode

play05:33

needs to be set to

play05:35

oops

play05:36

needs to be set to Prisma because no

play05:38

foreign keys with playing scale there

play05:40

are no foreign keys so prism will take

play05:41

care of that

play05:43

okay

play05:44

so now that we have our database set up

play05:46

let's install next auth

play05:49

so go to our terminal terminal type npm

play05:54

install next Dash auth

play05:57

while this is installing

play06:00

um we're going to use Discord as our

play06:02

oauth provider so log in with Discord so

play06:05

type in Discord developer

play06:10

go to your applications create a new

play06:11

application I've already created one

play06:13

called test API

play06:15

go to oauth

play06:17

copy the client ID

play06:20

and put it in your dot in so you'll have

play06:23

access to it so this will be Discord

play06:25

apply underscore ID like this boom and

play06:29

then we're also creating one Discord

play06:30

client Secret

play06:35

uh so I'll just reset mine

play06:37

copy

play06:39

last thing you need to need to do here

play06:42

is create a callback or a redirect URI

play06:45

so this so for testing it would be HTTP

play06:48

localhost 3000 forward slash API forward

play06:51

slash auth forward slash callback

play06:53

forward slash Discord

play06:55

so that will be the redirect you must

play06:57

have in here

play07:01

next thing you need to do

play07:06

I forgot to copy the database uh URL

play07:08

okay

play07:11

so now in your dot in you should have

play07:13

your database URL your Discord client ID

play07:16

and Discord client Secret

play07:18

so now we need to set up next auth with

play07:20

Prisma so to do that we're going to go

play07:22

to the docs for next auth

play07:31

we will click getting started

play07:36

Okay so

play07:41

what we're going to do is we're going to

play07:42

create a new uh

play07:44

in our source folder we're going to

play07:46

create a new uh folder called pages

play07:50

and in Pages we're going to create a

play07:52

folder called API forward slash auth

play07:55

bracket dot dot next auth

play07:58

dot TS

play08:00

that is not a file

play08:09

I messed this up let me restart

play08:11

so in in API auth you're going to create

play08:15

a file called dot next auth dot TS

play08:20

yep dot dot dot triple dot next off.ts

play08:24

uh in here we're just going to copy and

play08:27

paste

play08:28

from the docs

play08:31

and then in here we're going to

play08:34

npm

play08:35

install next auth

play08:38

I forgot if we installed this but we'll

play08:39

just install it one more time okay

play08:44

so that we're going to change this to

play08:45

Discord provider

play08:48

and the import will be providers Discord

play08:54

client ID

play08:57

copy this

play09:01

copy this

play09:02

like this

play09:04

so this might be null so just put this

play09:07

in a string

play09:08

so we'll stop here to that type error

play09:13

okay next thing we need to do is set up

play09:16

to work with Prisma

play09:20

so Prisma is an adapter so we're coming

play09:23

to here

play09:26

we'll run these commands so first thing

play09:28

we need to do

play09:38

is run npm install then install command

play09:50

we need to create a new Prisma client

play09:52

like this so we'll say import Prisma

play09:54

client and then cons Prisma equals new

play09:57

Prisma client and then set up the

play10:00

adapter like this whoops

play10:03

say Oops why is this not

play10:13

and of course we need to

play10:16

bring it in like this and then this

play10:19

seems to be as

play10:21

uh

play10:23

options to get rid of that type of error

play10:26

so next thing we need to do is add the

play10:29

schema it needs

play10:33

oops

play10:37

so copy and paste the schema

play10:42

into here

play10:46

now you're going to go into the terminal

play10:47

and do

play10:49

MPX Prisma DB push

play10:53

so this will push the schema to your

play10:55

newly created my squirrel

play10:57

platform

play10:59

next thing you want to do is run npm run

play11:02

Dev

play11:04

so now we should be set up with Prisma

play11:07

and next auth

play11:15

all right

play11:16

so now if everything's set up correctly

play11:18

we should be able to go to

play11:22

um API auth sign in

play11:26

so there we go we have our Discord sign

play11:28

in button we'll click sign in

play11:30

will this work on the first try

play11:34

and it did so if you set everything

play11:36

correctly should we bring you back to

play11:38

this home page

play11:40

and now if we inspect in our cookies we

play11:42

should see the next auth session token

play11:44

so that means we are logged in and to

play11:46

verify this let's go back to Planet

play11:48

scale

play11:49

go to console

play11:54

first of all

play11:57

we need to downgrade this branch

play12:05

um

play12:09

downgrade this Branch to not production

play12:13

so we can access

play12:15

uh so click allow web console access to

play12:18

production branches because we want to

play12:19

be able to see the data

play12:21

not recommended though so select all

play12:24

from user and here is the user so this

play12:27

is me so I just signed in so any users I

play12:29

create accounts with Discord on your app

play12:31

will appear here

play12:33

so cool next thing we want to do is in

play12:36

our app

play12:37

page.txs let's get rid of the

play12:39

boilerplate that versel provided us

play12:48

boom so now

play12:51

should just be a blank white screen

play12:55

oh we also want to clear out the CSS

play12:58

they put

play12:59

so now it should just be a blank white

play13:01

screen

play13:02

okay cool

play13:04

first thing uh I want to do is in the

play13:06

layout let's change the font from enter

play13:08

to Poppins

play13:11

a far superior font

play13:16

I want it to look nice so we will not be

play13:18

using enter

play13:20

so for some reason with this font you

play13:22

have to manually

play13:26

like specify

play13:32

the uh the widths

play13:40

okay

play13:43

so now we have just a nice white canvas

play13:45

to create our app on so first thing we

play13:48

want to do let's create a a header a

play13:51

navbar so in our components create a

play13:53

file called header.tsx

play13:55

export function header

play14:02

boom uh let's wrap this in a nav

play14:07

and in here we'll do Flex item Center

play14:10

let's do a gap of eight

play14:13

so here will be our logo and we'll do

play14:16

justify between

play14:18

so in here this can be our our local

play14:20

logo and we'll just do a simple text

play14:22

logo and with the href we'll just point

play14:24

back to the main page

play14:27

um so this can just be logo

play14:29

and let's make this a size of 2XL font

play14:32

semi-bold

play14:35

text black and when you hover it lower

play14:38

the opacity

play14:40

so now if we go back we should see

play14:44

we also but first thing we need to do is

play14:46

in our head uh

play14:48

page.txx import the header component

play14:52

so now we should see our logo there

play14:56

then

play14:58

we'll create another group

play15:00

of item Center this time with a gap of

play15:02

four just to shrink it

play15:04

the link so this so if we go to our

play15:08

little design document

play15:12

oops I got rid of it uh so we're gonna

play15:14

have two sections on the

play15:15

home page we're going to have one for

play15:17

the features so this will be slash

play15:19

hashtag features

play15:22

that's the href and then we'll have one

play15:24

for pricing

play15:27

which will be

play15:29

#pricing

play15:32

uh we'll make the font medium text small

play15:34

text black new hover we'll just lower

play15:38

the opacity

play15:41

and we will copy and paste this

play15:49

so there we go

play15:51

and we'll also create one more button

play15:55

to go to the dashboard

play15:57

this would be forward slash dashboard

play15:58

dashboard

play16:00

uh let's set the text to White BG black

play16:04

let's add some padding

play16:06

round it out a little bit

play16:11

so now it should be look like a button

play16:13

there we go

play16:14

now let's add some padding on the y-axis

play16:18

let's see py2

play16:23

like this

play16:25

and then let's let's set a Max width on

play16:27

the snap

play16:30

let's do 5xl

play16:32

like this M Auto PX of four so we want

play16:35

to have padding on the sides instead of

play16:37

Max width

play16:38

so there we go I think it could use a

play16:40

little more padding on the Y so let's do

play16:41

four

play16:44

there we go okay

play16:47

so that's already looking pretty good

play16:50

um

play16:51

next thing I want to do is let's get

play16:54

start working on the dashboard

play16:57

so in the app we're going to create a

play16:58

folder called dashboard

play17:01

and I like to mock everything out before

play17:03

I implement it just so we have something

play17:05

to look at

play17:06

make sure on the right track in the

play17:08

dashboard we're going to create a

play17:09

layout.tsx

play17:10

export function

play17:13

this has to be default function

play17:17

and if I'm going too fast all this code

play17:19

will be on GitHub so you can

play17:23

follow along if I'm moving too fast

play17:34

two and then in here we need to put the

play17:37

children

play17:38

so in the dashboard

play17:43

we'll call this page

play17:47

we'll wrap this in the main tag we'll

play17:49

say

play17:51

dashboard okay

play17:53

so now let's go to forward slash

play17:55

dashboard and there is our dashboard uh

play17:58

text so first thing I want to do is

play18:00

throw in the header here

play18:05

um

play18:06

and the code we used to set the max

play18:08

width

play18:10

um let's let's bring that on for the

play18:12

children so the children are

play18:14

locked in to the max with the 5xl with

play18:18

the padding and stuff

play18:24

like this all right so that's already

play18:25

looking pretty good

play18:27

trying to think okay

play18:31

so now I have the dashboard page and we

play18:34

don't want them to see this dashboard

play18:36

page if they're not logged in so in the

play18:37

layout

play18:39

what we're going to do is we're going to

play18:40

just export

play18:42

default async functions will be a server

play18:44

side function and we'll say

play18:51

you have to make sure the auth options

play18:52

are exported from this but where I did

play18:54

that so we're going to say const session

play18:56

equals await get server session

play19:01

with the off options

play19:04

um from the page so in the pages route

play19:06

we have the auth options so we're going

play19:07

to import that from there

play19:09

we're going to say if session

play19:11

console.log

play19:13

user is logged in else if session oops

play19:18

I'm just saying we're just going to say

play19:20

else console.logged user is not logged

play19:23

in

play19:26

so now if we go to the terminal

play19:29

we will see

play19:31

that the user is logged in but if I go

play19:34

to my cookies

play19:35

well if we go to the API

play19:38

auth sign

play19:41

out

play19:42

let's sign out which will clear the

play19:44

cookies and go to dashboard user is not

play19:46

logged in so what we want what do we

play19:49

want to do if the user is not logged in

play19:51

let's redirect to API auth sign in

play19:56

and we'll say await

play20:00

oops wrong import redirect

play20:03

you have to import from navigation next

play20:05

slash navigation

play20:07

so boom as you see

play20:09

if we go to dashboard when we're not

play20:11

logged in it will redirect us to the

play20:12

sign-in page so now let's sign in with

play20:14

Discord

play20:20

authorize

play20:23

now we can go to the dashboard page and

play20:25

we see the dashboard page so this will

play20:27

work on all sub routes of dashboard so

play20:29

if we do dashboard slash billing we

play20:31

don't have to implement that logic again

play20:33

since it's on the root layout and

play20:35

execute it on the server so they won't

play20:36

even see that page we'll intercept them

play20:39

before

play20:41

next thing I want to do is let's set up

play20:43

stripe

play20:44

which is the billing software so we're

play20:47

going to do is we're going to say stripe

play20:50

uh

play20:53

uh what is this stripe Js

play20:59

so let's do include

play21:04

my bad stripe node.js

play21:11

here's what we're going to do we're

play21:12

going to run npm install stripe

play21:18

um we also want

play21:23

this should come

play21:27

if I'm not mistaken

play21:35

let me

play21:38

this should come with type safety

play21:57

I believe that does come with type

play21:58

safety

play22:02

let me make sure oh is it

play22:21

yep okay sorry about that had to make

play22:23

sure it came with type safety

play22:25

um so it does by default next thing you

play22:27

want to do is go to strike.com

play22:30

we're going to

play22:34

uh go into test mode

play22:37

actually I'm gonna go to a different

play22:39

account let's get my testing account

play22:43

so this account is in test mode so we're

play22:45

going to do is we're going to go to

play22:46

we're going to search for API key

play22:52

reveal secret key copy the secret key

play22:55

into our DOT in environment we're going

play22:56

to say

play22:58

right Secret

play23:01

like this

play23:03

so now if we go to our layout

play23:06

when we create a new stripe

play23:09

we're going to pass in

play23:13

uh we're going to stringify this and

play23:15

we're going to do process.m dot strike

play23:17

Secret

play23:19

now to keep us from having to type this

play23:21

every time in our lib we're going to

play23:23

create a file called just called

play23:25

stripe.ts

play23:27

and we're just going to copy this code

play23:30

into here

play23:32

we're going to export this stripe object

play23:36

so now look at that so now

play23:41

we should be able to import

play23:46

stripe

play23:50

from our

play23:52

um

play23:53

lid folder and now we have access to all

play23:55

the stripe apis using our authenticated

play23:57

key

play23:58

also for this we can clean this up and

play24:01

we'll say We'll create a new file called

play24:02

auth

play24:03

.ts we'll say export default async will

play24:07

not default X4 async function

play24:10

called

play24:12

say check if user is logged

play24:17

we'll call this function must be

play24:20

logged in

play24:23

and basically we're just going to copy

play24:25

this code to here

play24:29

like this

play24:33

get server session

play24:38

and we can say if so if there's not a

play24:40

session redirect else do nothing

play24:43

and we need to import auth options

play24:46

so now in our layout we can just say

play24:48

await

play24:50

must be logged in so now

play24:52

we don't have to type that code every

play24:54

time we want to lock down a route

play24:56

which is pretty good

play24:59

next thing we want to do

play25:01

is we want to create a stripe customer

play25:04

for our user if they if there's already

play25:06

not a stripe customer so in our schema

play25:09

we're going to do is we're going to say

play25:11

stripe customer ID as a string

play25:15

which maybe null

play25:17

and the default

play25:20

now we don't need default so stripe

play25:21

underscore customer ID

play25:24

and we're going to push this schema so

play25:25

close down the other running instance

play25:27

and do npx Prisma DB

play25:30

push so this will push the schema and

play25:33

automatically migrate

play25:35

our databases so everything's in sync

play25:38

and now we can do npm run Dev to get the

play25:41

dev server running again

play25:42

and in here

play25:44

well we'll say well actually in our

play25:46

stripe we'll say

play25:47

export

play25:49

async function

play25:52

create

play25:53

customer

play25:58

uh

play26:00

if

play26:02

null

play26:05

so first thing we're going to do is

play26:06

we're gonna get the session so get

play26:08

server session pass in the auth options

play26:13

um if just a redundant check

play26:15

if there's a session

play26:18

and you have to await this this is a

play26:20

promise so if there's a session if

play26:22

session

play26:24

first of all so the session that returns

play26:27

just returns the email username and and

play26:30

the image so we need to fetch the user

play26:33

from the database based off the email so

play26:34

first thing we're going to do is we're

play26:35

going to say const user equals

play26:41

before we can do that though

play26:44

we must initialize Prisma

play26:49

which are orm don't forget to do that I

play26:51

almost forgot to do that so constant

play26:53

Prisma equals await Prisma dot user dot

play26:55

find first where we're going to say

play26:58

email is session.user.email

play27:03

okay

play27:05

so if user

play27:07

dot stripe customer ID so if not

play27:10

user.stripe customer goody we're going

play27:12

to create a customer

play27:14

so we're going to say const customer

play27:16

equals stripe dot customers dot create

play27:22

email is going to be user.email

play27:27

and we're going to put this in a string

play27:31

to make sure that the string exists so

play27:34

then we're going to do await Prisma dot

play27:36

user dot update

play27:38

where the ID is our user.id

play27:42

the data we want to overwrite is stripe

play27:46

customer ID and this is going to be

play27:47

customer dot ID

play27:51

you want to await this

play27:53

so this is going to be

play27:55

customer.id

play27:57

like this

play28:00

so now we're also going to do create

play28:03

customer if null we're going to call

play28:05

that function from our dashboard layout

play28:07

so now

play28:11

let's just

play28:13

refresh

play28:18

check the terminal make sure there's no

play28:19

errors

play28:21

no errors internal

play28:23

so then if we go back to our database

play28:27

we should see yep there's our customer

play28:29

so we have successfully created a

play28:32

customer but now if we refresh

play28:34

the number so this is oncc starting with

play28:37

oncc

play28:38

let's refresh this a couple times it

play28:40

shouldn't overwrite this customer so now

play28:42

if we refresh

play28:44

yep same customer and we can triple

play28:46

check this in stripe by going to the

play28:48

customers

play28:51

let that load

play29:05

that's taking a second to load

play29:21

and it's taking a really long time to

play29:22

load

play29:24

but it's there if you check your stripe

play29:25

it'll be there

play29:29

so next thing we need to do

play29:32

is create a product

play29:35

in stripe which we will use to measure

play29:39

the API usage

play29:43

so first thing we need to do is add to

play29:45

our schema

play29:46

stripe

play29:48

subscription

play29:51

item this is going to be a string with a

play29:53

question mark

play29:54

while we're here we're also going to be

play29:56

logging all the API bins so let's call

play29:58

this log

play30:01

uh copy the ID from user we just want a

play30:04

random ID

play30:06

so we'll say We'll bind this to a user

play30:10

by their ID

play30:13

and we'll say the method can be a string

play30:16

so this will be get or post all of them

play30:18

what we get for our use case

play30:20

and the status will be an INT

play30:24

and also thrown at created which will be

play30:26

a date time

play30:28

and the default for date time can just

play30:30

be now

play30:34

so now if we go here and do MPX Prisma

play30:36

DB push this will sync our schemas

play30:41

so everything will be in sync

play30:43

okay for some reason stripe has decided

play30:45

to come back

play30:48

let's close down some of these tabs

play30:54

so in products

play30:56

there's anything unload

play30:58

nothing's loading right now what

play31:07

do nothing's loading

play31:10

let's try it for some reason

play31:28

uh let me

play31:31

just reach there we go okay

play31:34

so resetting my cash work okay so here's

play31:36

the um the customer created earlier it's

play31:39

now in products

play31:41

we're gonna do add product

play31:44

we'll call this API access the

play31:47

description we gain access to our API

play31:52

so what we're going to do is we're going

play31:54

to say the usage is metered it's gonna

play31:57

be the sum of usage valids uh During the

play32:00

period and we'll say one dollar

play32:04

per API

play32:06

request

play32:09

was recent okay so we're going to save

play32:11

the product

play32:15

and this is important we need the price

play32:18

ID right here

play32:21

okay so let's go back here

play32:24

and in our stripe.ts I'm just going to

play32:27

put paste the price ID right here

play32:30

boom

play32:32

so now what we need to do is

play32:34

we need to create a checkout link

play32:38

um so they can check out and actually

play32:41

get paid for the subscription because we

play32:43

can't increment their usage if there's

play32:45

no subscription to increment

play32:49

so first thing what we're going to do

play32:52

we're going to create a function called

play32:53

export async function create

play32:56

check out link

play33:00

we're also going to create another

play33:01

function

play33:03

called has

play33:04

subscription

play33:09

so we're uh in this has subscription

play33:11

we're going to copy this from our create

play33:13

customer if null just to get the session

play33:15

and we're going to return a weight

play33:18

stripe

play33:20

dot subscriptions Dot

play33:25

what does this retrieve

play33:29

is this

play33:31

actually I think what we can do is we

play33:32

can actually do con subscriptions

play33:37

equals away

play33:40

stripe.customers.retrieve the

play33:41

user.strike customer ID

play33:47

let's say subscriptions

play33:49

object what

play33:55

hmm

play33:57

what we can do actually is we'll say

play33:59

stripe git

play34:01

subscriptions by customer

play34:04

node.js

play34:24

actually what we can do is we can use

play34:25

chat gbt

play34:27

to speed this up because I'm actually

play34:30

why they don't have a stripe dot

play34:32

customers

play34:34

for subscription item like

play34:37

list

play34:39

oh

play34:41

they do my bad

play34:43

stripe customer ID okay so this

play34:45

stripe.scriptions.list and the customer

play34:48

is a customer ID

play34:50

so we'll say this

play34:52

okay let's

play34:54

this needs to be in a string like this

play34:58

we'll leave chat DBT open we might be

play35:00

coming back to that

play35:01

uh we'll say return

play35:05

subscriptions.data dot length is bigger

play35:07

than

play35:08

zero so if they have a subscription

play35:10

turn else turn false oops

play35:17

whoops okay

play35:19

so now we'll say has sub equals await

play35:25

has subscription

play35:29

console.log has sub

play35:33

if they have a subscription we'll say

play35:34

has

play35:37

not

play35:38

go into our terminal

play35:42

refresh the page we can see

play35:48

has not okay so we don't have a

play35:50

subscription

play35:51

so back in our stripe.ts we're going to

play35:53

say in our checkout link we're going to

play35:55

say const checkout equals await stripe

play35:58

dot checkout dot sessions dot create

play36:02

and we're going to say line items

play36:12

so the first so we're only have one line

play36:14

item and the price ID

play36:16

is this price

play36:22

we also need to define a success URL

play36:25

which will just be localhost 3000 we'll

play36:29

say dashboard slash billing success it's

play36:32

true

play36:34

B like this

play36:40

I'll say cancel

play36:42

cancel URL like this

play36:46

be sure to put this in a string

play36:49

finally we're going to say return

play36:51

check out Dot

play36:54

URL

play36:58

and I think in here

play37:03

so create checkout link will take in

play37:06

a customer ID

play37:10

like this

play37:12

so now in here we'll say

play37:19

and then create customer if null

play37:23

what we can do

play37:28

is down here

play37:31

we'll say user two

play37:35

I'm going to refetch the user

play37:37

let's say return user2 Dot

play37:40

Drive customer

play37:43

ID

play37:45

uh so in here we'll say stripe we'll say

play37:48

const customer equals this

play37:51

will say console.log customer

play37:55

so now in the console we should have our

play37:57

yep there it is already

play37:59

uh customer ID so now we'll say const

play38:02

checkout

play38:04

link equals await

play38:08

create

play38:10

checkout link for our customer

play38:16

we'll put this in a string make sure it

play38:18

it's a valid string

play38:20

and let's just

play38:23

whoops

play38:31

and I forgot this

play38:36

mode is going to be

play38:39

subscription so the mode has to be

play38:41

subscription is recurring

play38:43

so now

play38:46

we should see a link in the terminal

play38:49

yep so now if we copy and paste this

play38:52

link that's in the terminal

play38:56

it is um it autofills the email with the

play38:59

customer and as you see it says we will

play39:01

be build one dollar per API call or

play39:04

usage

play39:06

so great

play39:07

so now what we can do

play39:13

now that we have all this we can get rid

play39:15

of these console logs

play39:17

and we can say down here

play39:21

we can actually move

play39:23

um

play39:25

these two things into the page

play39:28

so we'll change this to async

play39:31

like this and we say has subscription

play39:35

create

play39:39

checkout link

play39:42

we also we need this customer

play39:45

create customer if null like this and

play39:49

we'll say if they have a subscription

play39:52

we'll do this with our ternary operator

play39:55

so if they have a secret if not what

play39:57

we'll do is we'll say

play40:00

we'll say Min height of 60 view heights

play40:04

grid

play40:06

Place item Center we'll round it

play40:09

and give it a padding X of 6 padding y

play40:12

of 10 BG slate 100 just to darken it a

play40:15

little bit

play40:17

and this will be

play40:20

this will say

play40:22

you have no subscription

play40:26

check out now

play40:27

in the href will be the checkout link

play40:32

and then for styling we say font medium

play40:34

text SM and then on Hover we'll

play40:37

underline

play40:38

we'll throw this in a string just to

play40:40

make sure it is a string

play40:45

so now boom

play40:47

and actually we can take we can put this

play40:49

at text base so it's a little bigger

play40:53

so now

play40:55

if they go to their dashboard and they

play40:56

don't have subscription it will say hey

play40:58

check out

play41:00

so now if we click this

play41:04

uh and we put in our test data so for

play41:07

stripe it is just four two four oops my

play41:10

bad four two four two four two all the

play41:12

way down

play41:16

like this

play41:17

say 39651

play41:20

let me subscribe now we should get a 404

play41:22

error because the page we're redirecting

play41:24

to doesn't exist actually

play41:27

but if we go back to our dashboard

play41:31

that message doesn't go away because we

play41:32

have a subscription

play41:34

so now

play41:35

at the top here

play41:38

we'll say

play41:41

uh throughpx

play41:43

four py2 BG Emerald say 100 text white

play41:48

actually

play41:49

let's do BG Emerald 400 text White

play41:53

font medium

play41:55

we'll say

play41:57

you have a subscription

play42:05

boom just like that

play42:07

so now what we've done is we've created

play42:09

a way to check out if you don't have a

play42:11

subscription

play42:13

and if you do have subscription it tells

play42:15

you and gives you a nice message

play42:18

so now what you can do

play42:27

to take that font size

play42:30

okay so now that we have the basics of

play42:32

billing in place

play42:33

lettuce

play42:35

um

play42:36

create the API endpoint

play42:40

so how are we going to do this so in our

play42:42

schema what we can do is an under user

play42:45

we'll create a thing called API key and

play42:48

this is going to be a string

play42:51

it's like this and we'll say npx

play42:55

Prisma DB push we'll sync the schemas

play42:58

again

play43:00

and now if we rerun the project every

play43:02

time you change the schema you have to

play43:04

rerun the project to regenerate Prisma

play43:08

so now

play43:11

if we head down

play43:13

to our um the stripe where we say create

play43:16

customer if null if we have to create a

play43:19

customer what we can do is we can just

play43:20

create their API key right here which

play43:22

will just be random uid

play43:25

uh we can make a custom we'll say API

play43:27

key

play43:28

like this

play43:32

actually this will be a separate thing

play43:38

so if not user.api key

play43:41

copy this

play43:43

say API key

play43:47

we'll say

play43:49

secret change it up random uid

play43:55

so now

play43:56

if we go back to our test console after

play43:58

you go back to this page

play44:03

you see the API Keys generated

play44:06

so let's display this to the user so

play44:08

they know how to interact with the API

play44:09

so first thing here is we'll just make

play44:12

this a column

play44:14

and this

play44:19

and we're just let's design a nice card

play44:23

so the card will have a couple sections

play44:25

the first one will just be the label

play44:30

called API key

play44:33

as let's have a divide between them

play44:35

divide y divide zinc 200 border let's

play44:38

give it a border of zinc 200. rounded in

play44:41

b

play44:42

let's give this some padding of py6 py4

play44:50

and then

play44:51

let's also fetch the user so here we'll

play44:53

say const user equals await prisms

play44:57

oh wait Prisma

play45:01

what am I thinking we have to

play45:03

import Prisma again

play45:09

at the top here

play45:10

prisma.user.findfirst

play45:13

where email

play45:21

when I get the session so this will be

play45:23

await get server session

play45:25

of the auth options

play45:29

so we're going to get the the user from

play45:32

the database of the current sessions

play45:33

email

play45:35

just like that

play45:37

so now we'll say user dot API key

play45:42

and we'll say uh we'll make the font

play45:44

small the font mono space

play45:47

we'll make it text zinc 800 we'll add py

play45:51

of six p y or PX of six py of eight

play45:59

and now look at that so now uh p y of a

play46:03

is a little excessive so boom so now

play46:07

we have created a way for the user to

play46:09

see their API key

play46:12

and know if they need to have a

play46:14

subscription or not

play46:16

so this is pretty cool so now what we

play46:18

can do is recreate our first API

play46:20

endpoint

play46:22

um

play46:23

uh first API endpoint so we can start uh

play46:26

billing them

play46:28

so under app we're gonna actually we'll

play46:30

do this in the Pages directory the page

play46:32

is directly for me is easier to work

play46:33

with so do API let's call this inpoint

play46:38

rename employee.ts

play46:42

export default

play46:45

async function called Handler

play46:48

this takes in a request of next API

play46:51

request response next API response

play46:57

we're going to say res Dot

play47:01

status 200.json

play47:06

works

play47:07

true

play47:09

so now if we go here

play47:12

API in point

play47:15

we get that response back

play47:17

so first thing we want to do

play47:19

and this API endpoint will just return a

play47:22

random uuid and then and we'll charge

play47:24

them a dollar for that and then you can

play47:25

replace this with your own logic so

play47:27

first thing we need to do is

play47:29

in this request we want to bring the key

play47:32

along so we'll say API key equals

play47:35

our API key like this so we'll say

play47:39

const

play47:40

Avi key equals request.query

play47:45

if not API key so if there's not an API

play47:47

key let's say res dot status

play47:50

I forgot what the proper I think it's

play47:52

401 I believe is the right status code

play47:56

Dot

play47:58

Json we'll say error is

play48:02

must have a valid API key

play48:07

so we shouldn't get that here but if we

play48:09

remove it

play48:11

we'll say must have a valid API key

play48:14

so now we'll say

play48:17

const user equals await and then we'll

play48:20

need to import Prisma again

play48:24

so we'll bring in Prisma here

play48:29

just like this

play48:31

await Prisma dot user.findfirst where

play48:35

the API key is the one from the query

play48:39

parameter

play48:42

um

play48:43

a smart thing as a challenge is you

play48:45

might want to put this in the header so

play48:46

you might want to put this as a bearer

play48:47

token

play48:49

um so that but you guys can guys figure

play48:51

out how to do that so if there's not a

play48:53

user with this API key with this API key

play48:56

we can say

play48:57

there is no user with such

play49:01

apiki

play49:04

so we shouldn't get that here but if we

play49:07

jamble it we'll say there's no user of

play49:09

the API key

play49:12

but we get that boom

play49:17

status true okay

play49:20

so now we have a request

play49:23

uh we have an authenticated request with

play49:25

the user object so we have the user

play49:27

that's making the request next thing we

play49:29

need to do

play49:31

is get the subscription item and then

play49:33

increment the usage

play49:35

so basically what we're going to do

play49:36

we're going to say cons

play49:38

sub item

play49:40

well first of all let's get the customer

play49:41

so we'll say await stripe import the

play49:44

Stripe from a library customers.retrieve

play49:47

user dot stripe customer ID

play49:52

like this

play49:57

then we're going to say const

play49:59

subscription equals await dot Subs um

play50:04

how do we do that

play50:07

I'll just copy and paste this no need to

play50:09

rewrite it

play50:11

I'm going to say cons item equals

play50:13

subscriptions

play50:15

.data dot get let's get the

play50:18

first one

play50:24

oh it's ah my bad

play50:25

items

play50:27

dot data dot get oops dot at my bad

play50:31

the first one so now we can do item id

play50:33

all right

play50:35

so now if not item

play50:40

let's return a code that says 403

play50:43

you don't have a subscription

play50:45

you have no subscription so now we

play50:48

shouldn't get that if we do this

play50:52

so basically

play50:54

on all stripe subscriptions there's an

play50:57

item with the price uh the price object

play51:00

attached to it so we're basically saying

play51:02

if there's no items in the subscription

play51:03

then there they have no subscription so

play51:06

that's how you check if they have paid

play51:08

for it or not so next what we want to do

play51:09

is want to do a weight stripe

play51:12

the subscription items

play51:14

dot create usage record

play51:17

so the ID will be item.id

play51:21

we'll say the quantity will be one so

play51:23

they have

play51:24

incremented their usage

play51:26

by one

play51:28

and we'll say

play51:33

and we let's just return this in the

play51:35

result

play51:37

just so we can see it so now if we do

play51:38

this

play51:39

boom

play51:41

we can see we have created a usage

play51:42

record

play51:44

so now let's return the actual data

play51:46

constant data equals

play51:48

random uuid like this

play51:58

so some kind of special key so boom

play52:01

every time I hit this API endpoint with

play52:03

my valid API key we're getting our

play52:05

special key back and this special key

play52:07

can be anything you want this can be

play52:08

stock price data

play52:11

um just data from your database can be

play52:14

anything

play52:15

so now if we go back to our customers in

play52:18

stripe

play52:19

we can see

play52:22

that they have a subscription and can I

play52:25

we can see in their most recent invoice

play52:37

whoops

play52:40

ah we can see the usage record so this

play52:43

is all the API calls

play52:45

um

play52:48

that they have made so now what we want

play52:50

to do is we have our log that we wanted

play52:52

to maintain

play52:54

so what we can do here is we can do

play52:56

await

play52:57

prisma.log dot create

play53:00

the user

play53:02

data so

play53:05

prisma.log.create with our data

play53:07

the user ID is going to be user.id

play53:11

the status code is 200.

play53:14

the method

play53:17

is going to be git

play53:19

this has um

play53:21

get rid of this type error by wrapping

play53:23

this in a string

play53:25

just like that and we'll say we'll

play53:26

actually let's get past the log along

play53:29

this log log so now

play53:32

if you go to your API key you see you

play53:36

get the data back you requested and the

play53:38

response that we've logged

play53:41

cool so now what we can do is we can

play53:45

show in the dashboard

play53:48

um

play53:48

all the requests we've made but so like

play53:51

the top 10 or something so in here what

play53:53

we can do is we can say

play53:55

const top 10

play53:58

top 10 recent logs equals oh wait Prisma

play54:02

DOT log dot

play54:04

find mini

play54:07

where the user ID is user.id

play54:14

let's do order by

play54:17

created descending

play54:20

and then we're also going to

play54:22

take so 10 take 10.

play54:25

so we're going to get all the logs by

play54:27

the user we're going to order them by

play54:29

the created date that's automatically

play54:31

assigned and we're going to take the

play54:32

first 10.

play54:35

so now we can do

play54:37

is let's just copy and paste this little

play54:39

card we had for API key we'll say log

play54:41

events

play54:43

we can do top 10 log events

play54:46

this is Thompson log events Dot

play54:51

map

play54:52

item

play54:54

index

play54:57

like this now you have to put a key for

play55:00

each one we'll do item dot method just

play55:03

to make sure it works

play55:04

we'll put this in a P tag

play55:08

boom just like that

play55:21

now it's going to yell at us if we do

play55:23

that so move this stuff around

play55:27

say item dot method

play55:41

so let's go ahead and create a few more

play55:42

logs so just go hit this endpoint a

play55:44

couple times and we come back

play55:48

we should see all our requests

play55:52

so in here let's make this a flex

play55:54

item Center and give it a gap of four

play55:59

we'll give it the status

play56:01

and then

play56:03

the date

play56:04

or dot created dot to date string like

play56:08

this

play56:15

so now we have our API key with the log

play56:18

events so

play56:20

next thing we need to do is show the

play56:21

current usage for this month so they can

play56:24

see how much they're about to get billed

play56:26

so what can do up here

play56:29

let's copy and paste this

play56:31

let's do

play56:32

current usage

play56:35

uh we'll put this as a variable called

play56:37

current

play56:39

usage now let's go back up here

play56:41

we'll say let current usage equals zero

play56:47

and then what we'll say is oh um

play56:49

const invoice oh wait stripe

play56:55

in

play56:58

retrieve upcoming

play57:06

like this

play57:10

so first thing we need to do is we need

play57:12

to get the subscription for this user

play57:17

like this

play57:22

description equals the dot at to get the

play57:25

data that the oops data Dot

play57:28

at zero

play57:32

dot ID

play57:36

and then we're only going to do this if

play57:37

they have a subscription

play57:45

so let's do this

play57:50

and then current usage equals invoice

play57:54

dot Syria

play58:00

amount uh a Mountain Dew I believe

play58:11

boom now stripe Stores

play58:14

um numbers without decimals so you're

play58:16

going to want to divide it by a hundred

play58:22

to get the usage because each usage is

play58:25

one dollar so we just divide it by 100

play58:27

we'll get the usage count so boom

play58:31

so this is um our API so every time we

play58:34

hit this endpoint

play58:36

we'll get data back

play58:38

and we'll see it reflected in real time

play58:40

in our dashboard we'll see

play58:42

the current usage go up

play58:44

with the log event logged

play58:48

so that is how you create a simple API

play58:52

SAS using next.js in the app router with

play58:56

stripe

play58:57

so some challenges I want you guys to do

play59:00

is I want you guys to create a way to

play59:02

manage the invoices with stripe customer

play59:05

portal

play59:07

and then I want you guys to create a

play59:11

graph of the log events so now you have

play59:13

all the logs here it's very trivial to

play59:15

create just a simple graph join the

play59:18

Discord below if you need any help this

play59:20

code will be on git cup GitHub pretty

play59:22

soon

play59:24

um and also I have a AI SAS kit if you

play59:27

want to build your own AI SAS

play59:29

I have a kit linked down below right now

play59:32

it's on sale for 74 dollars you can get

play59:34

a full stack production ready AI SAS kit

play59:38

complete with billing and open AI

play59:40

integration so I hope you guys learned

play59:42

something and I'll see you guys another

play59:44

day

Rate This

5.0 / 5 (0 votes)

英語で要約が必要ですか?