Cal.com: Scheduling infrastructure for Absolutely Everyone

Cal.com (formerly Calendso)

The open-source Calendly alternative.

About The Project

booking-screen

Scheduling infrastructure for absolutely everyone

The open source Calendly alternative. You are in charge of your own data, workflow and appearance.

Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customisations.

That's where Cal.com comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.

Recognition

Hacker News

Featured on Hacker News

 Featured on Hacker News

Product Hunt

Cal.com - The open source Calendly alternative | Product Hunt Cal.com - The open source Calendly alternative | Product Hunt Cal.com - The open source Calendly alternative | Product Hunt

Built With

Stay Up-to-Date

Cal officially launched as v.1.0 on 15th of September, however a lot of new features are coming. Watch releases of this repository to be notified for future updates:

cal-star-github

Getting Started

To get a local copy up and running, please follow these simple steps.

Prerequisites

Here is what you need to be able to run Cal.

  • Node.js (Version: >=15.x <17)
  • PostgreSQL
  • Yarn (recommended)

If you want to enable any of the available integrations, you may want to obtain additional credentials for each one. More details on this can be found below under the integrations section.

Development

Setup

Clone the repo into a public GitHub repository (or fork https://github.com/calcom/cal.com/fork). If you plan to distribute the code, keep the source code public to comply with AGPLv3. To clone in a private repository, acquire a commercial license)

git clone https://github.com/calcom/cal.com.git

Go to the project folder

cd cal.com

Install packages with yarn

yarn

Set up your .env file

  • Duplicate .env.example to .env
  • Use openssl rand -base64 32 to generate a key and add it under NEXTAUTH_SECRET in the .env file.
  • Use openssl rand -base64 24 to generate a key and add it under CALENDSO_ENCRYPTION_KEY in the .env file.

Quick start with yarn dx

  • Requires Docker and Docker Compose to be installed
  • Will start a local Postgres instance with a few test users - the credentials will be logged in the console
yarn dx

Development tip

Add NEXT_PUBLIC_DEBUG=1 anywhere in your .env to get logging information for all the queries and mutations driven by trpc.

echo 'NEXT_PUBLIC_DEBUG=1' >> .env

Manual setup

Configure environment variables in the .env file. Replace <user>, <pass>, <db-host>, <db-port> with their applicable values

DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
  1. If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB using Heroku

Set a 32 character random string in your .env file for the CALENDSO_ENCRYPTION_KEY (You can use a command like openssl rand -base64 24 to generate one).

Set up the database using the Prisma schema (found in packages/prisma/schema.prisma)

yarn workspace @calcom/prisma db-deploy

Run (in development mode)

yarn dev

Setting up your first user

Open Prisma Studio to look at or modify the database content:

yarn db-studio

Click on the User model to add a new user record.

Fill out the fields email, username, password, and set metadata to empty {} (remembering to encrypt your password with BCrypt) and click Save 1 Record to create your first user.

New users are set on a TRIAL plan by default. You might want to adjust this behavior to your needs in the packages/prisma/schema.prisma file.

Open a browser to http://localhost:3000 and login with your just created, first user.

E2E-Testing

Be sure to set the environment variable NEXTAUTH_URL to the correct value. If you are running locally, as the documentation within .env.example mentions, the value should be http://localhost:3000.

# In a terminal just run:
yarn test-e2e

# To open last HTML report run:
yarn playwright show-report test-results/reports/playwright-html-report

Upgrading from earlier versions

Pull the current version:

git pull

Check if dependencies got added/updated/removed

yarn

Apply database migrations by running one of the following commands:

In a development environment, run:

yarn workspace @calcom/prisma db-migrate

(this can clear your development database in some cases)

In a production environment, run:

yarn workspace @calcom/prisma db-deploy

Check for .env variables changes

yarn predev

Start the server. In a development environment, just do:

yarn dev

For a production build, run for example:

yarn build
yarn start

Enjoy the new version.

Deployment

Docker

The Docker configuration for Cal is an effort powered by people within the community.

If you want to contribute to the Docker repository, reply here.

The Docker configuration can be found in our docker repository.

Issues with Docker? Find your answer or open a new discussion here to ask the community.

Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.

Heroku

Deploy

Railway

Deploy on Railway

You can deploy Cal on Railway using the button above. The team at Railway also have a detailed blog post on deploying Cal on their platform.

Vercel

Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.

Deploy with Vercel

Render

Deploy to Render

Roadmap

Cal.com Roadmap

See the roadmap project for a list of proposed features (and known issues). You can change the view to see planned tagged releases.

Repo Activity

Contributing

Please see our contributing guide.

Good First Issues

We have a list of help wanted that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.

Translations

Don't code but still want to contribute? Join our slack and join the #i18n channel and let us know what language you want to translate.

ar translation bg translation cs translation de translation el translation en translation es translation es-419 translation fr translation he translation hu translation it translation ja translation ko translation nl translation no translation pl translation pt translation pt-BR translation ro translation ru translation sr translation sv translation tr translation uk translation vi translation zh-CN translation zh-TW translation

Integrations

Obtaining the Google API Credentials

  1. Open Google API Console. If you don't have a project in your Google Cloud subscription, you'll need to create one before proceeding further. Under Dashboard pane, select Enable APIS and Services.
  2. In the search box, type calendar and select the Google Calendar API search result.
  3. Enable the selected API.
  4. Next, go to the OAuth consent screen from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page.
  5. In the second page on Scopes, select Add or Remove Scopes. Search for Calendar.event and select the scope with scope value .../auth/calendar.events, .../auth/calendar.readonly and select Update.
  6. In the third page (Test Users), add the Google account(s) you'll using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
  7. Now select Credentials from the side pane and then select Create Credentials. Select the OAuth Client ID option.
  8. Select Web Application as the Application Type.
  9. Under Authorized redirect URI's, select Add URI and then add the URI <Cal.com URL>/api/integrations/googlecalendar/callback and <Cal.com URL>/api/auth/callback/google replacing Cal.com URL with the URI at which your application runs.
  10. The key will be created and you will be redirected back to the Credentials page. Select the newly generated client ID under OAuth 2.0 Client IDs.
  11. Select Download JSON. Copy the contents of this file and paste the entire JSON string in the .env file as the value for GOOGLE_API_CREDENTIALS key.

Adding google calendar to Cal.com App Store

After adding Google credentials, you can now Google Calendar App to the app store. You can repopulate the App store by running

cd packages/prisma
yarn seed-app-store

You will need to complete a few more steps to activate Google Calendar App. Make sure to complete section "Obtaining the Google API Credentials". After the do the following

  1. Add extra redirect URL <Cal.com URL>/api/auth/callback/google
  2. Under 'OAuth concent screen', click "PUBLISH APP"

Obtaining Microsoft Graph Client ID and Secret

  1. Open Azure App Registration and select New registration
  2. Name your application
  3. Set Who can use this application or access this API? to Accounts in any organizational directory (Any Azure AD directory - Multitenant)
  4. Set the Web redirect URI to <Cal.com URL>/api/integrations/office365calendar/callback replacing Cal.com URL with the URI at which your application runs.
  5. Use Application (client) ID as the MS_GRAPH_CLIENT_ID attribute value in .env
  6. Click Certificates & secrets create a new client secret and use the value as the MS_GRAPH_CLIENT_SECRET attribute

Obtaining Zoom Client ID and Secret

  1. Open Zoom Marketplace and sign in with your Zoom account.
  2. On the upper right, click "Develop" => "Build App".
  3. On "OAuth", select "Create".
  4. Name your App.
  5. Choose "User-managed app" as the app type.
  6. De-select the option to publish the app on the Zoom App Marketplace.
  7. Click "Create".
  8. Now copy the Client ID and Client Secret to your .env file into the ZOOM_CLIENT_ID and ZOOM_CLIENT_SECRET fields.
  9. Set the Redirect URL for OAuth <Cal.com URL>/api/integrations/zoomvideo/callback replacing Cal.com URL with the URI at which your application runs.
  10. Also add the redirect URL given above as a allow list URL and enable "Subdomain check". Make sure, it says "saved" below the form.
  11. You don't need to provide basic information about your app. Instead click at "Scopes" and then at "+ Add Scopes". On the left, click the category "Meeting" and check the scope meeting:write.
  12. Click "Done".
  13. You're good to go. Now you can easily add your Zoom integration in the Cal.com settings.

Obtaining Daily API Credentials

  1. Open Daily and sign into your account.
  2. From within your dashboard, go to the developers tab.
  3. Copy your API key.
  4. Now paste the API key to your .env file into the DAILY_API_KEY field in your .env file.
  5. If you have the Daily Scale Plan set the DAILY_SCALE_PLAN variable to true in order to use features like video recording.

Obtaining HubSpot Client ID and Secret

  1. Open HubSpot Developer and sign into your account, or create a new one.
  2. From within the home of the Developer account page, go to "Manage apps".
  3. Click "Create app" button top right.
  4. Fill in any information you want in the "App info" tab
  5. Go to tab "Auth"
  6. Now copy the Client ID and Client Secret to your .env file into the HUBSPOT_CLIENT_ID and HUBSPOT_CLIENT_SECRET fields.
  7. Set the Redirect URL for OAuth <Cal.com URL>/api/integrations/hubspot/callback replacing Cal.com URL with the URI at which your application runs.
  8. In the "Scopes" section at the bottom of the page, make sure you select "Read" and "Write" for scope called crm.objects.contacts
  9. Click the "Save" button at the bottom footer.
  10. You're good to go. Now you can see any booking in Cal.com created as a meeting in HubSpot for your contacts.

Workflows

Setting up SendGrid for Email reminders

  1. Create a SendGrid account (https://signup.sendgrid.com/)
  2. Go to Settings -> API keys and create an API key
  3. Copy API key to your .env file into the SENDGRID_API_KEY field
  4. Go to Settings -> Sender Authentication and verify a single sender
  5. Copy the verified E-Mail to your .env file into the SENDGRID_EMAIL field
  6. Add your custom sender name to the .env file into the NEXT_PUBLIC_SENDGRID_SENDER_NAME field (fallback is Cal.com)

Setting up Twilio for SMS reminders

  1. Create a Twilio account (https://www.twilio.com/try-twilio)
  2. Click ‘Get a Twilio phone number’
  3. Copy Account SID to your .env file into the TWILIO_SID field
  4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
  5. Copy your Twilio phone number to your .env file into the TWILIO_PHONE_NUMBER field
  6. Add your own sender id to the .env file into the NEXT_PUBLIC_SENDER_ID field (fallback is Cal)
  7. Create a messaging service (Develop -> Messaging -> Services)
  8. Choose any name for the messaging service
  9. Click 'Add Senders'
  10. Choose phone number as sender type
  11. Add the listed phone number
  12. Leave all other fields as they are
  13. Complete setup and click ‘View my new Messaging Service’
  14. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
  15. Create a verify service
  16. Copy Verify Service SID to your .env file into the TWILIO_VERIFY_SID field

Acknowledgements

Special thanks to these amazing projects which help power Cal.com:

Jitsu.com

Cal.com is an open startup and Jitsu (an open-source Segment alternative) helps us to track most of the usage metrics.

Download Details:

Author: Calcom
Source Code: https://github.com/calcom/cal.com 
License: View license

#opensource #typescript #nextjs #postgresql #prisma #tailwindcss

Cal.com: Scheduling infrastructure for Absolutely Everyone
Lawrence  Lesch

Lawrence Lesch

1672151100

An Open Source Application Built using The New Router in Next.js 13

Taxonomy

An open source application built using the new router, server components and everything new in Next.js 13.

Demo

screenshot-2

About this project

Right now, I'm using this project as an experiment to see how a modern app (with features like authentication, subscriptions, API routes, static pages for docs ...etc) would work in Next.js 13 and server components.

I'll be posting updates and issues here.

A few people have asked me to turn this into a starter. I think we could do that once the new features are out of beta.

Note on Performance

Warning This app is using the canary releases for Next.js 13 and React 18. The new router and app dir is still in beta and not production-ready. NextAuth.js, which is used for authentication, is also not fully supported in Next.js 13 and RSC. Expect some performance hits when testing the dashboard. If you see something broken, you can ping me @shadcn.

Features

  • New /app dir,
  • Routing, Layouts, Nested Layouts and Layout Groups
  • Data Fetching, Caching and Mutation
  • Loading UI
  • Server and Client Components
  • API Routes and Middlewares
  • Authentication using NextAuth.js
  • ORM using Prisma
  • Database on PlanetScale
  • UI Components built using Radix UI
  • Documentation and blog using MDX and Contentlayer
  • Subscriptions using Stripe
  • Styled using Tailwind CSS
  • Validations using Zod
  • Written in TypeScript

Roadmap

  •  Add MDX support for basic pages
  •  Build marketing pages
  •  Subscriptions using Stripe
  •  Responsive styles
  •  Add OG image for blog using @vercel/og
  •  Add tests
  •  Dark mode

Known Issues

A list of things not working right now:

  1. GitHub authentication (use email)
  2. Prisma: Error: ENOENT: no such file or directory, open '/var/task/.next/server/chunks/schema.prisma'
  3. Next.js 13: Client side navigation does not update head

Why not tRPC, Turborepo or X?

I might add this later. For now, I want to see how far we can get using Next.js only.

If you have some suggestions, feel free to create an issue.

Running Locally

Install dependencies using pnpm:

pnpm install

Copy .env.example to .env.local and update the variables.

cp .env.example .env.local

Start the development server:

pnpm dev

Warning This app is a work in progress. I'm building this in public. You can follow the progress on Twitter @shadcn. See the roadmap below.

Download Details:

Author: Shadcn
Source Code: https://github.com/shadcn/taxonomy 
License: MIT license

#typescript #nextjs #prisma #tailwindcss 

An Open Source Application Built using The New Router in Next.js 13
React Dev

React Dev

1671161813

Build FullStack Apps with with Prisma, Next.js & Postgres

Getting started with Prisma ORM, Next.js as full-stack app & Postgres running in a Docker container.

The heroku postgres is the free formula: https://prisma-next-postgres.vercel.app/ 

free avatar for testing: https://pravatar.cc/ 
github: https://github.com/daniellaera/prisma-next-postgres 
Should You Run Your Database in Docker?: https://vsupalov.com/database-in-docker/ 

Subscribe: https://www.youtube.com/@daniellaerachannel/featured 

#prisma  #nextjs  #postgres 

Build FullStack Apps with with Prisma, Next.js & Postgres
Vida  Herzog

Vida Herzog

1671027840

How to Create a Quick App using Prisma

In this Prisma article, we will learn about How to Create a Quick App using Prisma. Prisma is an ORM (object–relational mapping) tool for Node.js using TypeScript. The software integrates with many of the most popular databases today, including MySQL, SQL Server, SQLite, and MongoDB, and emphasizes a human-readable schema with a type-safe database client. Prisma also includes other features such as migrations, seed data, and a virtual database browser.

In this project, you will use Prisma to connect your Express application to a database server. You will build a schema to model a workout tracker application. Then you’ll create some seed data and use Prisma to run migrations and seed your database. Finally, you’ll create the web application using Pug and Tailwind CSS to build the application frontend.

Prerequisites

This tutorial uses the following technologies but doesn’t require any prior experience:

  • An IDE for JavaScript. I will use Visual Studio Code but you can use Webstorm, or any other IDE you prefer.
  • Node.js
  • A database, such as PostgreSQL, MySQL, SQLite, SQL Server, or MongoDB. In this tutorial we’ll use SQLite.
  • Okta CLI

If you’d like to skip the tutorial and check out the fully built project, you can go view it on GitHub.

Create your OAuth2 authorization server

Create a new directory for your application. Use the cd command to navigate to that folder.

Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run okta register to sign up for a new account. If you already have an account, run okta login. Then, run okta apps create. Select the default app name, or change it as you see fit. Choose Web and press Enter.

Select Other. Then, change the Redirect URI to http://localhost:3000/authorization-code/callback and use http://localhost:3000/ for the Logout Redirect URI.

What does the Okta CLI do?

The Okta CLI will create an OIDC Web App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. You will see output like the following when it’s finished:

Okta application configuration has been written to: /path/to/app/.okta.env

Run cat .okta.env (or type .okta.env on Windows) to see the issuer and credentials for your app.

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

Your Okta domain is the first part of your issuer, before /oauth2/default.

NOTE: You can also use the Okta Admin Console to create your app. See Create a Web App for more information.

Create your Express application

Next, you will use the express-generator application generator tool to quickly scaffold your application. Run the following command.

npx express-generator@4.16 --view=pug

Now you can install your packages.

npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
  • @prisma/client is used to access the database from your server code.
  • dotenv reads configuration settings from .env files like the one produced by the Okta CLI.
  • passport is a middleware for Node.js that is flexible enough to handle most authentication scenarios, including Okta. passport-openidconnect is a module for passport that lets you authenticate with OpenID Connect.
  • express-session is required for passport. Express apps use this package for session support. Your application must intiialize session support to use passport.
  • Tailwind CSS is the CSS framework you will use. If you’ve never used Tailwind before, you may wonder why it’s a development dependency. This is because Tailwind will dynamically build your CSS files from your views and configuration. More on that later.
  • prisma is the ORM you are using. This is a dev dependency because the Prisma library handles the migrations, seed data, etc., while the @prisma/client package is used in your application at runtime.

At this point, you will initialize Tailwind CSS and Prisma. You will configure them for usage later on.

Start with Prisma and run the following command.

npx prisma init

This command will add a new folder named prisma to your application. It also adds a file called .env to your project with some default configuration. Replace the contents of that file with the code below.

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}

In this example, I used SQLite because it’s easy and compact. But one of the cool things about Prisma and most ORMs is that they can support many databases. The boilerplate code includes comments about configuring your application for other database servers. Feel free to use what you feel most comfortable with and use the appropriate connection string.

Your seed data will use the SEED_USER_NAME setting. You want to ensure that it’s the same as the Okta username with which you sign in. This will allow the application to associate your logged-in user with the data you will seed into your database.

Next, update your package.json file with the following code.

{
  "name": "workout-app",
  "version": "0.0.0",
  "private": true,
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@prisma/client": "^3.13.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.0",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "passport": "^0.5.2",
    "passport-openidconnect": "^0.1.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "prisma": "^3.13.0",
    "tailwindcss": "^3.0.24"
  }
}

This will add the prisma seed command you need later.

Now you can initialize Tailwind CSS.

npx tailwindcss init

You’ve now added a new file called tailwind.config.js to the root of your application. Replace the contents of that file with the code below.

module.exports = {
  content: ["./views/**/*.pug"],
  theme: {
    extend: {},
  },
  plugins: [],
}

In this step, you tell Tailwind CSS where to find the classes you used in your application. You want Tailwind to look in the .pug files in your views directory. Tailwind CSS is highly extensible, as seen by the configuration object’s theme and plugins settings. A deep dive into this is out of the scope of this article, but I encourage you to look into Tailwind CSS’s site for more information.

Use Prisma to create your database

The next task is to create the database for your application. Steps will involve writing the schema, writing some seed data, creating the migration, and applying the migration, which will also seed your data.

The prisma init task from above should have added a file called schema.prisma to your prisma directory. Replace the code there with the code below.

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

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

model WorkoutLog {
  id        Int      @id @default(autoincrement())
  userId    Int
  exercise  String
  amount    Decimal
  units     String
  date      DateTime
  minutes   Int
  calories  Int
  user      User     @relation(fields: [userId], references: [id])
}

model User {
  id          Int          @id @default(autoincrement())
  username    String       @unique
  workoutLogs WorkoutLog[]
}

Make sure you replace {yourDatabaseProvider} with the provider you are using. As you can see, this file defines the User and WorkoutLog objects that you will store in the database. The WorkoutLog has basic information about the user’s workout on a specific day and then associates that record with the User object. You can also define keys, constraints, and indices from this file. Note the user property on the WorkoutLog has a relation defined by the userId to the id of the User table.

Add a file to the prisma directory named seed.js and add the following code to it.

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

require("dotenv").config();

async function main() {
  const user = await prisma.user.upsert({
    where: { username: process.env.SEED_USER_NAME },
    update: {},
    create: {
      username: process.env.SEED_USER_NAME,
      workoutLogs: {
        create: [
          {
            exercise: "Running",
            amount: 1,
            units: "Miles",
            date: new Date(2022, 1, 1),
            minutes: 8,
            calories: 100
          },
          {
            exercise: "Running",
            amount: 1.2,
            units: "Miles",
            date: new Date(2022, 1, 3),
            minutes: 10,
            calories: 120
          },
          {
            exercise: "Running",
            amount: 1.5,
            units: "Miles",
            date: new Date(2022, 1, 5),
            minutes: 12,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 1),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Heavy Bag",
            amount: 6,
            units: "Rounds",
            date: new Date(2022, 1, 3),
            minutes: 22,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 5),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 10,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 10,
            calories: 100
          },
        ],
      },
    },
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Prisma knows to use the seed.js file from the command you added to package.json. This file will insert the data when you run that command.

You can now add and apply the migration using the Prisma CLI. From the root of your application directory, run the following command.

npx prisma migrate dev --name init

The CLI has several ways to add and apply migrations. I suggest you understand the best way to manage migrations for your environment. The method above is the easiest and fastest way to prepare your database. As part of the migration process, this command will look for the seed command from your package.json and run that as well. Once complete, your database should be ready with a database full of seed data to work with.

Add OIDC authentication

Now that your database is ready, you can turn your attention to the core of your application.

First, add a new file in the root of your application called ensureLoggedIn.js and add the following code to it.

function ensureLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login')
}

module.exports = ensureLoggedIn;

This little piece of middleware will make sure the user is authenticated. If they aren’t, you will redirect them to the login route, which you will configure to use Okta. Otherwise, you will allow the user to reach the next screen.

Next, you can update your routes in the routes directory. First, remove users.js as you won’t be using that. Replace the code in index.js with the following.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});

module.exports = router;

Here you will pass a property for isAuthenticated so your layout page can properly display a login or logout button.

Next, add a file in the routes directory for dashboard.js with the following code.

var express = require("express");
var router = express.Router();

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const ensureLoggedIn = require("../ensureLoggedIn");

/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
  const username = req.user.username;

  const dbUser = await prisma.user.findUnique({
    where: {
      username: username,
    },
    include: {
      workoutLogs: true,
    },
  });

  res.render("dashboard", {
    title: "The Workout Tracker - Dashboard",
    isAuthenticated: req.isAuthenticated(),
    user: dbUser
  });
});

module.exports = router;

There’s a lot of magic in this route. First, you are using the ensureLoggedIn middleware to protect the route. Then you can extract the username from the request and look up the user in the database using the Prisma client. Since the user has a property for its workout logs, you can pass the user as the model and parse the logs on the pug view you will create. This will give view access to the user’s name as well. Of course, you could query the workoutLogs table directly and include features such as pagination and search if this approach better fits your workflow.

Now, replace the code in app.js with the following.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');

const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

var app = express();

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient();

require('dotenv').config({path:'./.okta.env'});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(session({
  secret: 'CanYouLookTheOtherWay',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());


const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;

// set up passport
passport.use('oidc', new Strategy({
  issuer: OKTA_OAUTH2_ISSUER,
  authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
  tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
  userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
  clientID: OKTA_OAUTH2_CLIENT_ID,
  clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/authorization-code/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/login', passport.authenticate('oidc'));

app.use('/authorization-code/callback',
  passport.authenticate('oidc', { failureRedirect: '/error' }),
  (req, res, next) => {
    res.redirect('/dashboard');
  }
);

app.post('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

You updated your routes to properly use the dashboard routes as well as any login or logout routes that Okta will use. You also set up passport to use Okta using the values the Okta CLI produced in the .okta.env file.

Add views to your Express application

By default, express-generator will add a few views for you. You will need to edit them and add a new one of your own. Start by opening the layout.pug file in the views directory and replacing the code there with the following.

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/output.css')
  body
    nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
      span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
      .w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
        .text-sm(class='lg:flex-grow')
        div
          if(isAuthenticated)
            form(action="logout" method="POST") 
              button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
          else 
            a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
    
    block content

You’ll notice a couple of things about this file. First, the classes are very specific and plentiful. This is typical of Tailwind CSS. Rather than defining how a specific element looks, Tailwind CSS strives to define how a particular style looks, then you can add and remove styles from elements to fit the desired look.

The other thing you’ll see is the Login/Logout button that will change based on the isAuthenticated property.

Next, your can update index.pug with the following.

extends layout

block content
  .py-12.bg-white
    .max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
      div.c(class='lg:text-center')
        p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
        p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
          | A small demo built with 
          a(href="https://expressjs.com/") Express on 
          a(href="https://nodejs.org/en/") Node.JS 
          | Secured with 
          a(href="https://developer.okta.com/signup") Okta.
          br          
          | This app uses 
          a(href="https://tailwindcss.com/") Tailwind CSS 
          | for its CSS framework and 
          a(href="https://www.prisma.io/") Prisma.JS 
          | for its ORM.  

There’s not much to talk about here. Just a splash page with some more Tailwind CSS and some links to the technologies used.

Finally, add a file for dashboard.pug with the following code.

extends layout

block content 
    .flex.flex-wrap
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-green-600
                            i.fa.fa-wallet.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0) 
                            span.text-green-500
                                i.fas.fa-caret-up
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-pink-600
                            i.fas.fa-users.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Calories Burned
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)  
                            span.text-pink-500
                                i.fas.fa-exchange-alt
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-yellow-600
                            i.fas.fa-user-plus.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Days Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
                            span.text-yellow-600
                                i.fas.fa-caret-up
    .w-full.p-6
        .bg-white.border-transparent.rounded-lg.shadow-xl
            .bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
                h2.font-bold.uppercase.text-gray-600 Workout Log
            .p-5
                table.w-full.p-5.text-gray-700
                    thead
                        tr
                            th.text-left.text-blue-900 Date
                            th.text-left.text-blue-900 Execise
                            th.text-left.text-blue-900 Amount
                            th.text-left.text-blue-900 Time
                            th.text-left.text-blue-900 Calories Burned
                    tbody
                        each log in user.workoutLogs.sort((a,b) => a.date - b.date)
                            tr
                                td=log.date.toLocaleDateString()
                                td=log.exercise
                                td=log.amount + ' ' + log.units 
                                td=log.minutes + ' Minutes'
                                td=log.calories                        

As you saw before, this page is protected. Therefore, it requires that you have a user to access this page. The application will pull the user details and workout logs and form them into a summary at the top of the page with a couple of tables of the logs based on the exercises the user has performed.

Create the CSS file using Tailwind CSS

The last part of this process is to create the actual CSS file that your application will use. In your public/stylesheets directory, you’ll see a file called style.css. Replace the code in that file with the code below.

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

This is largely the same. However, you are importing the directives for each of Tailwind’s layers.

Next, you can compile the classes you used in your pug files and the style sheet you just modified to create a new CSS file. Run the following command.

npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch

Now you can open the output.css file and see the complete Tailwind CSS code. The --watch parameter here is helpful for local development. This will rebuild the output.css file each time you change one of your layouts. You can disregard that parameter if you do not want to make any changes.

Test your application

You’re now ready to start your application. Run the following command.

npm run start

Once your application is started you should be able to navigate to http://localhost:3000/ and see your home page.

Home page

Use the login button at the top of the screen and navigate out to the Okta login page. Once you’ve logged in you will be redirected back to your website where you will be able to see your dashboard page.

Dashboard page

What you learned in this article

In this article, you learned how to use Prisma to define a schema for your database. You then learned how to create a migration from that schema and seed your database. You saw that Prisma could be used with the most popular database options, including SQL Server, MySql, MongoDB, and more.

You also learned how to build a CSS file with Tailwind CSS. We walked through a simple tailwind configuration and how the Tailwind CLI uses your code to build its CSS file.

Finally, you learned how to secure your express application using passport.


Original article sourced at: https://developer.okta.com

#prisma 

How to Create a Quick App using Prisma
Kevin  Taylor

Kevin Taylor

1666812900

Tiktok Clone: A Fullstack TikTok Clone with Nextjs, Prisma, Trpc

TikTok Clone

A fullstack TikTok clone with Nextjs, Prisma, trpc 

Live demo

Official website: https://toptop-clone.vercel.app/

Main technology used

  • The t3 stack: create.t3.gg
    • Nextjs
    • Prisma
    • trpc
    • Typescript
    • Tailwind
  • next-auth
  • react-hot-toast

Features

  • Auth (Google, Facebook)
  • Upload video with thumbnail
  • Infinite loading
  • Follow user
  • Following tab
  • Like a video
  • Comment on a video
  • Share video on Facebook, Twitter, Reddit,...
  • User profile
  • Search accounts and videos
  • SEO

Installation

See SELF-HOSTING.md

.env.example

NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000

DATABASE_URL=mysql://username:password@host/database-name

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
FACEBOOK_APP_ID=
FACEBOOK_APP_SECRET=

NEXT_PUBLIC_UPLOAD_URL=https://discloud-storage.herokuapp.com/
NEXT_PUBLIC_IMAGE_UPLOAD_URL=https://discord.com/api/webhooks/id

.eslintrc.json

{
  "plugins": ["simple-import-sort"],
  "extends": ["next/core-web-vitals"],
  "rules": {
    "no-unused-vars": "warn",
    "@next/next/no-img-element": "off",
    "simple-import-sort/imports": "warn",
    "simple-import-sort/exports": "warn"
  }
}

.gitignore

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/
.swc

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo

next-env.d.ts

/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

Download Details:

Author: napthedev
Source Code: https://github.com/napthedev/toptop-clone

#react #nextjs #prisma #tiktok 

Tiktok Clone: A Fullstack TikTok Clone with Nextjs, Prisma, Trpc
Jade Bird

Jade Bird

1663204687

Full Stack Application with Azure SQL, NodeJS & Prisma

Learn: Full Stack Application with Azure SQL & Prisma in this tutorial
Come along with us to create a scalable, resilient and robust Full Stack application using the main stacks, such as:

✅ Node.Js
✅ Prisma
✅ Azure SQL
✅ Azure Functions
✅ Azure Static Web Apps & GitHub Actions

📕 Course Structure 📕

✅ Module 01 - Prisma Fundamentals
✅ Module 02 - Azure SQL Fundamentals
✅ Module 03 - Azure Static Web Apps Fundamentals
✅ Module 04 - Dev Tools
✅ Module 05 - Application Development
✅ Module 06 - Next Steps

In this course, you'll learn about:

  1. Course Overview | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners
  2. Learning Objectives | A Full Stack Application with Azure SQL & Prisma for Beginners
  3. What is Prisma? | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners
  4. Why Should we use Prisma? | A Full Stack Application with Azure SQL & Prisma for Beginners
  5. Key Concepts about Prisma | A Full Stack Application with Azure SQL & Prisma for Beginners
  6. Important Resources & Recap | Full Stack Application with Azure SQL & Prisma for Beginners
  7. Module 02: Overview | Learn: Full Stack Application with Azure SQL & Prisma for Beginners
  8. What is Azure SQL? | Learn: Full Stack Application with Azure SQL & Prisma for Beginners
  9. Why should we use Azure SQL? | Full Stack Application with Azure SQL & Prisma for Beginners
  10. Azure SQL Support in Prisma | Full Stack Application with Azure SQL & Prisma for Beginners
  11. Important Resources & Recap | Full Stack Application with Azure SQL & Prisma for Beginners
  12. Module 03: Overview | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners
  13. What is Azure Static Web Apps? | Full Stack Application with Azure SQL & Prisma for Beginners
  14. Azure Static Web Apps Workflow | Full Stack Application with Azure SQL & Prisma for Beginners
  15. Why should we use ASWA? | Full Stack Application with Azure SQL & Prisma for Beginners
  16. Frameworks we can use with ASWA | Full Stack Application with Azure SQL &Prisma for Beginners
  17. Important Resources & Recap | Full Stack Application with Azure SQL & Prisma for Beginners
  18. Dev Tools Overview & Installation | Full Stack Application w/ Azure SQL &Prisma for Beginners
  19. Configuring .devContainer folder | Full Stack Application w/ Azure SQL & Prisma for Beginners
  20. Important Resources & Recap | Full Stack Application with Azure SQL & Prisma for Beginners
  21. Application Overview | A Full Stack Application with Azure SQL & Prisma for Beginners
  22. Forking the Front-End Application | Full Stack Application w/ Azure SQL &Prisma for Beginners
  23. Structuring the Back-End Project | Full Stack Application w/ Azure SQL & Prisma for Beginners
  24. Creating Database Server | Full Stack Application with Azure SQL & Prisma for Beginners
  25. Creating Database | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners
  26. Creating Shadow Database | Full Stack Application with Azure SQL & Prisma for Beginners
  27. Install packages | Learn: Full Stack Application with Azure SQL & Prisma for Beginners
  28. Modeling Database with Prisma | Full Stack Application with Azure SQL & Prisma for Beginners
  29. Using Prisma Migrations | Full Stack Application with Azure SQL & Prisma for Beginners
  30. Using Prisma Studio | Learn: Full Stack Application with Azure SQL & Prisma for Beginners
  31. Developing 'CreateEmployee' | Full Stack Application with Azure SQL & Prisma for Beginners
  32. Developing 'GetEmployees' | A Full Stack Application with Azure SQL & Prisma for Beginners
  33. Developing 'GetEmployee' | A Full Stack Application with Azure SQL & Prisma for Beginners
  34. Developing 'UpdateEmployee' | Full Stack Application with Azure SQL & Prisma for Beginners
  35. Developing 'DeleteEmployee' | Full Stack Application with Azure SQL & Prisma for Beginners
  36. Deploying the App with ASWA | Full Stack Application with Azure SQL & Prisma for Beginners
  37. Next Steps | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

Course Overview [1 of 37] | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

Learning Objectives [2 of 37] | A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will see the learning objectives of the Prisma Fundamentals module!

What is Prisma? [3 of 37] | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will learn what is Prisma.

Why Should we use Prisma? [4 of 37] | A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will learn why we should use Prisma, which has been considered a powerful ORM for JavaScript Developers.

Key Concepts about Prisma [5 of 37] | A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will learn the main pillars around the Prisma!

Important Resources & Recap [6 of 37] | Full Stack Application with Azure SQL & Prisma for Beginners

Recap on what we learned and how you can dig deeper into Prisma.

Module 02: Overview [7 of 37] | Learn: Full Stack Application with Azure SQL & Prisma for Beginners

In this video you will see what will learn during the Module 02 about Azure SQL.

What is Azure SQL? [8 of 37] | Learn: Full Stack Application with Azure SQL & Prisma for Beginners

In this video you will understand what is Azure SQL and the different options like: Single Database and Elastic Pool.

Why should we use Azure SQL? [9 of 37]| Full Stack Application with Azure SQL & Prisma for Beginners

In this video you will understand why is so important to use Azure SQL in your projects!

Azure SQL Support in Prisma [10 of 37]| Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will show you that Azure SQL has a perfect fit with Prisma!

Important Resources & Recap [11 of 37]| Full Stack Application with Azure SQL & Prisma for Beginners

Recap on what we learned and how you can dig deeper into Azure SQL.

Module 03: Overview [12 of 37] Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

In this video you will see what will learn during the Module 03 about Azure Static Web Apps.

What is Azure Static Web Apps? [13of37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will learn what is Azure Static Web Apps.

Azure Static Web Apps Workflow [14of37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will understand under the hood how the Azure Static Web Apps workflow works.

Why should we use ASWA? [15 of 37] | A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will understand the benefits to use Azure Static Web Apps!

Frameworks we can use with ASWA [16of37] Full Stack Application with Azure SQL &Prisma for Beginners

In this video we will see that can use Azure Static Web Apps with different libs and the most popular frameworks!

Important Resources & Recap [17 of 37]| Full Stack Application with Azure SQL & Prisma for Beginners

Recap on what we learned and how you can dig deeper into Azure Static Web Apps.

Dev Tools Overview & Installation [18of37] Full Stack Application w/ Azure SQL &Prisma for Beginners

In this video we are going to install all the necessary dev tools that we will go into need to develop our application in the BackEnd side.

Configuring .devContainer folder [19of37] Full Stack Application w/ Azure SQL & Prisma for Beginners

In this video we will understand the importance around to create a .devContainer folder in any project and a little overview about it!

Important Resources & Recap [20 of 37] Full Stack Application with Azure SQL & Prisma for Beginners

Recap what we saw during the module 04 and preparing for what is coming!

Application Overview [21 of 37] | A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we will take a first look around the BackEnd application that we are going to build!

Forking the Front-End Application [22of37] Full Stack Application w/ Azure SQL &Prisma for Beginners

In this video we are going to forking/cloning the Front-End starter project!

Structuring the Back-End Project [23of37] Full Stack Application w/ Azure SQL & Prisma for Beginners

In this video we are going to structuring the BackEnd project using Azure Functions.

Creating Database Server [24 of 37] | Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to create our Database Server using the Azure Portal.

Creating Database [25 of 37] | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

Now we already created our Database server, it's time to create our Employee Database using Azure Portal. 

Creating Shadow Database [26 of 37] | Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to create shadow database using Azure Portal.

Install packages [27 of 37] | Learn: Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to install all the necessary packages for our project!

Modeling Database with Prisma [28of37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to learn how to initialize and modeling our database using Prisma.

Using Prisma Migrations [29 of 37] | Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to understand how Prisma migrations works!

Using Prisma Studio [30 of 37] | Learn: Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to take a look how Prisma Studio works and use it to manipulate our database.

Developing 'CreateEmployee' [31 of 37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we're going to create our first function: Create Employee using Prisma & Azure Functions

Developing 'GetEmployees' [32 of 37] A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to develop the functionality to list all the employees using Azure Functions.

Developing 'GetEmployee' [33 of 37] A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to develop the functionality to return an employee by Id using Azure Functions.

Developing 'UpdateEmployee' [34 of 37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to develop the functionality to update an employee by Id using Azure Functions.

Developing 'DeleteEmployee' [35 of 37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to develop the functionality to delete an employee by Id using Azure Functions

Deploying the App with ASWA [36 of 37] Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to deploy our application using Azure Static Web Apps with GitHub Actions Integration!

Next Steps [37 of 37] | Learn: A Full Stack Application with Azure SQL & Prisma for Beginners

In this video we are going to see the next steps that you should see after this course!

⭐️ GitHub repository: https://github.com/glaucia86/azure-sql-prisma-vue 

💻 Important Resources & Links Importantes & Links 💻

✅ Build serverless, full stack applications in Azure - Free Course: https://aka.ms/fullstack-mslearn-course 
✅ Azure SQL Documentation: https://aka.ms/azuresql-official-documentation   
✅ Microsoft Learn [Free Course] - Azure Static Web Apps: https://aka.ms/mslearn-aswa-course 
✅ Microsoft Learn [Free Course] - Build Serverless APIs with Azure Functions: https://aka.ms/azurefunctions-msleanr-course  
✅  Prisma Official Website: https://www.prisma.io/ 
✅  Official Prisma Documentation: https://www.prisma.io/docs/ 
✅ Articles & Updates about Prisma: https://www.prisma.io/blog/   
✅ Prisma Community: https://www.prisma.io/community 
 

#azure #azuresql #azurefunctions #prisma #nodejs #javascript #crudapplication #aswa #vuejs, #webdevelopment #tutorial #programming #coding #fullstackapplication

Full Stack Application with Azure SQL, NodeJS & Prisma
React Dev

React Dev

1661401032

Build a Nested Comment System In React ,TypeScript, tRPC and Prisma

Build a Nested Comment System In React - TypeScript, tRPC, Prisma

0:00 Intro
1:00 Starting point
1:32 Codebase
3:08 Comment model
6:21 API endpoints
15:19 Comment form
22:01 Display comments
30:53 Reply form
34:10 Nest comments funciton
42:00 Display children
48:05 What next?

Repository: https://github.com/TomDoesTech/nested-comments 

Subscribe: https://www.youtube.com/c/TomDoesTech/featured 

#react #typescript #trpc #prisma 

Build a Nested Comment System In React ,TypeScript, tRPC and Prisma
Felix Kling

Felix Kling

1661305923

How to Create a Serverless API with Next.js, Vercel and Prisma

BUILD AND SELL an API with Next.js, Vercel, Prisma and RapidAPI. Learn how to create a Serverless API with Next.js, Vercel and Prisma. Register this API in RapidAPI so that you can charge for access to your API.

Timestamps

00:00 - Intro
00:15 - What We’ll Build
00:55 - Sponsor Shoutout (RapidAPI)
01:05 - Resources
01:50 - Database Setup in PlanetScale
02:30 - Create a New Next.js Project with TypeScript
03:45 - Setup Prisma with Next.js
05:00 - Configuring Prisma with PlanetScale
06:20 - Database Schema
07:00 - Seed the Database
10:30 - Scaffolding API Next.js Endpoints
11:47 - Testing API Endpoints with the RapidAPI Client VS COde Extension
12:52 - Prisma Client Setup
14:16 - Integrating Prisma Client in Next.js API Endpoint
15:15 - Add Query Parameters and Validation to API Endpoint
18:28 - Adding Config Parameters to Prisma Query
20:25 - Dynamic API Route in Next.js
23:49 - Deploy to Vercel
23:50 - Create New Github Repository
24:44 - Deploy and Host in Vercel
25:53 - Multiple Builds in Vercel
29:10 - Publishing API to RapidAPI
30:22 - Define Endpoints in RapidAPI
32:05 - Monetizing Your API in RapidAPI
34:20 - Wrap Up

RapidAPI Studio - https://rapidapi.com/studio

Rapid API Client VS Code Extension - https://marketplace.visualstudio.com/items?itemName=RapidAPI.vscode-rapidapi-client 

Resources
Github Repository - https://github.com/jamesqquick/nextjs-throne-of-glass-api 

#serverless #api #nextjs #vercel #prisma #rapidapi

How to Create a Serverless API with Next.js, Vercel and Prisma
Jacob Banks

Jacob Banks

1659933162

How to Create Nested Comment System with React and Prisma

Learn how to build a full stack nested comments system - This tutorial shows how to create nested comment system with React and Prisma. Learn how to setup and connect a backend, database, and React frontend.

If you are wanting to build a complex full stack project then this is the video for you. In this video we will be covering how to setup and connect a backend, database, and React frontend. This is a large and complex project, but is amazing for your resume.


⏱️ Timestamps:

00:00:00 - Introduction
00:01:55 - Prisma database setup
00:13:26 - Server setup
00:21:01 - Connecting the client to the server
00:30:57 - React Router setup
00:33:19 - useAsync custom hook
00:41:58 - Get post server route
00:44:23 - Post context setup
00:50:44 - Render comments
01:09:30 - Comment form component
01:15:45 - Create comment client logic
01:19:15 - Create comment server logic
01:21:21 - User authentication
01:26:00 - Saving comments on the client
01:30:15 - Comment reply logic
01:34:35 - Update comment server logic
01:38:00 - Update comment client logic
01:42:53 - Delete comment server logic
01:43:54 - Delete comment client logic
01:46:23 - User permission UI
01:48:38 - Like comments server logic
01:57:55 - Like comments client logic

GitHub Code: https://github.com/WebDevSimplified/nested-comments 

Subscribe: https://www.youtube.com/c/WebDevSimplified/featured 


#react #prisma

How to Create Nested Comment System with React and Prisma
Thierry  Perret

Thierry Perret

1659023820

Comment Créer Une Application Express à L'aide De Prisma Et Tailwind

Prisma est un outil ORM (mappage objet-relationnel) pour Node.js utilisant TypeScript. Le logiciel s'intègre à la plupart des bases de données les plus populaires aujourd'hui, notamment MySQL, SQL Server, SQLite et MongoDB, et met l'accent sur un schéma lisible par l'homme avec un client de base de données de type sécurisé. Prisma inclut également d'autres fonctionnalités telles que les migrations, les données de départ et un navigateur de base de données virtuelle.

Dans ce projet, vous utiliserez Prisma pour connecter votre application Express à un serveur de base de données. Vous allez créer un schéma pour modéliser une application de suivi d'entraînement. Ensuite, vous créerez des données de départ et utiliserez Prisma pour exécuter des migrations et amorcer votre base de données. Enfin, vous créerez l'application Web à l'aide de Pug et Tailwind CSS pour créer l'interface de l'application.

Conditions préalables

Ce tutoriel utilise les technologies suivantes mais ne nécessite aucune expérience préalable :

  • Un IDE pour JavaScript. J'utiliserai Visual Studio Code mais vous pouvez utiliser Webstorm ou tout autre IDE que vous préférez.
  • Node.js
  • Une base de données, telle que PostgreSQL, MySQL, SQLite, SQL Server ou MongoDB. Dans ce tutoriel, nous utiliserons SQLite.
  • CLI d'Okta

Si vous souhaitez ignorer le didacticiel et consulter le projet entièrement construit, vous pouvez le consulter sur GitHub .

Créez votre serveur d'autorisation OAuth2

Créez un nouveau répertoire pour votre application. Utilisez la cdcommande pour accéder à ce dossier.

Avant de commencer, vous aurez besoin d'un compte développeur Okta gratuit. Installez l' interface de ligne de commande Okta et exécutez- okta registerla pour vous inscrire à un nouveau compte. Si vous avez déjà un compte, exécutez okta login. Ensuite, courez okta apps create. Sélectionnez le nom de l'application par défaut ou modifiez-le comme bon vous semble. Choisissez Web et appuyez sur Entrée .

Sélectionnez Autre . Ensuite, remplacez l'URI de redirection par http://localhost:3000/authorization-code/callbacket utilisez http://localhost:3000/-le pour l'URI de redirection de déconnexion.

Que fait la CLI d'Okta ?

L'Okta CLI créera une application Web OIDC dans votre Okta Org. Il ajoutera les URI de redirection que vous avez spécifiés et accordera l'accès au groupe Tout le monde. Vous verrez une sortie comme celle-ci lorsqu'elle sera terminée :

Okta application configuration has been written to: /path/to/app/.okta.env

Exécutez cat .okta.env(ou type .okta.envsous Windows) pour voir l'émetteur et les informations d'identification de votre application.

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

Votre domaine Okta est la première partie de votre émetteur, avant /oauth2/default.

REMARQUE : Vous pouvez également utiliser la console d'administration Okta pour créer votre application. Voir Créer une application Web pour plus d'informations.

Créez votre application Express

Ensuite, vous utiliserez l' express-generatoroutil générateur d'applications pour échafauder rapidement votre application. Exécutez la commande suivante.

npx express-generator@4.16 --view=pug

Vous pouvez maintenant installer vos packages.

npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
  • @prisma/clientest utilisé pour accéder à la base de données à partir de votre code serveur.
  • dotenvlit les paramètres de configuration à partir de .envfichiers tels que celui produit par la CLI Okta.
  • passportest un middleware pour Node.js suffisamment flexible pour gérer la plupart des scénarios d'authentification, y compris Okta. passport-openidconnectest un module pour passeport qui vous permet de vous authentifier avec OpenID Connect.
  • express-sessionest nécessaire pour le passeport. Les applications Express utilisent ce package pour la prise en charge des sessions. Votre application doit initialiser le support de session pour utiliser le passeport.
  • Tailwind CSSest le framework CSS que vous utiliserez. Si vous n'avez jamais utilisé Tailwind auparavant, vous vous demandez peut-être pourquoi il s'agit d'une dépendance au développement. En effet, Tailwind créera dynamiquement vos fichiers CSS à partir de vos vues et de votre configuration. Plus sur cela plus tard.
  • prismaest l'ORM que vous utilisez. Il s'agit d'une dépendance de développement car la bibliothèque Prisma gère les migrations, les données de départ, etc., tandis que le @prisma/clientpackage est utilisé dans votre application au moment de l'exécution.

À ce stade, vous allez initialiser Tailwind CSS et Prisma. Vous les configurerez pour une utilisation ultérieure.

Commencez avec Prisma et exécutez la commande suivante.

npx prisma init

Cette commande ajoutera un nouveau dossier nommé prismaà votre application. Il ajoute également un fichier appelé .envà votre projet avec une configuration par défaut. Remplacez le contenu de ce fichier par le code ci-dessous.

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}

Dans cet exemple, j'ai utilisé SQLite parce que c'est simple et compact. Mais l'un des avantages de Prisma et de la plupart des ORM est qu'ils peuvent prendre en charge de nombreuses bases de données. Le code passe-partout inclut des commentaires sur la configuration de votre application pour d'autres serveurs de base de données. N'hésitez pas à utiliser ce avec quoi vous vous sentez le plus à l'aise et utilisez la chaîne de connexion appropriée.

Vos données de départ utiliseront le SEED_USER_NAMEparamètre. Vous voulez vous assurer qu'il s'agit du même nom d'utilisateur Okta avec lequel vous vous connectez. Cela permettra à l'application d'associer votre utilisateur connecté aux données que vous ajouterez à votre base de données.

Ensuite, mettez à jour votre package.jsonfichier avec le code suivant.

{
  "name": "workout-app",
  "version": "0.0.0",
  "private": true,
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@prisma/client": "^3.13.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.0",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "passport": "^0.5.2",
    "passport-openidconnect": "^0.1.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "prisma": "^3.13.0",
    "tailwindcss": "^3.0.24"
  }
}

Cela ajoutera la prisma seedcommande dont vous avez besoin plus tard.

Vous pouvez maintenant initialiser Tailwind CSS.

npx tailwindcss init

Vous avez maintenant ajouté un nouveau fichier appelé tailwind.config.jsà la racine de votre application. Remplacez le contenu de ce fichier par le code ci-dessous.

module.exports = {
  content: ["./views/**/*.pug"],
  theme: {
    extend: {},
  },
  plugins: [],
}

Dans cette étape, vous indiquez à Tailwind CSS où trouver les classes que vous avez utilisées dans votre application. Vous souhaitez que Tailwind recherche dans les .pugfichiers de votre viewsrépertoire. themeTailwind CSS est hautement extensible, comme le montrent les paramètres et les objets de pluginsconfiguration. Une plongée approfondie dans ce sujet n'entre pas dans le cadre de cet article, mais je vous encourage à consulter le site de Tailwind CSS pour plus d'informations.

Utilisez Prisma pour créer votre base de données

La tâche suivante consiste à créer la base de données pour votre application. Les étapes impliqueront l'écriture du schéma, l'écriture de données de départ, la création de la migration et l'application de la migration, qui amorcera également vos données.

La prisma inittâche ci-dessus devrait avoir ajouté un fichier appelé schema.prismadans votre prismarépertoire. Remplacez le code par le code ci-dessous.

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

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

model WorkoutLog {
  id        Int      @id @default(autoincrement())
  userId    Int
  exercise  String
  amount    Decimal
  units     String
  date      DateTime
  minutes   Int
  calories  Int
  user      User     @relation(fields: [userId], references: [id])
}

model User {
  id          Int          @id @default(autoincrement())
  username    String       @unique
  workoutLogs WorkoutLog[]
}

Assurez-vous de remplacer {yourDatabaseProvider}par le fournisseur que vous utilisez . Comme vous pouvez le voir, ce fichier définit les objets Useret WorkoutLogque vous stockerez dans la base de données. Le WorkoutLogcontient des informations de base sur l'entraînement de l'utilisateur un jour spécifique, puis associe cet enregistrement à l' Userobjet. Vous pouvez également définir des clés, des contraintes et des index à partir de ce fichier. Notez que la userpropriété sur WorkoutLoga une relation définie par le userIdsur le idde la Usertable.

Ajoutez un fichier au prismarépertoire nommé seed.jset ajoutez-y le code suivant.

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

require("dotenv").config();

async function main() {
  const user = await prisma.user.upsert({
    where: { username: process.env.SEED_USER_NAME },
    update: {},
    create: {
      username: process.env.SEED_USER_NAME,
      workoutLogs: {
        create: [
          {
            exercise: "Running",
            amount: 1,
            units: "Miles",
            date: new Date(2022, 1, 1),
            minutes: 8,
            calories: 100
          },
          {
            exercise: "Running",
            amount: 1.2,
            units: "Miles",
            date: new Date(2022, 1, 3),
            minutes: 10,
            calories: 120
          },
          {
            exercise: "Running",
            amount: 1.5,
            units: "Miles",
            date: new Date(2022, 1, 5),
            minutes: 12,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 1),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Heavy Bag",
            amount: 6,
            units: "Rounds",
            date: new Date(2022, 1, 3),
            minutes: 22,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 5),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 10,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 10,
            calories: 100
          },
        ],
      },
    },
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Prisma sait utiliser le seed.jsfichier à partir de la commande que vous avez ajoutée à package.json. Ce fichier insérera les données lorsque vous exécuterez cette commande.

Vous pouvez maintenant ajouter et appliquer la migration à l'aide de la CLI Prisma. À partir de la racine du répertoire de votre application, exécutez la commande suivante.

npx prisma migrate dev --name init

L'interface de ligne de commande propose plusieurs façons d'ajouter et d'appliquer des migrations. Je vous suggère de comprendre la meilleure façon de gérer les migrations pour votre environnement. La méthode ci-dessus est le moyen le plus simple et le plus rapide de préparer votre base de données. Dans le cadre du processus de migration, cette commande recherchera la seedcommande de votre package.jsonet l'exécutera également. Une fois terminée, votre base de données devrait être prête avec une base de données pleine de données de départ avec lesquelles travailler.

Ajouter l'authentification OIDC

Maintenant que votre base de données est prête, vous pouvez vous concentrer sur le cœur de votre application.

Tout d'abord, ajoutez un nouveau fichier à la racine de votre application appelée ensureLoggedIn.jset ajoutez-y le code suivant.

function ensureLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login')
}

module.exports = ensureLoggedIn;

Ce petit middleware s'assurera que l'utilisateur est authentifié. S'ils ne le sont pas, vous les redirigerez vers la loginroute, que vous configurerez pour utiliser Okta. Sinon, vous permettrez à l'utilisateur d'accéder à l'écran suivant.

Ensuite, vous pouvez mettre à jour vos routes dans l' routesannuaire. Tout d'abord, supprimez- users.jsle car vous ne l'utiliserez pas. Remplacez le code dans index.jspar ce qui suit.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});

module.exports = router;

Ici, vous passerez une propriété pour isAuthenticatedque votre page de mise en page puisse afficher correctement un bouton de connexion ou de déconnexion.

Ensuite, ajoutez un fichier dans le routesrépertoire pour dashboard.jsavec le code suivant.

var express = require("express");
var router = express.Router();

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const ensureLoggedIn = require("../ensureLoggedIn");

/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
  const username = req.user.username;

  const dbUser = await prisma.user.findUnique({
    where: {
      username: username,
    },
    include: {
      workoutLogs: true,
    },
  });

  res.render("dashboard", {
    title: "The Workout Tracker - Dashboard",
    isAuthenticated: req.isAuthenticated(),
    user: dbUser
  });
});

module.exports = router;

Il y a beaucoup de magie dans ce parcours. Tout d'abord, vous utilisez le ensureLoggedInmiddleware pour protéger la route. Ensuite, vous pouvez extraire le nom d'utilisateur de la demande et rechercher l'utilisateur dans la base de données à l'aide du client Prisma. Étant donné que l'utilisateur a une propriété pour ses journaux d'entraînement, vous pouvez passer l'utilisateur en tant que modèle et analyser les journaux sur la vue carlin que vous allez créer. Cela donnera également un accès en vue au nom de l'utilisateur. Bien sûr, vous pouvez interroger workoutLogsdirectement la table et inclure des fonctionnalités telles que la pagination et la recherche si cette approche correspond mieux à votre flux de travail.

Maintenant, remplacez le code app.jspar ce qui suit.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');

const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

var app = express();

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient();

require('dotenv').config({path:'./.okta.env'});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(session({
  secret: 'CanYouLookTheOtherWay',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());


const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;

// set up passport
passport.use('oidc', new Strategy({
  issuer: OKTA_OAUTH2_ISSUER,
  authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
  tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
  userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
  clientID: OKTA_OAUTH2_CLIENT_ID,
  clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/authorization-code/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/login', passport.authenticate('oidc'));

app.use('/authorization-code/callback',
  passport.authenticate('oidc', { failureRedirect: '/error' }),
  (req, res, next) => {
    res.redirect('/dashboard');
  }
);

app.post('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Vous avez mis à jour vos itinéraires pour utiliser correctement les dashboarditinéraires ainsi que tous les itinéraires de connexion ou de déconnexion qu'Okta utilisera. Vous configurez également un passeport pour utiliser Okta en utilisant les valeurs produites par la CLI d'Okta dans le .okta.envfichier.

Ajouter des vues à votre application Express

Par défaut, express-generatorajoutera quelques vues pour vous. Vous devrez les modifier et en ajouter un nouveau de votre cru. Commencez par ouvrir le layout.pugfichier dans le viewsrépertoire et remplacez le code par ce qui suit.

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/output.css')
  body
    nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
      span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
      .w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
        .text-sm(class='lg:flex-grow')
        div
          if(isAuthenticated)
            form(action="logout" method="POST") 
              button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
          else 
            a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
    
    block content

Vous remarquerez plusieurs choses à propos de ce fichier. Tout d'abord, les cours sont très spécifiques et nombreux. Ceci est typique du CSS Tailwind. Plutôt que de définir l'apparence d'un élément spécifique, Tailwind CSS s'efforce de définir l'apparence d'un style particulier, puis vous pouvez ajouter et supprimer des styles d'éléments pour s'adapter à l'apparence souhaitée.

L'autre chose que vous verrez est le bouton Connexion/Déconnexion qui changera en fonction de la isAuthenticatedpropriété.

Ensuite, vous pouvez mettre à jour index.pugavec ce qui suit.

extends layout

block content
  .py-12.bg-white
    .max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
      div.c(class='lg:text-center')
        p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
        p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
          | A small demo built with 
          a(href="https://expressjs.com/") Express on 
          a(href="https://nodejs.org/en/") Node.JS 
          | Secured with 
          a(href="https://developer.okta.com/signup") Okta.
          br          
          | This app uses 
          a(href="https://tailwindcss.com/") Tailwind CSS 
          | for its CSS framework and 
          a(href="https://www.prisma.io/") Prisma.JS 
          | for its ORM.  

Il n'y a pas grand chose à dire ici. Juste une page d'accueil avec un peu plus de CSS Tailwind et quelques liens vers les technologies utilisées.

Enfin, ajoutez un fichier pour dashboard.pugavec le code suivant.

extends layout

block content 
    .flex.flex-wrap
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-green-600
                            i.fa.fa-wallet.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0) 
                            span.text-green-500
                                i.fas.fa-caret-up
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-pink-600
                            i.fas.fa-users.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Calories Burned
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)  
                            span.text-pink-500
                                i.fas.fa-exchange-alt
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-yellow-600
                            i.fas.fa-user-plus.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Days Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
                            span.text-yellow-600
                                i.fas.fa-caret-up
    .w-full.p-6
        .bg-white.border-transparent.rounded-lg.shadow-xl
            .bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
                h2.font-bold.uppercase.text-gray-600 Workout Log
            .p-5
                table.w-full.p-5.text-gray-700
                    thead
                        tr
                            th.text-left.text-blue-900 Date
                            th.text-left.text-blue-900 Execise
                            th.text-left.text-blue-900 Amount
                            th.text-left.text-blue-900 Time
                            th.text-left.text-blue-900 Calories Burned
                    tbody
                        each log in user.workoutLogs.sort((a,b) => a.date - b.date)
                            tr
                                td=log.date.toLocaleDateString()
                                td=log.exercise
                                td=log.amount + ' ' + log.units 
                                td=log.minutes + ' Minutes'
                                td=log.calories                        

Comme vous l'avez vu précédemment, cette page est protégée. Par conséquent, il faut que vous ayez un utilisateur pour accéder à cette page. L'application extrait les détails de l'utilisateur et les journaux d'entraînement et les forme dans un résumé en haut de la page avec quelques tableaux des journaux basés sur les exercices que l'utilisateur a effectués.

Créer le fichier CSS à l'aide de Tailwind CSS

La dernière partie de ce processus consiste à créer le fichier CSS réel que votre application utilisera. Dans votre public/stylesheetsrépertoire, vous verrez un fichier appelé style.css. Remplacez le code de ce fichier par le code ci-dessous.

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

C'est en grande partie la même chose. Cependant, vous importez les directives pour chacune des couches de Tailwind.

Ensuite, vous pouvez compiler les classes que vous avez utilisées dans vos fichiers pug et la feuille de style que vous venez de modifier pour créer un nouveau fichier CSS. Exécutez la commande suivante.

npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch

Vous pouvez maintenant ouvrir le output.cssfichier et voir le code CSS Tailwind complet. Le --watchparamètre ici est utile pour le développement local. Cela reconstruira le output.cssfichier chaque fois que vous modifierez l'une de vos mises en page. Vous pouvez ignorer ce paramètre si vous ne souhaitez apporter aucune modification.

Testez votre application

Vous êtes maintenant prêt à démarrer votre application. Exécutez la commande suivante.

npm run start

Une fois votre application démarrée, vous devriez pouvoir accéder à http://localhost:3000/ et voir votre page d'accueil.

Page d'accueil

Utilisez le bouton de connexion en haut de l'écran et accédez à la page de connexion d'Okta. Une fois connecté, vous serez redirigé vers votre site Web où vous pourrez voir votre page de tableau de bord.

Tableau de bord

Ce que vous avez appris dans cet article

Dans cet article, vous avez appris à utiliser Prisma pour définir un schéma pour votre base de données. Vous avez ensuite appris à créer une migration à partir de ce schéma et à amorcer votre base de données. Vous avez vu que Prisma pouvait être utilisé avec les options de base de données les plus populaires, notamment SQL Server, MySql, MongoDB, etc.

Vous avez également appris à créer un fichier CSS avec Tailwind CSS. Nous avons parcouru une configuration simple de Tailwind et comment la CLI Tailwind utilise votre code pour créer son fichier CSS.

Enfin, vous avez appris comment sécuriser votre demande express à l'aide d'un passeport.

Lien : https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind

#tailwindcss #express  #prisma 

Comment Créer Une Application Express à L'aide De Prisma Et Tailwind

Как создать экспресс-приложение с помощью Prisma и TailwindCSS

Prisma — это инструмент ORM (объектно-реляционное сопоставление) для Node.js с использованием TypeScript. Программное обеспечение интегрируется со многими из самых популярных на сегодняшний день баз данных, включая MySQL, SQL Server, SQLite и MongoDB, и делает упор на удобочитаемую схему с безопасным типом клиента базы данных. Prisma также включает в себя другие функции, такие как миграция, начальные данные и виртуальный браузер базы данных.

В этом проекте вы будете использовать Prisma для подключения вашего приложения Express к серверу базы данных. Вы создадите схему для моделирования приложения для отслеживания тренировок. Затем вы создадите некоторые исходные данные и используете Prisma для запуска миграции и заполнения базы данных. Наконец, вы создадите веб-приложение, используя Pug и Tailwind CSS для построения внешнего интерфейса приложения.

Предпосылки

В этом руководстве используются следующие технологии, но не требуется никакого предварительного опыта:

  • IDE для JavaScript. Я буду использовать Visual Studio Code , но вы можете использовать Webstorm или любую другую IDE, которую вы предпочитаете.
  • Node.js
  • База данных, такая как PostgreSQL, MySQL, SQLite, SQL Server или MongoDB. В этом уроке мы будем использовать SQLite.
  • Окта CLI

Если вы хотите пропустить руководство и ознакомиться с полностью собранным проектом, вы можете посмотреть его на GitHub .

Создайте свой сервер авторизации OAuth2

Создайте новый каталог для вашего приложения. Используйте cdкоманду для перехода к этой папке.

Прежде чем начать, вам понадобится бесплатная учетная запись разработчика Okta. Установите Okta CLI и запустите okta registerрегистрацию новой учетной записи. Если у вас уже есть учетная запись, запустите okta login. Затем бегите okta apps create. Выберите имя приложения по умолчанию или измените его по своему усмотрению. Выберите Интернет и нажмите Enter .

Выберите Другое . Затем измените URI перенаправления на http://localhost:3000/authorization-code/callbackи используйте http://localhost:3000/для URI перенаправления выхода из системы.

Что делает интерфейс командной строки Okta?

Интерфейс командной строки Okta создаст веб-приложение OIDC в ​​вашей организации Okta. Он добавит указанные вами URI перенаправления и предоставит доступ группе «Все». Вы увидите вывод, подобный следующему, когда он будет завершен:

Okta application configuration has been written to: /path/to/app/.okta.env

Запустите cat .okta.env(или type .okta.envв Windows), чтобы увидеть издателя и учетные данные для вашего приложения.

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

Ваш домен Okta — это первая часть вашего эмитента, до /oauth2/default.

ПРИМЕЧАНИЕ . Вы также можете использовать Okta Admin Console для создания своего приложения. Дополнительные сведения см. в разделе Создание веб-приложения .

Создайте свое экспресс-приложение

Далее вы будете использовать express-generatorинструмент генератора приложений, чтобы быстро сформировать шаблон вашего приложения. Выполните следующую команду.

npx express-generator@4.16 --view=pug

Теперь вы можете установить свои пакеты.

npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
  • @prisma/clientиспользуется для доступа к базе данных из кода вашего сервера.
  • dotenvсчитывает параметры конфигурации из .envфайлов, подобных тому, который создается интерфейсом командной строки Okta.
  • passport— это промежуточное ПО для Node.js, достаточно гибкое для обработки большинства сценариев аутентификации, включая Okta. passport-openidconnectэто модуль для паспорта, который позволяет вам аутентифицироваться с помощью OpenID Connect.
  • express-sessionтребуется для загранпаспорта. Экспресс-приложения используют этот пакет для поддержки сеансов. Ваше приложение должно инициализировать поддержку сеанса для использования паспорта.
  • Tailwind CSSэто CSS-фреймворк, который вы будете использовать. Если вы никогда раньше не использовали Tailwind, вам может быть интересно, почему это зависит от разработки. Это связано с тем, что Tailwind будет динамически создавать файлы CSS из ваших представлений и конфигурации. Подробнее об этом позже.
  • prismaэто ORM, который вы используете. Это зависимость от разработчиков, потому что библиотека Prisma обрабатывает миграции, начальные данные и т. д., в то время как @prisma/clientпакет используется в вашем приложении во время выполнения.

На этом этапе вы инициализируете Tailwind CSS и Prisma. Вы настроите их для использования позже.

Начните с Prisma и выполните следующую команду.

npx prisma init

Эта команда добавит новую папку с именем prismaв ваше приложение. Он также добавляет .envв ваш проект файл с некоторой конфигурацией по умолчанию. Замените содержимое этого файла приведенным ниже кодом.

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}

В этом примере я использовал SQLite, потому что он простой и компактный. Но одна из замечательных особенностей Prisma и большинства ORM заключается в том, что они могут поддерживать множество баз данных. Стандартный код включает комментарии о настройке вашего приложения для других серверов баз данных. Не стесняйтесь использовать то, что вам удобнее, и используйте соответствующую строку подключения.

Ваши исходные данные будут использовать эту SEED_USER_NAMEнастройку. Вы хотите убедиться, что оно совпадает с именем пользователя Okta, с которым вы входите в систему. Это позволит приложению связать вошедшего в систему пользователя с данными, которые вы введете в свою базу данных.

Затем обновите package.jsonфайл с помощью следующего кода.

{
  "name": "workout-app",
  "version": "0.0.0",
  "private": true,
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@prisma/client": "^3.13.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.0",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "passport": "^0.5.2",
    "passport-openidconnect": "^0.1.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "prisma": "^3.13.0",
    "tailwindcss": "^3.0.24"
  }
}

Это добавит prisma seedкоманду, которая вам понадобится позже.

Теперь вы можете инициализировать Tailwind CSS.

npx tailwindcss init

Теперь вы добавили новый файл с именем tailwind.config.jsв корень вашего приложения. Замените содержимое этого файла приведенным ниже кодом.

module.exports = {
  content: ["./views/**/*.pug"],
  theme: {
    extend: {},
  },
  plugins: [],
}

На этом шаге вы сообщаете Tailwind CSS, где найти классы, которые вы использовали в своем приложении. Вы хотите, чтобы Tailwind искал .pugфайлы в вашем viewsкаталоге. Tailwind CSS обладает широкими возможностями расширения, что видно по объектам конфигурации themeи pluginsнастройкам. Подробное рассмотрение этого вопроса выходит за рамки данной статьи, но я рекомендую вам заглянуть на сайт Tailwind CSS для получения дополнительной информации.

Используйте Prisma для создания базы данных

Следующей задачей является создание базы данных для вашего приложения. Шаги будут включать в себя написание схемы, запись некоторых начальных данных, создание миграции и применение миграции, которая также заполнит ваши данные.

Задача prisma initсверху должна была добавить файл с именем schema.prismaв ваш prismaкаталог. Замените код там кодом ниже.

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

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

model WorkoutLog {
  id        Int      @id @default(autoincrement())
  userId    Int
  exercise  String
  amount    Decimal
  units     String
  date      DateTime
  minutes   Int
  calories  Int
  user      User     @relation(fields: [userId], references: [id])
}

model User {
  id          Int          @id @default(autoincrement())
  username    String       @unique
  workoutLogs WorkoutLog[]
}

Убедитесь, что вы заменили {yourDatabaseProvider}провайдера , которого используете . Как видите, этот файл определяет объекты Userи WorkoutLog, которые вы будете хранить в базе данных. Он WorkoutLogсодержит основную информацию о тренировке пользователя в определенный день, а затем связывает эту запись с Userобъектом. Вы также можете определить ключи, ограничения и индексы из этого файла. Обратите внимание, что userсвойство WorkoutLogимеет отношение, определенное userIdк idтаблице User.

Добавьте файл в указанный prismaкаталог seed.jsи добавьте в него следующий код.

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

require("dotenv").config();

async function main() {
  const user = await prisma.user.upsert({
    where: { username: process.env.SEED_USER_NAME },
    update: {},
    create: {
      username: process.env.SEED_USER_NAME,
      workoutLogs: {
        create: [
          {
            exercise: "Running",
            amount: 1,
            units: "Miles",
            date: new Date(2022, 1, 1),
            minutes: 8,
            calories: 100
          },
          {
            exercise: "Running",
            amount: 1.2,
            units: "Miles",
            date: new Date(2022, 1, 3),
            minutes: 10,
            calories: 120
          },
          {
            exercise: "Running",
            amount: 1.5,
            units: "Miles",
            date: new Date(2022, 1, 5),
            minutes: 12,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 1),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Heavy Bag",
            amount: 6,
            units: "Rounds",
            date: new Date(2022, 1, 3),
            minutes: 22,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 5),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 10,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 10,
            calories: 100
          },
        ],
      },
    },
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Prisma знает, что нужно использовать seed.jsфайл из команды, которую вы добавили в package.json. Этот файл будет вставлять данные при запуске этой команды.

Теперь вы можете добавить и применить миграцию с помощью интерфейса командной строки Prisma. В корневом каталоге вашего приложения выполните следующую команду.

npx prisma migrate dev --name init

В интерфейсе командной строки есть несколько способов добавления и применения миграций. Я предлагаю вам понять, как лучше всего управлять миграциями для вашей среды. Описанный выше метод — самый простой и быстрый способ подготовить базу данных. В рамках процесса миграции эта команда будет искать seedкоманду у вас package.jsonи запускать ее. После завершения ваша база данных должна быть готова с базой данных, полной исходных данных для работы.

Добавить аутентификацию OIDC

Теперь, когда ваша база данных готова, вы можете обратить внимание на ядро ​​вашего приложения.

Во-первых, добавьте новый файл в корень вашего приложения с именем ensureLoggedIn.jsи добавьте в него следующий код.

function ensureLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login')
}

module.exports = ensureLoggedIn;

Этот небольшой компонент промежуточного программного обеспечения обеспечит аутентификацию пользователя. Если это не так, вы перенаправите их на loginмаршрут, который вы настроите для использования Okta. В противном случае вы позволите пользователю перейти на следующий экран.

Далее вы можете обновить свои маршруты в routesкаталоге. Во-первых, удалите, users.jsпоскольку вы не будете использовать это. Замените код index.jsследующим.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});

module.exports = router;

Здесь вы передадите свойство isAuthenticated, чтобы ваша страница макета могла правильно отображать кнопку входа или выхода.

Затем добавьте файл в routesкаталог для dashboard.jsсо следующим кодом.

var express = require("express");
var router = express.Router();

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const ensureLoggedIn = require("../ensureLoggedIn");

/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
  const username = req.user.username;

  const dbUser = await prisma.user.findUnique({
    where: {
      username: username,
    },
    include: {
      workoutLogs: true,
    },
  });

  res.render("dashboard", {
    title: "The Workout Tracker - Dashboard",
    isAuthenticated: req.isAuthenticated(),
    user: dbUser
  });
});

module.exports = router;

В этом маршруте много волшебства. Во-первых, вы используете ensureLoggedInпромежуточное ПО для защиты маршрута. Затем вы можете извлечь имя пользователя из запроса и найти пользователя в базе данных с помощью клиента Prisma. Поскольку у пользователя есть свойство для его журналов тренировок, вы можете передать пользователя в качестве модели и проанализировать журналы в представлении мопса, которое вы создадите. Это также даст доступ к просмотру имени пользователя. Конечно, вы можете запросить workoutLogsтаблицу напрямую и включить такие функции, как разбиение на страницы и поиск, если этот подход лучше подходит для вашего рабочего процесса.

Теперь замените код app.jsна следующий.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');

const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

var app = express();

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient();

require('dotenv').config({path:'./.okta.env'});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(session({
  secret: 'CanYouLookTheOtherWay',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());


const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;

// set up passport
passport.use('oidc', new Strategy({
  issuer: OKTA_OAUTH2_ISSUER,
  authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
  tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
  userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
  clientID: OKTA_OAUTH2_CLIENT_ID,
  clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/authorization-code/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/login', passport.authenticate('oidc'));

app.use('/authorization-code/callback',
  passport.authenticate('oidc', { failureRedirect: '/error' }),
  (req, res, next) => {
    res.redirect('/dashboard');
  }
);

app.post('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Вы обновили свои маршруты, чтобы правильно использовать dashboardмаршруты, а также любые маршруты входа или выхода, которые будет использовать Okta. Вы также настроили паспорт для использования Okta, используя значения, созданные интерфейсом командной строки Okta в .okta.envфайле.

Добавьте представления в свое приложение Express

По умолчанию express-generatorдобавит вам несколько просмотров. Вам нужно будет отредактировать их и добавить новый собственный. Начните с открытия layout.pugфайла в viewsкаталоге и замены кода следующим.

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/output.css')
  body
    nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
      span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
      .w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
        .text-sm(class='lg:flex-grow')
        div
          if(isAuthenticated)
            form(action="logout" method="POST") 
              button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
          else 
            a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
    
    block content

Вы заметите пару вещей об этом файле. Во-первых, занятия очень специфичны и многочисленны. Это типично для Tailwind CSS. Вместо того, чтобы определять, как выглядит конкретный элемент, Tailwind CSS стремится определить, как выглядит конкретный стиль, после чего вы можете добавлять и удалять стили из элементов, чтобы они соответствовали желаемому виду.

Еще одна вещь, которую вы увидите, — это кнопка входа/ выхода , которая будет меняться в зависимости от isAuthenticatedсвойства.

Затем вы можете обновить index.pugследующее.

extends layout

block content
  .py-12.bg-white
    .max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
      div.c(class='lg:text-center')
        p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
        p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
          | A small demo built with 
          a(href="https://expressjs.com/") Express on 
          a(href="https://nodejs.org/en/") Node.JS 
          | Secured with 
          a(href="https://developer.okta.com/signup") Okta.
          br          
          | This app uses 
          a(href="https://tailwindcss.com/") Tailwind CSS 
          | for its CSS framework and 
          a(href="https://www.prisma.io/") Prisma.JS 
          | for its ORM.  

Здесь не о чем говорить. Просто заставка с дополнительным CSS-кодом Tailwind и некоторыми ссылками на используемые технологии.

Наконец, добавьте файл для dashboard.pugсо следующим кодом.

extends layout

block content 
    .flex.flex-wrap
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-green-600
                            i.fa.fa-wallet.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0) 
                            span.text-green-500
                                i.fas.fa-caret-up
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-pink-600
                            i.fas.fa-users.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Calories Burned
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)  
                            span.text-pink-500
                                i.fas.fa-exchange-alt
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-yellow-600
                            i.fas.fa-user-plus.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Days Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
                            span.text-yellow-600
                                i.fas.fa-caret-up
    .w-full.p-6
        .bg-white.border-transparent.rounded-lg.shadow-xl
            .bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
                h2.font-bold.uppercase.text-gray-600 Workout Log
            .p-5
                table.w-full.p-5.text-gray-700
                    thead
                        tr
                            th.text-left.text-blue-900 Date
                            th.text-left.text-blue-900 Execise
                            th.text-left.text-blue-900 Amount
                            th.text-left.text-blue-900 Time
                            th.text-left.text-blue-900 Calories Burned
                    tbody
                        each log in user.workoutLogs.sort((a,b) => a.date - b.date)
                            tr
                                td=log.date.toLocaleDateString()
                                td=log.exercise
                                td=log.amount + ' ' + log.units 
                                td=log.minutes + ' Minutes'
                                td=log.calories                        

Как вы видели ранее, эта страница защищена. Поэтому для доступа к этой странице требуется, чтобы у вас был пользователь. Приложение извлечет данные о пользователе и журналы тренировок и сформирует их в сводку в верхней части страницы с парой таблиц журналов на основе упражнений, которые выполнил пользователь.

Создайте файл CSS с помощью Tailwind CSS.

Последней частью этого процесса является создание фактического файла CSS, который будет использовать ваше приложение. В вашем public/stylesheetsкаталоге вы увидите файл с именем style.css. Замените код в этом файле кодом ниже.

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

Это во многом то же самое. Однако вы импортируете директивы для каждого из слоев Tailwind.

Затем вы можете скомпилировать классы, которые вы использовали в своих файлах pug, и таблицу стилей, которую вы только что изменили, чтобы создать новый файл CSS. Выполните следующую команду.

npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch

Теперь вы можете открыть output.cssфайл и увидеть полный CSS-код Tailwind. Параметр --watchздесь полезен для местного развития. Это будет перестраивать output.cssфайл каждый раз, когда вы меняете один из ваших макетов. Вы можете игнорировать этот параметр, если не хотите вносить какие-либо изменения.

Протестируйте свое приложение

Теперь вы готовы запустить свое приложение. Выполните следующую команду.

npm run start

После запуска приложения вы сможете перейти по адресу http://localhost:3000/ и увидеть свою домашнюю страницу.

Домашняя страница

Используйте кнопку входа в верхней части экрана и перейдите на страницу входа в Okta. После того, как вы вошли в систему, вы будете перенаправлены обратно на свой веб-сайт, где вы сможете увидеть свою страницу панели инструментов.

Страница панели инструментов

Что вы узнали из этой статьи

В этой статье вы узнали, как использовать Prisma для определения схемы вашей базы данных. Затем вы узнали, как создать миграцию из этой схемы и заполнить базу данных. Вы видели, что Prisma можно использовать с самыми популярными вариантами баз данных, включая SQL Server, MySql, MongoDB и другие.

Вы также узнали, как создать файл CSS с помощью Tailwind CSS. Мы рассмотрели простую конфигурацию попутного ветра и то, как интерфейс командной строки Tailwind использует ваш код для создания файла CSS.

Наконец, вы узнали, как защитить экспресс-заявку с помощью паспорта.

Ссылка: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind

#tailwindcss #express  #prisma 

Как создать экспресс-приложение с помощью Prisma и TailwindCSS
Callum  Owen

Callum Owen

1659012496

How to Build an Express Application Using Prisma & TailwindCSS

Prisma is an ORM (object–relational mapping) tool for Node.js using TypeScript. The software integrates with many of the most popular databases today, including MySQL, SQL Server, SQLite, and MongoDB, and emphasizes a human-readable schema with a type-safe database client. Prisma also includes other features such as migrations, seed data, and a virtual database browser.

In this project, you will use Prisma to connect your Express application to a database server. You will build a schema to model a workout tracker application. Then you’ll create some seed data and use Prisma to run migrations and seed your database. Finally, you’ll create the web application using Pug and Tailwind CSS to build the application frontend.

See more at: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind

#tailwindcss #express  #prisma 

How to Build an Express Application Using Prisma & TailwindCSS
加藤  七夏

加藤 七夏

1659002100

如何使用 Prisma 和 TailwindCSS 構建 Express 應用程序

Prisma 是一個使用 TypeScript 的 Node.js 的 ORM(對象-關係映射)工具。該軟件與當今許多最流行的數據庫集成,包括 MySQL、SQL Server、SQLite 和 MongoDB,並強調具有類型安全數據庫客戶端的人類可讀模式。Prisma 還包括其他功能,例如遷移、種子數據和虛擬數據庫瀏覽器。

在這個項目中,您將使用 Prisma 將您的Express應用程序連接到數據庫服務器。您將構建一個模式來為鍛煉跟踪器應用程序建模。然後,您將創建一些種子數據並使用 Prisma 運行遷移並為您的數據庫提供種子。最後,您將使用PugTailwind CSS創建 Web 應用程序來構建應用程序前端。

先決條件

本教程使用以下技術,但不需要任何先前的經驗:

  • JavaScript 的 IDE。我將使用Visual Studio Code,但您可以使用 Webstorm 或您喜歡的任何其他 IDE。
  • 節點.js
  • 數據庫,例如 PostgreSQL、MySQL、SQLite、SQL Server 或 MongoDB。在本教程中,我們將使用 SQLite。
  • 奧克塔 CLI

如果您想跳過本教程並查看完整構建的項目,可以在 GitHub 上查看

創建您的 OAuth2 授權服務器

為您的應用程序創建一個新目錄。使用該cd命令導航到該文件夾。

在開始之前,您需要一個免費的 Okta 開發者帳戶。安裝Okta CLI並運行okta register以註冊一個新帳戶。如果您已經有一個帳戶,請運行okta login. 然後,運行okta apps create。選擇默認應用名稱,或根據需要進行更改。選擇Web並按Enter

選擇其他。然後,將重定向 URI 更改為http://localhost:3000/authorization-code/callbackhttp://localhost:3000/用於註銷重定向 URI。

Okta CLI 有什麼作用?

Okta CLI 將在您的 Okta 組織中創建一個 OIDC Web 應用程序。它將添加您指定的重定向 URI,並授予對 Everyone 組的訪問權限。完成後,您將看到如下輸出:

Okta application configuration has been written to: /path/to/app/.okta.env

運行cat .okta.env(或type .okta.env在 Windows 上)以查看您的應用的頒發者和憑據。

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

您的 Okta 域是您的發行人的第一部分,在/oauth2/default.

注意:您還可以使用 Okta 管理控制台來創建您的應用程序。有關詳細信息,請參閱創建 Web 應用程序

創建您的 Express 應用程序

接下來,您將使用express-generator應用程序生成器工具快速搭建應用程序。運行以下命令。

npx express-generator@4.16 --view=pug

現在你可以安裝你的包了。

npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
  • @prisma/client用於從您的服務器代碼訪問數據庫。
  • dotenv.env從像 Okta CLI 生成的文件中讀取配置設置。
  • passport是 Node.js 的中間件,它足夠靈活,可以處理大多數身份驗證場景,包括 Okta。passport-openidconnect是一個護照模塊,可讓您使用 OpenID Connect 進行身份驗證。
  • express-session需要護照。Express 應用程序使用這個包來支持會話。您的應用程序必須初始化會話支持才能使用護照。
  • Tailwind CSS是您將使用的 CSS 框架。如果您以前從未使用過 Tailwind,您可能想知道為什麼它是開發依賴項。這是因為 Tailwind 會根據您的視圖和配置動態構建您的 CSS 文件。稍後再談。
  • prisma是您正在使用的 ORM。這是一個開發依賴項,因為 Prisma 庫處理遷移、種子數據等,而@prisma/client包在運行時在您的應用程序中使用。

此時,您將初始化 Tailwind CSS 和 Prisma。您將配置它們以供稍後使用。

從 Prisma 開始並運行以下命令。

npx prisma init

此命令將添加一個名為prisma您的應用程序的新文件夾。它還.env使用一些默認配置添加一個名為您的項目的文件。用下面的代碼替換該文件的內容。

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}

在這個示例中,我使用了 SQLite,因為它簡單且緊湊。但是關於 Prisma 和大多數 ORM 的一個很酷的事情是它們可以支持許多數據庫。樣板代碼包括有關為其他數據庫服務器配置應用程序的註釋。隨意使用您覺得最舒服的東西並使用適當的連接字符串。

您的種子數據將使用該SEED_USER_NAME設置。您要確保它與您登錄的 Okta 用戶名相同。這將允許應用程序將您的登錄用戶與您將播種到數據庫中的數據相關聯。

接下來,package.json使用以下代碼更新您的文件。

{
  "name": "workout-app",
  "version": "0.0.0",
  "private": true,
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@prisma/client": "^3.13.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.0",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "passport": "^0.5.2",
    "passport-openidconnect": "^0.1.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "prisma": "^3.13.0",
    "tailwindcss": "^3.0.24"
  }
}

這將添加prisma seed您稍後需要的命令。

現在您可以初始化 Tailwind CSS。

npx tailwindcss init

您現在已經添加了一個新文件,名為tailwind.config.js應用程序的根目錄。用下面的代碼替換該文件的內容。

module.exports = {
  content: ["./views/**/*.pug"],
  theme: {
    extend: {},
  },
  plugins: [],
}

在這一步中,您告訴 Tailwind CSS 在哪裡可以找到您在應用程序中使用的類。您希望 Tailwind 查看.pugviews目錄中的文件。Tailwind CSS 具有高度可擴展性,從配置對象themeplugins設置可以看出。對此的深入探討超出了本文的範圍,但我鼓勵您查看 Tailwind CSS 的網站以獲取更多信息。

使用 Prisma 創建數據庫

下一個任務是為您的應用程序創建數據庫。步驟將涉及編寫架構、編寫一些種子數據、創建遷移和應用遷移,這也將為您的數據提供種子。

上面的prisma init任務應該已經添加了一個名為schema.prisma您的prisma目錄的文件。用下面的代碼替換那裡的代碼。

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

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

model WorkoutLog {
  id        Int      @id @default(autoincrement())
  userId    Int
  exercise  String
  amount    Decimal
  units     String
  date      DateTime
  minutes   Int
  calories  Int
  user      User     @relation(fields: [userId], references: [id])
}

model User {
  id          Int          @id @default(autoincrement())
  username    String       @unique
  workoutLogs WorkoutLog[]
}

確保您替換為您正在使用的提供{yourDatabaseProvider}程序。如您所見,該文件定義了您將存儲在數據庫中的對象和對象。具有有關用戶在特定日期鍛煉的基本信息,然後將該記錄與對象相關聯。您還可以從此文件中定義鍵、約束和索引。請注意,上的屬性具有由表定義的關係。UserWorkoutLogWorkoutLogUseruserWorkoutLoguserIdidUser

將文件添加到prisma名為的目錄中seed.js,並將以下代碼添加到其中。

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

require("dotenv").config();

async function main() {
  const user = await prisma.user.upsert({
    where: { username: process.env.SEED_USER_NAME },
    update: {},
    create: {
      username: process.env.SEED_USER_NAME,
      workoutLogs: {
        create: [
          {
            exercise: "Running",
            amount: 1,
            units: "Miles",
            date: new Date(2022, 1, 1),
            minutes: 8,
            calories: 100
          },
          {
            exercise: "Running",
            amount: 1.2,
            units: "Miles",
            date: new Date(2022, 1, 3),
            minutes: 10,
            calories: 120
          },
          {
            exercise: "Running",
            amount: 1.5,
            units: "Miles",
            date: new Date(2022, 1, 5),
            minutes: 12,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 1),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Heavy Bag",
            amount: 6,
            units: "Rounds",
            date: new Date(2022, 1, 3),
            minutes: 22,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 5),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 10,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 10,
            calories: 100
          },
        ],
      },
    },
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Prisma 知道使用seed.js您添加到的命令中的文件package.json。當您運行該命令時,此文件將插入數據。

您現在可以使用 Prisma CLI 添加和應用遷移。從應用程序目錄的根目錄運行以下命令。

npx prisma migrate dev --name init

CLI 有多種添加和應用遷移的方法。我建議您了解為您的環境管理遷移的最佳方式。上述方法是準備數據庫的最簡單、最快的方法。作為遷移過程的一部分,此命令seed將從您的命令中查找package.json並運行該命令。完成後,您的數據庫應該準備好使用充滿種子數據的數據庫。

添加 OIDC 認證

現在您的數據庫已準備就緒,您可以將注意力轉向應用程序的核心。

首先,在名為的應用程序的根目錄中添加一個新文件,ensureLoggedIn.js並向其中添加以下代碼。

function ensureLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login')
}

module.exports = ensureLoggedIn;

這個小中間件將確保用戶通過身份驗證。如果不是,您會將它們重定向到login您將配置為使用 Okta 的路由。否則,您將允許用戶到達下一個屏幕。

接下來,您可以更新routes目錄中的路線。首先,刪除users.js,因為您不會使用它。將代碼替換為index.js以下內容。

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});

module.exports = router;

在這裡,您將傳遞一個屬性,isAuthenticated以便您的佈局頁面可以正確顯示登錄或註銷按鈕。

接下來,使用以下代碼在routes目錄中添加一個文件。dashboard.js

var express = require("express");
var router = express.Router();

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const ensureLoggedIn = require("../ensureLoggedIn");

/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
  const username = req.user.username;

  const dbUser = await prisma.user.findUnique({
    where: {
      username: username,
    },
    include: {
      workoutLogs: true,
    },
  });

  res.render("dashboard", {
    title: "The Workout Tracker - Dashboard",
    isAuthenticated: req.isAuthenticated(),
    user: dbUser
  });
});

module.exports = router;

這條路線有很多魔法。首先,您正在使用ensureLoggedIn中間件來保護路由。然後您可以從請求中提取用戶名並使用 Prisma 客戶端在數據庫中查找用戶。由於用戶有其鍛煉日誌的屬性,您可以將用戶作為模型傳遞,並在您將創建的 pug 視圖上解析日誌。這也將授予對用戶名的查看訪問權限。當然,workoutLogs如果這種方法更適合您的工作流程,您可以直接查詢表格並包含分頁和搜索等功能。

現在,將代碼替換為app.js以下內容。

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');

const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

var app = express();

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient();

require('dotenv').config({path:'./.okta.env'});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(session({
  secret: 'CanYouLookTheOtherWay',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());


const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;

// set up passport
passport.use('oidc', new Strategy({
  issuer: OKTA_OAUTH2_ISSUER,
  authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
  tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
  userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
  clientID: OKTA_OAUTH2_CLIENT_ID,
  clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/authorization-code/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/login', passport.authenticate('oidc'));

app.use('/authorization-code/callback',
  passport.authenticate('oidc', { failureRedirect: '/error' }),
  (req, res, next) => {
    res.redirect('/dashboard');
  }
);

app.post('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

您更新了路由以正確使用dashboard路由以及 Okta 將使用的任何登錄或註銷路由。.okta.env您還可以使用 Okta CLI 在文件中生成的值設置通行證以使用 Okta 。

將視圖添加到您的 Express 應用程序

默認情況下,express-generator會為您添加一些視圖。您將需要編輯它們並添加一個新的。首先打開目錄layout.pug中的文件views並將其中的代碼替換為以下內容。

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/output.css')
  body
    nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
      span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
      .w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
        .text-sm(class='lg:flex-grow')
        div
          if(isAuthenticated)
            form(action="logout" method="POST") 
              button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
          else 
            a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
    
    block content

你會注意到關於這個文件的一些事情。首先,課程非常具體和豐富。這是 Tailwind CSS 的典型特徵。Tailwind CSS 不是定義特定元素的外觀,而是努力定義特定樣式的外觀,然後您可以從元素中添加和刪除樣式以適應所需的外觀。

您將看到的另一件事是登錄/註銷按鈕,該按鈕將根據isAuthenticated屬性進行更改。

接下來,您可以index.pug使用以下內容進行更新。

extends layout

block content
  .py-12.bg-white
    .max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
      div.c(class='lg:text-center')
        p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
        p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
          | A small demo built with 
          a(href="https://expressjs.com/") Express on 
          a(href="https://nodejs.org/en/") Node.JS 
          | Secured with 
          a(href="https://developer.okta.com/signup") Okta.
          br          
          | This app uses 
          a(href="https://tailwindcss.com/") Tailwind CSS 
          | for its CSS framework and 
          a(href="https://www.prisma.io/") Prisma.JS 
          | for its ORM.  

這裡沒什麼好說的。只是一個帶有更多 Tailwind CSS 和一些所用技術鏈接的啟動頁面。

最後,dashboard.pug使用以下代碼添加文件。

extends layout

block content 
    .flex.flex-wrap
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-green-600
                            i.fa.fa-wallet.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0) 
                            span.text-green-500
                                i.fas.fa-caret-up
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-pink-600
                            i.fas.fa-users.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Calories Burned
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)  
                            span.text-pink-500
                                i.fas.fa-exchange-alt
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-yellow-600
                            i.fas.fa-user-plus.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Days Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
                            span.text-yellow-600
                                i.fas.fa-caret-up
    .w-full.p-6
        .bg-white.border-transparent.rounded-lg.shadow-xl
            .bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
                h2.font-bold.uppercase.text-gray-600 Workout Log
            .p-5
                table.w-full.p-5.text-gray-700
                    thead
                        tr
                            th.text-left.text-blue-900 Date
                            th.text-left.text-blue-900 Execise
                            th.text-left.text-blue-900 Amount
                            th.text-left.text-blue-900 Time
                            th.text-left.text-blue-900 Calories Burned
                    tbody
                        each log in user.workoutLogs.sort((a,b) => a.date - b.date)
                            tr
                                td=log.date.toLocaleDateString()
                                td=log.exercise
                                td=log.amount + ' ' + log.units 
                                td=log.minutes + ' Minutes'
                                td=log.calories                        

如您之前所見,此頁面受到保護。因此,它要求您有一個用戶才能訪問此頁面。該應用程序將提取用戶詳細信息和鍛煉日誌,並將它們形成頁面頂部的摘要,其中包含基於用戶執行的鍛煉的幾個日誌表。

使用 Tailwind CSS 創建 CSS 文件

此過程的最後一部分是創建應用程序將使用的實際 CSS 文件。在您的public/stylesheets目錄中,您將看到一個名為style.css. 用下面的代碼替換該文件中的代碼。

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

這在很大程度上是相同的。但是,您正在為 Tailwind 的每個層導入指令。

接下來,您可以編譯您在 pug 文件中使用的類和您剛剛修改的樣式表,以創建一個新的 CSS 文件。運行以下命令。

npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch

現在您可以打開output.css文件並查看完整的 Tailwind CSS 代碼。這裡的--watch參數對本地開發很有幫助。output.css這將在您每次更改其中一個佈局時重建文件。如果您不想進行任何更改,可以忽略該參數。

測試您的應用程序

您現在已準備好啟動您的應用程序。運行以下命令。

npm run start

啟動應用程序後,您應該能夠導航到http://localhost:3000/並查看您的主頁。

主頁

使用屏幕頂部的登錄按鈕並導航到 Okta 登錄頁面。登錄後,您將被重定向回您的網站,在那裡您將能夠看到您的儀表板頁面。

儀表板頁面

你在這篇文章中學到了什麼

在本文中,您學習瞭如何使用 Prisma 為您的數據庫定義模式。然後,您學習瞭如何從該架構創建遷移並為您的數據庫播種。您看到 Prisma 可以與最流行的數據庫選項一起使用,包括 SQL Server、MySql、MongoDB 等。

您還學習瞭如何使用 Tailwind CSS 構建 CSS 文件。我們介紹了一個簡單的順風配置以及 Tailwind CLI 如何使用您的代碼來構建其 CSS 文件。

最後,您學習瞭如何使用護照保護您的快速申請。

鏈接:https ://developer.okta.com/blog/2022/07/25/express-prisma-tailwind

#tailwindcss #express  #prisma 

如何使用 Prisma 和 TailwindCSS 構建 Express 應用程序
Hong  Nhung

Hong Nhung

1658991189

Cách Xây Dựng ứng Dụng Cấp Tốc Bằng Prisma & TailwindCSS

Prisma là một công cụ ORM (ánh xạ đối tượng-quan hệ) cho Node.js sử dụng TypeScript. Phần mềm tích hợp với nhiều cơ sở dữ liệu phổ biến nhất hiện nay, bao gồm MySQL, SQL Server, SQLite và MongoDB, đồng thời nhấn mạnh một lược đồ có thể đọc được với máy khách cơ sở dữ liệu kiểu an toàn. Prisma cũng bao gồm các tính năng khác như di chuyển, dữ liệu hạt giống và trình duyệt cơ sở dữ liệu ảo.

Trong dự án này, bạn sẽ sử dụng Prisma để kết nối ứng dụng Express của mình với máy chủ cơ sở dữ liệu. Bạn sẽ xây dựng một lược đồ để lập mô hình ứng dụng theo dõi tập luyện. Sau đó, bạn sẽ tạo một số dữ liệu gốc và sử dụng Prisma để chạy quá trình di chuyển và khởi tạo cơ sở dữ liệu của bạn. Cuối cùng, bạn sẽ tạo ứng dụng web bằng PugTailwind CSS để xây dựng giao diện ứng dụng.

Điều kiện tiên quyết

Hướng dẫn này sử dụng các công nghệ sau nhưng không yêu cầu bất kỳ kinh nghiệm nào trước đó:

  • IDE cho JavaScript. Tôi sẽ sử dụng Visual Studio Code nhưng bạn có thể sử dụng Webstorm hoặc bất kỳ IDE nào khác mà bạn thích.
  • Node.js
  • Cơ sở dữ liệu, chẳng hạn như PostgreSQL, MySQL, SQLite, SQL Server hoặc MongoDB. Trong hướng dẫn này, chúng tôi sẽ sử dụng SQLite.
  • Okta CLI

Nếu bạn muốn bỏ qua hướng dẫn và xem dự án được xây dựng đầy đủ, bạn có thể xem nó trên GitHub .

Tạo máy chủ ủy quyền OAuth2 của bạn

Tạo một thư mục mới cho ứng dụng của bạn. Sử dụng cdlệnh để điều hướng đến thư mục đó.

Trước khi bắt đầu, bạn cần có tài khoản nhà phát triển Okta miễn phí. Cài đặt Okta CLI và chạy okta registerđể đăng ký tài khoản mới. Nếu bạn đã có tài khoản, hãy chạy okta login. Sau đó, chạy okta apps create. Chọn tên ứng dụng mặc định hoặc thay đổi nó khi bạn thấy phù hợp. Chọn Web và nhấn Enter .

Chọn Khác . Sau đó, thay đổi URI chuyển hướng thành http://localhost:3000/authorization-code/callbackvà sử dụng http://localhost:3000/cho URI chuyển hướng đăng xuất.

Okta CLI làm gì?

Okta CLI sẽ tạo Ứng dụng web OIDC trong Tổ chức Okta của bạn. Nó sẽ thêm các URI chuyển hướng mà bạn đã chỉ định và cấp quyền truy cập vào nhóm Mọi người. Bạn sẽ thấy kết quả như sau khi hoàn tất:

Okta application configuration has been written to: /path/to/app/.okta.env

Chạy cat .okta.env(hoặc type .okta.envtrên Windows) để xem nhà phát hành và thông tin đăng nhập cho ứng dụng của bạn.

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

Miền Okta của bạn là phần đầu tiên của công ty phát hành của bạn, trước đây /oauth2/default.

LƯU Ý : Bạn cũng có thể sử dụng Bảng điều khiển dành cho quản trị viên Okta để tạo ứng dụng của mình. Xem Tạo ứng dụng web để biết thêm thông tin.

Tạo ứng dụng Express của bạn

Tiếp theo, bạn sẽ sử express-generatordụng công cụ tạo ứng dụng để tạo nhanh ứng dụng của mình. Chạy lệnh sau.

npx express-generator@4.16 --view=pug

Bây giờ bạn có thể cài đặt các gói của mình.

npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
  • @prisma/clientđược sử dụng để truy cập cơ sở dữ liệu từ mã máy chủ của bạn.
  • dotenvđọc cài đặt cấu hình từ .envcác tệp như tệp được tạo bởi Okta CLI.
  • passportlà một phần mềm trung gian cho Node.js đủ linh hoạt để xử lý hầu hết các tình huống xác thực, bao gồm cả Okta. passport-openidconnectlà một mô-đun dành cho hộ chiếu cho phép bạn xác thực với OpenID Connect.
  • express-sessionlà bắt buộc đối với hộ chiếu. Các ứng dụng Express sử dụng gói này để hỗ trợ phiên. Ứng dụng của bạn phải phức tạp hóa hỗ trợ phiên để sử dụng hộ chiếu.
  • Tailwind CSSlà khung CSS mà bạn sẽ sử dụng. Nếu bạn chưa từng sử dụng Tailwind trước đây, bạn có thể thắc mắc tại sao nó lại phụ thuộc vào sự phát triển. Điều này là do Tailwind sẽ tự động tạo các tệp CSS của bạn từ các chế độ xem và cấu hình của bạn. Thêm về điều đó sau.
  • prismalà ORM bạn đang sử dụng. Đây là sự phụ thuộc của nhà phát triển vì thư viện Prisma xử lý các quá trình di chuyển, dữ liệu hạt giống, v.v., trong khi @prisma/clientgói được sử dụng trong ứng dụng của bạn lúc chạy.

Tại thời điểm này, bạn sẽ khởi tạo Tailwind CSS và Prisma. Bạn sẽ cấu hình chúng để sử dụng sau này.

Bắt đầu với Prisma và chạy lệnh sau.

npx prisma init

Lệnh này sẽ thêm một thư mục mới có tên prismavào ứng dụng của bạn. Nó cũng thêm một tệp được gọi .envvào dự án của bạn với một số cấu hình mặc định. Thay thế nội dung của tệp đó bằng mã bên dưới.

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB, and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}

Trong ví dụ này, tôi đã sử dụng SQLite vì nó dễ dàng và nhỏ gọn. Nhưng một trong những điều thú vị về Prisma và hầu hết các ORM là chúng có thể hỗ trợ nhiều cơ sở dữ liệu. Mã boilerplate bao gồm các nhận xét về việc định cấu hình ứng dụng của bạn cho các máy chủ cơ sở dữ liệu khác. Hãy thoải mái sử dụng những gì bạn cảm thấy thoải mái nhất và sử dụng chuỗi kết nối thích hợp.

Dữ liệu hạt giống của bạn sẽ sử dụng SEED_USER_NAMEcài đặt này. Bạn muốn đảm bảo rằng nó giống với tên người dùng Okta mà bạn đăng nhập. Điều này sẽ cho phép ứng dụng liên kết người dùng đã đăng nhập của bạn với dữ liệu bạn sẽ đưa vào cơ sở dữ liệu của mình.

Tiếp theo, cập nhật package.jsontệp của bạn bằng mã sau.

{
  "name": "workout-app",
  "version": "0.0.0",
  "private": true,
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@prisma/client": "^3.13.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.0",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "passport": "^0.5.2",
    "passport-openidconnect": "^0.1.1",
    "pug": "2.0.0-beta11"
  },
  "devDependencies": {
    "prisma": "^3.13.0",
    "tailwindcss": "^3.0.24"
  }
}

Thao tác này sẽ thêm prisma seedlệnh bạn cần sau đó.

Bây giờ bạn có thể khởi tạo CSS Tailwind.

npx tailwindcss init

Bây giờ bạn đã thêm một tệp mới được gọi tailwind.config.jsvào thư mục gốc của ứng dụng của bạn. Thay thế nội dung của tệp đó bằng mã bên dưới.

module.exports = {
  content: ["./views/**/*.pug"],
  theme: {
    extend: {},
  },
  plugins: [],
}

Trong bước này, bạn cho Tailwind CSS biết nơi tìm các lớp bạn đã sử dụng trong ứng dụng của mình. Bạn muốn Tailwind tìm kiếm các .pugtệp trong thư mục của bạn views. Tailwind CSS có khả năng mở rộng cao, như được thấy bởi đối tượng cấu hình themepluginscài đặt. Tìm hiểu sâu về vấn đề này nằm ngoài phạm vi của bài viết này, nhưng tôi khuyến khích bạn nên xem trang web của Tailwind CSS để biết thêm thông tin.

Sử dụng Prisma để tạo cơ sở dữ liệu của bạn

Nhiệm vụ tiếp theo là tạo cơ sở dữ liệu cho ứng dụng của bạn. Các bước sẽ liên quan đến việc viết lược đồ, viết một số dữ liệu gốc, tạo quá trình di chuyển và áp dụng quá trình di chuyển, cũng sẽ tạo ra dữ liệu của bạn.

Tác prisma initvụ từ trên đáng lẽ phải thêm một tệp được gọi schema.prismavào thư mục của bạn prisma. Thay thế mã ở đó bằng mã dưới đây.

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

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

model WorkoutLog {
  id        Int      @id @default(autoincrement())
  userId    Int
  exercise  String
  amount    Decimal
  units     String
  date      DateTime
  minutes   Int
  calories  Int
  user      User     @relation(fields: [userId], references: [id])
}

model User {
  id          Int          @id @default(autoincrement())
  username    String       @unique
  workoutLogs WorkoutLog[]
}

Đảm bảo rằng bạn thay thế {yourDatabaseProvider}bằng nhà cung cấp bạn đang sử dụng . Như bạn có thể thấy, tệp này xác định đối tượng UserWorkoutLogđối tượng mà bạn sẽ lưu trữ trong cơ sở dữ liệu. Có WorkoutLogthông tin cơ bản về quá trình tập luyện của người dùng vào một ngày cụ thể và sau đó liên kết bản ghi đó với Userđối tượng. Bạn cũng có thể xác định khóa, ràng buộc và chỉ số từ tệp này. Lưu ý rằng thuộc usertính trên WorkoutLogcó một mối quan hệ được xác định bởi mục userIdcủa idbảng User.

Thêm tệp vào thư mục prismacó tên seed.jsvà thêm mã sau vào thư mục đó.

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

require("dotenv").config();

async function main() {
  const user = await prisma.user.upsert({
    where: { username: process.env.SEED_USER_NAME },
    update: {},
    create: {
      username: process.env.SEED_USER_NAME,
      workoutLogs: {
        create: [
          {
            exercise: "Running",
            amount: 1,
            units: "Miles",
            date: new Date(2022, 1, 1),
            minutes: 8,
            calories: 100
          },
          {
            exercise: "Running",
            amount: 1.2,
            units: "Miles",
            date: new Date(2022, 1, 3),
            minutes: 10,
            calories: 120
          },
          {
            exercise: "Running",
            amount: 1.5,
            units: "Miles",
            date: new Date(2022, 1, 5),
            minutes: 12,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 1),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Heavy Bag",
            amount: 6,
            units: "Rounds",
            date: new Date(2022, 1, 3),
            minutes: 22,
            calories: 150
          },
          {
            exercise: "Heavy Bag",
            amount: 4,
            units: "Rounds",
            date: new Date(2022, 1, 5),
            minutes: 15,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 2),
            minutes: 10,
            calories: 100
          },
          {
            exercise: "Situps",
            amount: 50,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 5,
            calories: 50
          },
          {
            exercise: "Pushups",
            amount: 100,
            units: "Reps",
            date: new Date(2022, 1, 4),
            minutes: 10,
            calories: 100
          },
        ],
      },
    },
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Prisma biết sử dụng seed.jstệp từ lệnh bạn đã thêm vào package.json. Tệp này sẽ chèn dữ liệu khi bạn chạy lệnh đó.

Bây giờ bạn có thể thêm và áp dụng việc di chuyển bằng Prisma CLI. Từ thư mục gốc của thư mục ứng dụng của bạn, hãy chạy lệnh sau.

npx prisma migrate dev --name init

CLI có một số cách để thêm và áp dụng việc di chuyển. Tôi khuyên bạn nên hiểu cách tốt nhất để quản lý việc di chuyển cho môi trường của bạn. Phương pháp trên là cách dễ nhất và nhanh nhất để chuẩn bị cơ sở dữ liệu của bạn. Là một phần của quá trình di chuyển, lệnh này sẽ tìm kiếm seedlệnh từ của bạn package.jsonvà chạy lệnh đó. Sau khi hoàn tất, cơ sở dữ liệu của bạn sẽ sẵn sàng với một cơ sở dữ liệu đầy đủ dữ liệu hạt giống để làm việc.

Thêm xác thực OIDC

Bây giờ cơ sở dữ liệu của bạn đã sẵn sàng, bạn có thể chuyển sự chú ý của mình đến phần cốt lõi của ứng dụng của bạn.

Đầu tiên, thêm một tệp mới trong thư mục gốc của ứng dụng của bạn được gọi ensureLoggedIn.jsvà thêm mã sau vào tệp đó.

function ensureLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/login')
}

module.exports = ensureLoggedIn;

Phần mềm trung gian nhỏ này sẽ đảm bảo người dùng được xác thực. Nếu không, bạn sẽ chuyển hướng chúng đến logintuyến đường mà bạn sẽ định cấu hình để sử dụng Okta. Nếu không, bạn sẽ cho phép người dùng đến màn hình tiếp theo.

Tiếp theo, bạn có thể cập nhật các tuyến đường của mình trong routesdanh bạ. Đầu tiên, hãy xóa users.jsvì bạn sẽ không sử dụng nó. Thay thế mã bằng mã index.jssau.

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});

module.exports = router;

Tại đây, bạn sẽ chuyển một thuộc tính để isAuthenticatedtrang bố cục của bạn có thể hiển thị đúng nút đăng nhập hoặc đăng xuất.

Tiếp theo, thêm một tệp trong routesthư mục dashboard.jsvới mã sau.

var express = require("express");
var router = express.Router();

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const ensureLoggedIn = require("../ensureLoggedIn");

/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
  const username = req.user.username;

  const dbUser = await prisma.user.findUnique({
    where: {
      username: username,
    },
    include: {
      workoutLogs: true,
    },
  });

  res.render("dashboard", {
    title: "The Workout Tracker - Dashboard",
    isAuthenticated: req.isAuthenticated(),
    user: dbUser
  });
});

module.exports = router;

Có rất nhiều phép thuật trong tuyến đường này. Đầu tiên, bạn đang sử dụng ensureLoggedInphần mềm trung gian để bảo vệ tuyến đường. Sau đó, bạn có thể trích xuất tên người dùng từ yêu cầu và tra cứu người dùng trong cơ sở dữ liệu bằng ứng dụng khách Prisma. Vì người dùng có một thuộc tính cho nhật ký tập luyện của mình, bạn có thể chuyển người dùng làm mô hình và phân tích cú pháp các nhật ký trên chế độ xem pug mà bạn sẽ tạo. Điều này cũng sẽ cung cấp cho chế độ xem quyền truy cập vào tên của người dùng. Tất nhiên, bạn có thể truy vấn workoutLogsbảng trực tiếp và bao gồm các tính năng như phân trang và tìm kiếm nếu cách tiếp cận này phù hợp hơn với quy trình làm việc của bạn.

Bây giờ, thay thế mã bằng mã app.jssau.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');

const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');

var app = express();

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient();

require('dotenv').config({path:'./.okta.env'});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(session({
  secret: 'CanYouLookTheOtherWay',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());


const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;

// set up passport
passport.use('oidc', new Strategy({
  issuer: OKTA_OAUTH2_ISSUER,
  authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
  tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
  userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
  clientID: OKTA_OAUTH2_CLIENT_ID,
  clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/authorization-code/callback',
  scope: 'openid profile'
}, (issuer, profile, done) => {
  return done(null, profile);
}));

passport.serializeUser((user, next) => {
  next(null, user);
});

passport.deserializeUser((obj, next) => {
  next(null, obj);
});

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/login', passport.authenticate('oidc'));

app.use('/authorization-code/callback',
  passport.authenticate('oidc', { failureRedirect: '/error' }),
  (req, res, next) => {
    res.redirect('/dashboard');
  }
);

app.post('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Bạn đã cập nhật các tuyến đường của mình để sử dụng đúng các dashboardtuyến đường cũng như mọi tuyến đường đăng nhập hoặc đăng xuất mà Okta sẽ sử dụng. Bạn cũng thiết lập hộ chiếu để sử dụng Okta bằng các giá trị Okta CLI được tạo trong .okta.envtệp.

Thêm chế độ xem vào ứng dụng Express của bạn

Theo mặc định, express-generatorsẽ thêm một vài chế độ xem cho bạn. Bạn sẽ cần phải chỉnh sửa chúng và thêm một cái mới của riêng bạn. Bắt đầu bằng cách mở layout.pugtệp trong thư mục viewsvà thay thế mã ở đó bằng mã sau.

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/output.css')
  body
    nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
      span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
      .w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
        .text-sm(class='lg:flex-grow')
        div
          if(isAuthenticated)
            form(action="logout" method="POST") 
              button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
          else 
            a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
    
    block content

Bạn sẽ nhận thấy một số điều về tệp này. Đầu tiên, các lớp học rất cụ thể và phong phú. Đây là điển hình của Tailwind CSS. Thay vì xác định giao diện của một phần tử cụ thể, Tailwind CSS cố gắng xác định kiểu dáng cụ thể trông như thế nào, sau đó bạn có thể thêm và xóa kiểu khỏi các phần tử để phù hợp với giao diện mong muốn.

Điều khác bạn sẽ thấy là nút Đăng nhập / Đăng xuất sẽ thay đổi dựa trên thuộc isAuthenticatedtính.

Tiếp theo, bạn có thể cập nhật index.pugnhững điều sau.

extends layout

block content
  .py-12.bg-white
    .max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
      div.c(class='lg:text-center')
        p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
        p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
          | A small demo built with 
          a(href="https://expressjs.com/") Express on 
          a(href="https://nodejs.org/en/") Node.JS 
          | Secured with 
          a(href="https://developer.okta.com/signup") Okta.
          br          
          | This app uses 
          a(href="https://tailwindcss.com/") Tailwind CSS 
          | for its CSS framework and 
          a(href="https://www.prisma.io/") Prisma.JS 
          | for its ORM.  

Không có nhiều điều để nói ở đây. Chỉ là một trang giới thiệu với một số CSS Tailwind khác và một số liên kết đến các công nghệ được sử dụng.

Cuối cùng, thêm một tệp cho dashboard.pugvới mã sau.

extends layout

block content 
    .flex.flex-wrap
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-green-600
                            i.fa.fa-wallet.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0) 
                            span.text-green-500
                                i.fas.fa-caret-up
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-pink-600
                            i.fas.fa-users.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Calories Burned
                        p.font-bold.text-3xl
                            span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)  
                            span.text-pink-500
                                i.fas.fa-exchange-alt
        .w-full.p-6(class='md:w-1/2 xl:w-1/3')
            .bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
                .flex.flex-row.items-center
                    .flex-shrink.pr-4
                        .rounded-full.p-5.bg-yellow-600
                            i.fas.fa-user-plus.fa-2x.fa-inverse
                    .flex-1.text-right(class='md:text-center')
                        h2.font-bold.uppercase.text-gray-600 Total Days Worked
                        p.font-bold.text-3xl
                            span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
                            span.text-yellow-600
                                i.fas.fa-caret-up
    .w-full.p-6
        .bg-white.border-transparent.rounded-lg.shadow-xl
            .bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
                h2.font-bold.uppercase.text-gray-600 Workout Log
            .p-5
                table.w-full.p-5.text-gray-700
                    thead
                        tr
                            th.text-left.text-blue-900 Date
                            th.text-left.text-blue-900 Execise
                            th.text-left.text-blue-900 Amount
                            th.text-left.text-blue-900 Time
                            th.text-left.text-blue-900 Calories Burned
                    tbody
                        each log in user.workoutLogs.sort((a,b) => a.date - b.date)
                            tr
                                td=log.date.toLocaleDateString()
                                td=log.exercise
                                td=log.amount + ' ' + log.units 
                                td=log.minutes + ' Minutes'
                                td=log.calories                        

Như bạn đã thấy trước đây, trang này đã được bảo vệ. Do đó, nó yêu cầu bạn phải có một người dùng để truy cập trang này. Ứng dụng sẽ kéo chi tiết người dùng và nhật ký tập luyện và tạo thành một bản tóm tắt ở đầu trang với một vài bảng nhật ký dựa trên các bài tập mà người dùng đã thực hiện.

Tạo tệp CSS bằng Tailwind CSS

Phần cuối cùng của quá trình này là tạo tệp CSS thực tế mà ứng dụng của bạn sẽ sử dụng. Trong thư mục của bạn public/stylesheets, bạn sẽ thấy một tệp có tên style.css. Thay thế mã trong tệp đó bằng mã bên dưới.

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

Điều này phần lớn giống nhau. Tuy nhiên, bạn đang nhập các lệnh cho từng lớp của Tailwind.

Tiếp theo, bạn có thể biên dịch các lớp bạn đã sử dụng trong tệp pug của mình và bảng định kiểu bạn vừa sửa đổi để tạo tệp CSS mới. Chạy lệnh sau.

npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch

Bây giờ bạn có thể mở output.csstệp và xem mã Tailwind CSS hoàn chỉnh. Tham --watchsố ở đây rất hữu ích cho sự phát triển của địa phương. Thao tác này sẽ xây dựng lại output.csstệp mỗi khi bạn thay đổi một trong các bố cục của mình. Bạn có thể bỏ qua tham số đó nếu bạn không muốn thực hiện bất kỳ thay đổi nào.

Kiểm tra ứng dụng của bạn

Bây giờ bạn đã sẵn sàng để bắt đầu ứng dụng của mình. Chạy lệnh sau.

npm run start

Khi ứng dụng của bạn được khởi động, bạn sẽ có thể điều hướng đến http: // localhost: 3000 / và xem trang chủ của mình.

Trang chủ

Sử dụng nút đăng nhập ở đầu màn hình và điều hướng ra trang đăng nhập Okta. Sau khi đăng nhập, bạn sẽ được chuyển hướng trở lại trang web của mình, nơi bạn có thể xem trang bảng điều khiển của mình.

Trang tổng quan

Những gì bạn học được trong bài viết này

Trong bài viết này, bạn đã học cách sử dụng Prisma để xác định lược đồ cho cơ sở dữ liệu của mình. Sau đó, bạn đã học cách tạo sự di chuyển từ lược đồ đó và khởi tạo cơ sở dữ liệu của mình. Bạn thấy rằng Prisma có thể được sử dụng với các tùy chọn cơ sở dữ liệu phổ biến nhất, bao gồm SQL Server, MySql, MongoDB, v.v.

Bạn cũng đã học cách tạo tệp CSS bằng Tailwind CSS. Chúng ta đã xem qua cấu hình tailwind đơn giản và cách Tailwind CLI sử dụng mã của bạn để tạo tệp CSS của nó.

Cuối cùng, bạn đã học được cách bảo mật đơn xin cấp tốc của mình bằng hộ chiếu.

Liên kết: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind

#tailwindcss #express  #prisma 

Cách Xây Dựng ứng Dụng Cấp Tốc Bằng Prisma & TailwindCSS

Realizar Migrações De Esquema De Banco De Dados Usando O Prisma ORM

As migrações de esquema de banco de dados são algumas das tarefas mais frequentes e importantes executadas pelos desenvolvedores, especialmente à medida que os requisitos do produto evoluem. Se essas migrações não forem executadas corretamente, podem ocorrer consequências desastrosas, como perda de dados e inconsistências de esquema, levando a um estado inconsistente do banco de dados.

No entanto, eles são bastante tediosos para executar e gerenciar. Para resolver esse problema, o Prisma ORM criou uma ferramenta chamada Prisma Migrate que fornece mecanismos para realizar e gerenciar migrações de esquemas de banco de dados sem problemas.

Neste artigo, teremos uma visão prática detalhada de como realizar migrações de esquema de banco de dados usando o Prisma ORM. Começaremos projetando um esquema básico de um modelo de aplicativo de mídia social e trabalharemos fazendo alterações simples e complexas nesse esquema. Em essência, veremos como a funcionalidade integrada do Prisma (chamada Prisma Migrate) torna a criação e o gerenciamento de migrações de banco de dados o mais simples possível.

Pré-requisitos

Devido aos aspectos práticos nas seções posteriores deste artigo, você deve ter conhecimento prático de Node.js e JavaScript. Você também precisará instalar e configurar o PostgreSQL localmente, seguindo o guia do site oficial .

O que é Prisma?

Prisma é um ORM (Object Relational Mapper) que fornece uma abstração de alto nível sobre consultas de banco de dados brutos e funcionalidades de gerenciamento de banco de dados. Ele fornece uma API de tipo seguro e classes JavaScript para realizar consultas comuns ao banco de dados.

No contexto deste artigo, o Prisma fornece um sistema de migração robusto chamado Prisma Migrate, que simplifica o processo de criação e gerenciamento de migrações de esquema de banco de dados. Veremos como aproveitar esse sistema de migração mais tarde, mas antes disso, vamos fazer uma rápida atualização sobre migrações de esquema de banco de dados de uma perspectiva geral de banco de dados.

O que são migrações de esquema de banco de dados?

As migrações de esquema de banco de dados são uma maneira de gerenciar alterações incrementais feitas em um esquema de banco de dados existente, normalmente devido a alterações de requisitos ou correção de erros iniciais de design. Essas alterações em um banco de dados relacional incluem adicionar ou remover colunas e tabelas, alterar tipos de dados específicos para determinadas colunas, entre outras coisas.

Normalmente, realizamos migrações de esquema usando um arquivo de migração que contém código SQL com as alterações necessárias. Pode ser bastante desafiador gerenciar manualmente os arquivos de migração, especialmente quando o banco de dados contém dados do usuário do mundo real. No entanto, o Prisma fornece mecanismos para tornar esse processo menos assustador e arriscado do que o normal, usando um recurso específico chamado Prisma Migrate.

Configurando o projeto

Com isso coberto, vamos montar um projeto e entrar no cerne do artigo. Primeiro, vá para um diretório adequado e execute o seguinte comando no terminal:

npm init

O comando acima inicializa o diretório e cria um package.jsonarquivo. Quando estiver concluído, execute o seguinte comando para adicionar o pacote Prisma como uma dependência de desenvolvimento:

npm install prisma --save-dev

Então, podemos inicializar um projeto Prisma executando outro comando no terminal:

npx prisma init

O comando acima inicializa um projeto Prisma criando um diretório chamado prismae um arquivo dentro desse diretório chamado schema.prisma. Este arquivo é onde a maior parte do trabalho acontecerá, e chegaremos a isso daqui a pouco.

Em seguida, criaremos um esquema de modelo de um aplicativo de mídia social simples; esse esquema servirá como base para fazer alterações incrementais, imitando o que normalmente encontraremos em um cenário do mundo real.

Modelagem de uma plataforma de mídia social

Deve-se mencionar mais uma vez que, embora esse esquema seja muito básico, ele nos ajudará a entender como podemos realizar migrações de esquema no Prisma. Com isso dito, vá em frente e cole o seguinte código no schema.prismaarquivo, e nós o analisaremos logo em seguida:

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

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

model Users {
  id String @id @db.Uuid @default(uuid())
  fullName String
  email String @unique
  password String
  bio String

  @@map("users")
}

model Posts {
  id String @id @db.Uuid @default(uuid())
  userId String
  postId String
  content String

  @@map("posts")
}

model Followers {
  id String @id @db.Uuid @default(uuid())
  userId String
  followerId String

  @@map("followers")
}

model Likes {
  id String @id @db.Uuid @default(uuid())
  userId String
  postId String

  @@map("likes")
}

model Comments {
  id String @id @db.Uuid @default(uuid())
  userId String
  postId String
  content String

  @@map("comments")
}

O arquivo de esquema acima contém um esquema do aplicativo projetado usando uma sintaxe especial fornecida pelo Prisma. Os modelos são mapeados diretamente para tabelas no banco de dados relacional subjacente.

Uma das principais vantagens desse método é que o Prisma restringe a modelagem de dados à camada de banco de dados, diferente de outros ORMs que exigem algum nível de modelagem no nível do aplicativo. Pode haver problemas de design óbvios com o esquema acima, como nenhum relacionamento entre tabelas; isso foi feito de propósito.

Nas seções a seguir, veremos como podemos corrigir esses problemas fazendo alterações simples de esquema (adicionando colunas, alterando tipos de dados e estabelecendo relacionamentos) a alterações de esquema complexas (fazendo alterações no esquema com dados existentes no banco de dados).

Para aplicar o esquema ao banco de dados, execute este comando no diretório do terminal:

npx prisma migrate dev --name init

Quando rodamos npx prisma initanteriormente, o Prisma gerava um .envarquivo para gerenciar as variáveis ​​ambientais da aplicação. Este arquivo contém uma variável de ambiente chamada DATABASE_URL. Substitua o valor por uma URL para se conectar à sua conexão de banco de dados Postgres local (ou remota). Para referência, conectei-me ao meu banco de dados local usando isto:
postgresql://postgres:admin@localhost:5433/prisma-migration?schema=public

Quando esse comando terminar de ser executado, poderemos ver as tabelas e colunas reais existentes nesse banco de dados. O Prisma também gera uma pasta chamada migrationsno mesmo diretório do schema.prismaarquivo. Se abrirmos o arquivo de migração recém-criado, poderemos ver as instruções SQL brutas geradas automaticamente pelo Prisma. Sob o capô, o Prisma converteu o conteúdo do schema.prismaarquivo em instruções SQL brutas e aplicou isso ao banco de dados.

Vamos em frente e fazer alterações no esquema do banco de dados.

Aplicando atualizações de esquema simples

Em seguida, faremos alterações relativamente diretas no esquema e as aplicaremos.

Adicionando uma nova coluna a uma tabela existente.

Vamos supor que os requisitos de negócios evoluíram e queremos que os usuários de nosso aplicativo tenham nomes de usuário exclusivos. Para implementar essa alteração no nível do esquema, vá até o schema.prismaarquivo e adicione a linha destacada no snippet de código abaixo, e nós a analisaremos depois:

model Users {
  ...
  username String @unique
  ...
}

No snippet de código acima, adicionamos uma nova coluna à Userstabela e aplicamos uma restrição exclusiva, garantindo que vários usuários não possam ter o mesmo nome de usuário. Para aplicar essa alteração, tudo o que precisamos fazer é dizer ao Prisma para sincronizar nossas alterações com o banco de dados; podemos fazer isso facilmente executando o seguinte código no terminal:

npx prisma migrate dev --name added_username_column

Adicionando um relacionamento

Agora vamos ver uma mudança mais complexa em nosso esquema. Está claro que não há relacionamentos entre as tabelas em nosso esquema devido ao “design ruim”, então vamos ver como podemos fazer essas alterações. Vamos definir um relacionamento um-para-muitos entre a Userstabela e a Poststabela de forma que um usuário possa ter muitos posts. Mude para o esquema para implementar isso:

model Users {
  id String @id @db.Uuid @default(uuid())
  fullName String
  username String @unique
  email String @unique
  password String
  bio String
  posts Posts[]

  @@map("users")
}

model Posts {
  id String @id @db.Uuid @default(uuid())
  postId String
  content String
  user Users @relation(fields: [userId], references: [id])
  userId String // (used in the `@relation` attribute above)

  @@map("posts")
}

Mais uma vez, tudo o que precisamos fazer é executar o seguinte comando no terminal e o Prisma Migrate sincroniza automaticamente essas alterações com o banco de dados:

npx prisma migrate dev --name connect_users_posts_tables

Essa é a simplicidade de aplicar atualizações de esquema com o Prisma Migrate. No entanto, há coisas importantes a serem observadas. Depois que o Prisma aplica os arquivos de migração, eles nunca devem ser editados ou excluídos manualmente. Fazer isso pode levar a inconsistências, forçando o Prisma a fornecer um prompt solicitando a redefinição do banco de dados, o que pode levar à perda de dados. Em resumo, podemos acabar com históricos incompatíveis entre as versões dos arquivos de migração. Além disso, migrate devsó deve ser executado em ambientes de desenvolvimento, pois isso pode levar a problemas se executado em ambientes de produção.

Aplicando atualizações de esquema complexas

Nas duas seções anteriores, vimos como fazer alterações em nosso esquema e com que facilidade o Prisma faz isso. No entanto, só examinamos isso sem ter nenhum dado no banco de dados. Torna-se um pouco mais complicado quando temos dados, porque as alterações erradas podem levar à perda de dados ou inconsistências de dados no banco de dados. Nesta seção, examinaremos brevemente como aplicar alterações em nosso esquema nesses cenários.

O primeiro passo é adicionar um ou mais usuários ao aplicativo, adicionar alguns usuários manualmente ou executar o seguinte SQL:

INSERT INTO users (id, "fullName", email, password, bio, username)
VALUES ('7f93880a-a8c3-4d4f-b3b3-369aa89a73fa', 'John Doe', 'john@yahoo.com', 'johndoe', 'Im a software developer', 'johndoe');

Com isso feito, digamos mais adiante, decidimos renomear a coluna bio na tabela do usuário para biography; aparentemente podemos fazer isso simplesmente alterando o Usermodelo no prisma.schemaarquivo assim:

model Users {
  ...
  biography String
  ...
}

Agora, se tentarmos confirmar essa alteração usando o Prisma Migrate, obteremos um erro nesta linha:

Erro:⚠️Encontramos alterações que não podem ser executadas: Etapa 0 Adicionada a biografia da coluna necessária à tabela de usuários sem um valor padrão. Há 1 linha nesta tabela, não é possível executar esta etapa. Você pode usar prisma migrate dev --create-only para criar o arquivo de migração e modificá-lo manualmente para resolver os problemas subjacentes. Em seguida, execute prisma migrate dev para aplicá-lo e verificar se funciona.

Isso ocorre principalmente porque temos uma ou mais linhas na userstabela e a migração gerada pelo Prisma é potencialmente destrutiva. Para evitar isso, o Prisma solicita que criemos o arquivo de migração sem sincronizá-lo com o banco de dados imediatamente. Podemos fazer isso executando:

npx prisma migrate dev --name update-biography --create-only

Com isso, um arquivo de migração é gerado, mas não aplicado imediatamente. Vamos dar uma olhada nisso; abra o arquivo de migração e ele deve se parecer com algo assim:

ALTER TABLE "users" DROP COLUMN "bio",
ADD COLUMN "biography" TEXT NOT NULL;

Pode ser óbvio por que isso é um problema – o SQL acima descarta a biocoluna e adiciona uma nova coluna chamada biography, mas há pelo menos uma linha com um valor nessa coluna, o que significa que perderemos todos os dados nessa coluna. Assim, o Prisma lança um aviso e nos permite atualizar o arquivo manualmente.

Nesse caso, para resolver o problema, podemos atualizar ou reescrever facilmente o SQL no arquivo de migração para isso:

ALTER TABLE "users" RENAME COLUMN "bio" TO "biography";

A nova instrução SQL nos permite renomear a coluna sem perda de dados. Com isso, tudo o que precisamos fazer é dizer ao Prisma para sincronizar o arquivo de migração executando o usual:

npx prisma migrate dev

E voilà, nosso arquivo de esquema atualizado é sincronizado com o banco de dados.

Limitações do Prisma Migrate

No momento, o Prisma Migrate não oferece suporte a provedores de banco de dados MongoDB; isso é apenas uma limitação se você planeja usar o MongoDB.

Em ambientes de desenvolvimento, às vezes o Prisma Migrate pode enviar um prompt para redefinir o banco de dados; isso, infelizmente, leva à perda de dados em seu ambiente de desenvolvimento. Se tivermos arquivos de semente, isso não será um grande problema, pois o banco de dados pode ser propagado novamente com dados. É importante observar que esse prompt para redefinir o banco de dados não ocorre em ambientes de produção.

Por fim, o Prisma Migrate não permite aplicar migrações para diferentes provedores de banco de dados especificados no schema.prismaarquivo, ou seja, se criarmos um arquivo de migração para um provedor PostgreSQL em ambientes dev, não podemos aplicar essa migração para um provedor MySQL em ambientes de produção.

Conclusão

Neste artigo, passamos pelo processo de execução de migrações de esquema usando o Prisma Migrate. Fizemos isso modelando uma plataforma de mídia social simples, fazendo alterações incrementais no esquema e usando as funcionalidades fornecidas pelo Prisma Migrate para criar e aplicar uma migração automaticamente.

Fonte: https://blog.logrocket.com/effortless-database-schema-migration-prisma/

#database #prisma 

Realizar Migrações De Esquema De Banco De Dados Usando O Prisma ORM