es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Crear llamadas de API en Vite (con ViteSSR / ViteSSG)

Actualmente estoy desarrollando un sitio web de comercio electrónico simple con ViteJS. Mi proyecto utiliza:
* Vue 3
* Vite 2.4
* vite-plugin-pages, para la generación automática de rutas (estilo Nuxt)
* vite-ssg, para la generación del lado del servidor (optimización para SEO)

El alojamiento se realiza en una cuenta gratuita de Netlify (estoy abierto a cambiar a Vercel u otra si es necesario). El manejo de datos se realiza a través de Headless CMS GraphCMS, alojado en sus servidores.

Siguiendo un tutorial sobre integración de Stripe con NextJS y GraphCMS, me encontré con un problema. En NextJs puedes crear rutas de API del lado del servidor nativamente, a diferencia de Vite.

Como ViteSSG (Generación del lado del servidor) tiene la capacidad de pre-renderizar el HTML enviado, y ejecutar código del lado del servidor, me gustaría entender cómo podría crear una ruta del servidor para servir una API sin tener que pasar por la molestia de crear y alojar un backend separado solo para una llamada simple:

> POST https://localhost:3000/api/create-checkout-session

Debido a que si creo la sesión de pago de Stripe en el lado del cliente (dentro del controlador de clics payBtn), los datos (precio, etc.) podrían ser alterados por el cliente, ya que el código del controlador se serviría del lado del cliente. Quiero que esta sesión se genere del lado del servidor y que su identificador se envíe de vuelta al cliente para ser utilizado en el proceso de pago. Eliminaría la capacidad de alterar los datos.

Puedo compartir más código si es necesario y expandir la información si algo no está lo suficientemente claro. ¡Gracias por la ayuda!

/* main.js */
import 'vue-global-api'
import { ViteSSG } from 'vite-ssg'
import generatedRoutes from 'virtual:generated-pages'
import { setupLayouts } from 'virtual:generated-layouts'
import App from './App.vue'

const routes = setupLayouts(generatedRoutes)

export const createApp = ViteSSG(
  App,
  { routes },
  (ctx) => {
    // instalar todos los módulos en la carpeta <code>modules/</code> (i18n, nprogress, pwa)
    Object.values(import.meta.globEager('./modules/*.js')).map(i => i.install?.(ctx))
  }
)
/* Ruta API deseada */
import Stripe from 'stripe'
import { useAPI } from '@/utils/GraphCMS'

const graphcms = useAPI()
const stripe = new Stripe(/* STIPE API KEY*/)

export default async (req, res) => {
  const { slug } = req.body
  // obtener el producto de GraphCMS
  const result = await graphcms.getSingleProduct(slug)

  try {
    const session = await stripe.checkout.sessions.create({
      success_url: 'http://localhost:3000/?id={CHECKOUT_SESSION_ID}',
      cancel_url: `http://localhost:3000/services/${slug}`,
      // ...
    })
    res.json(session)
  } catch(e) {
    res.json({ error: {message: e }})
  }

  return
}
<!-- Llamada de API del lado del cliente -->
<script setup="">
...
/* goPay, llamado en el clic del botón => debería POSTEAR datos a la ruta de API del lado del servidor */
const goPay = async (e) => {
    e.preventDefault()
    // Crear sesión de pago
    const session = await fetch('/api/create-checkout-session', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        slug: service.slug
      })
    })

    // redirigir al pago
}
</script>

<template>
  <paybtn @click="goPay"></paybtn>
</template>
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para crear una ruta de servidor para servir una API en Vite, puede usar la API de Vite para manejar llamadas de API en el lado del servidor. Para hacer esto, puede agregar un archivo server.js en la raíz del proyecto y exportar una función que pueda aceptar la instancia de aplicación express.

    // server.js
    import Stripe from 'stripe'
    import { useAPI } from '@/utils/GraphCMS'
    
    const graphcms = useAPI()
    const stripe = new Stripe(/* STRIPE API KEY*/)
    
    export default (app) => {
      app.post('/api/create-checkout-session', async (req, res) => {
        const { slug } = req.body
        // fetch product from GraphCMS
        const result = await graphcms.getSingleProduct(slug)
    
        try {
          const session = stripe.checkout.sessions.create({
            success_url: 'http://localhost:3000/?id={CHECKOUT_SESSION_ID}',
            cancel_url: `http://localhost:3000/services/${slug}`,
            /*...*/
          })
          res.json(session)
        } catch(e) {
          res.json({ error: {message: e }})
        }
      })
    }
    

    Luego, debe importar y llamar el archivo server.js en el archivo vite.config.js.

    // vite.config.js
    import { createVuePlugin } from 'vite-plugin-vue2'
    import ViteSSG from 'vite-ssg'
    import generatedRoutes from 'virtual:generated-pages'
    import { setupLayouts } from 'virtual:generated-layouts'
    import server from './server'
    
    export default {
      plugins: [
        createVuePlugin(),
      ],
      ssgOptions: {
        script: 'async',
        ...,
      },
      optimizeDeps: {
        include: [
          'axios',
          'lodash',
        ],
      },
      server: {
        middlewareMode: 'ssr',
      },
      build: {
        outDir: 'dist',
      },
      after() {
        if (this.config.mode === 'development') {
          this.server.middlewares.use(server);
        }
      },
    }
    

    Finalmente, puede modificar la llamada de API del lado del cliente para usar la ruta de servidor recién creada /api/create-checkout-session.

    <!-- Llamado de API del lado del cliente -->
    <script setup="">
    ...
    /* goPay, llamado al hacer clic en el botón => Debería POSTEAR datos a la ruta de API en el lado del servidor */
    const goPay = async (e) => {
        e.preventDefault()
        // Create checkout session
        const session = await fetch('/api/create-checkout-session', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            slug: service.slug
          })
        })
    
        // redirect to checkout
      }
    </script>
    
    <template>
      <paybtn @click="goPay"></paybtn>
    </template>
    

    Con esta configuración, el lado del cliente no puede modificar los datos ya que la sesión de pago se genera en el lado del servidor.

Comments are closed.