5

NextJS with Prisma on Kubernetes

 2 years ago
source link: https://willschenk.com/articles/2021/next_js_with_prisma/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Published December 8, 2021 #nextjs, #javascript, #prisma, #knative, #kubernetes

Now that we have our cluster up and running, lets look at how to build and deploy a NextJS app on it, including the database.

Create a NextJS app

We'll scaffold out a TypeScript app.

  npx create-next-app@latest --typescript  myapp 

  cd myapp

  npm run dev

Fireup a local data

  docker run -e POSTGRES_PASSWORD=awesome_password -p 5432:5432 postgres

Install prisma

We'll add the npm packages to our project.

  npm install prisma

  npx prisma init

Edit .env and change the url to be

  DATABASE_URL="postgresql://postgres:awesome_password@localhost:5432/mydb?schema=public"

Data Models

In primsa/schema.prisma add a model:

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  title     String   @db.VarChar(255)
  content   String?
}

Run the migration

  npx prisma migrate dev --name init

Run prisma studio to check it out

  npx prisma studio

Write the post page and api

This is a basic demostration, so nothing fancy.

tsconfig.json

"baseUrl" : "." and "strict":"false" to compilerOptions.

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;

pages/posts.ts

We'll use react-hook-form to make things simplier.

  npm install react-hook-form

A couple points.

  1. We query the database in getServiceSideProps

  2. We are fetching our API from window.location.origin

  3. We are totally hacking the window reload, I'm just lazy

  import Head from 'next/head';
  import { prisma } from 'lib/prisma';
  import { Post } from '.prisma/client';
  import Link from 'next/link';
  import { useForm } from 'react-hook-form';

  export default function PostsPage({ posts }) {
    const {
      register,
      handleSubmit,
      formState: { errors, isSubmitted },
    } = useForm();

    const onSubmit = (data) => {
      try {
        fetch(`${window.location.origin}/api/create`, {
          body: JSON.stringify(data),
          headers: {
            'Content-Type': 'application/json',
          },
          method: 'POST',
        }).then( () => {
          window.location.href = window.location.href
         } )
      } catch (error) {
        throw new Error(error);
      }
    };

      return (
        <>
          <ul>
              {posts.map((item: Post) => (
                  <li key={item.id}>{item.title} - {item.content}</li>
              ))}
          </ul>
          <form onSubmit={handleSubmit(onSubmit)}>
            <label>Title</label>
            <input 
              id="title"
              type="text" 
              {...register('title', {required: true})}/>
              <br/>
            <label>Content</label>
            <input 
              type="text" 
              id="content"
              {...register('content', {required: true})}/>
            <input type="submit"/>
          </form>
          </>
      );
  }

  export const getServerSideProps = async () => {
    const posts = await prisma.post.findMany({
      select: {
        id: true,
        title: true,
        content: true
      },
    });

    console.log(posts);
    return {
      props: {
        posts,
      },
    };
  };

api/create.ts

This is the api request to handle the post. It runs on the server.

import { prisma } from 'lib/prisma';

export default async function handler(req, res) {
  const { content, title } = req.body;

  try {
    const feedback = await prisma.post.create({
      data: {
        content,
        title,
      },
    });
    res.status(200).json(feedback);
  } catch (error) {
    res.status(400).json({
      message: `Something went wrong :/ ${error}`,
    });
  }
}

Setup postgres on your cluster

  helm repo add bitnami https://charts.bitnami.com/bitnami
  helm upgrade --install postgres bitnami/postgresql

Eventually the database will be available on postgres-postgresql.default.svc.cluster.local

We can get the password using

  kubectl get secret --namespace default postgres-postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode
1oOFIcOvB1

When we deploy the function we'll set the DATABASE_URL to look something like:

  DATABASE_URL="postgresql://postgres:[email protected]:5432/database?schema=public"

You'll need to update that with your actual password of course.

Migrate the database

We are going to setup the database from our local enviornment. We'll use kubectl to forward the local postgres port to the remote install. Remember to stop your local postgres container

  kubectl port-forward svc/postgres-postgresql 5432:5432

Then, using the password that we found above, deploy the current migrations to the postgres that's being forwared on localhost. This is a different url then the previous one shown!:

  DATABASE_URL="postgresql://postgres:1oOFIcOvB1@localhost:5432/database?schema=public" \
              npx prisma migrate deploy

You can also run prisma studio that way, by the way.

Deploy the service

Build image

We need to tweaks the dockerfile from the official docs to add prisma generate to get the client code. So, copy over that Dockerfile and then, right after it copies the node_modules over added

RUN npx prisma generate

For me it's on like 14, right before yarn build.

Since I'm working off an M1 mac, I need to set the --platform to make sure that the image is built using the correct binaries. An example of this is:

  docker build . --platform linux/amd64 -t wschenk/prismatest && docker push wschenk/prismatest

Create the service

    kn service create prisma \
       --image wschenk/prismatest \
       --env DATABASE_URL="postgresql://postgres:[email protected]:5432/database?schema=public"

Once this is up and running, you should be able to interact with your service and add messages.

References


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK