4º. 1er cuatrimestre. Itinerario de Sistemas de la Información. Grado en Ingeniería Informática. ULL
import Head from "next/head"
import styles from "./index.module.css"
<link rel="icon" href="/dog.png" />
<main className={styles.main}>
Curly Braces in JSX<form onSubmit={onSubmit}> ...</form>
const response = await fetch("/api/generate", { ... })
In this lab we will build a “pet name generator web app” wich is described in the
You can see a solution deployed at netlify: https://nextjs-oai.netlify.app/
The code uses the Next.js framework with React.
Next.js is a framework on top of React that handles the tooling and configuration needed for React, and provides additional structure, features, and optimizations for your application.
Read both this tutorial and the original OpenAI API quickstart tutorial.
Create an account at openai api. Unfortunately, at some point is going to ask you for a phone number:
It does not provide any alternatives. Let me know in advance if this is a problem for you.
Go to the user on the upper right corner and click on the API Keys tab.
Choose View API Keys.
Click on the “Create new secret Key” button and copy the key.
Make requests to the OpenAI API using a client like the Thunder Client or Postman or Insomnia.
See https://beta.openai.com/docs/api-reference/
Here is the file tree hierarchy of the project:
✗ tree -I node_modules
.
├── README.md
├── docs
│ └── images
│ ├── generate-api-key.png
│ ├── local-app-runnning.png
│ └── menu-1.png
├── package-lock.json
├── package.json
├── pages
│ ├── api
│ │ └── generate.js
│ ├── index.js
│ └── index.module.css
└── public
└── dog.png
5 directories, 10 files
Proceed as follows:
Starting with the assignment repo, install the requirements
$ npm install
Which installs the dependencies listed in package.json
:
➜ openai-quickstart-node git:(main) ✗ jq '.dependencies' package.json
{
"next": "^12.1.6",
"openai": "^3.0.0",
"react": "17.0.2",
"react-dom": "17.0.2"
}
Notice that the next
version major is 12
Make a copy of the example environment variables file
$ cp .env.example .env
Add your API key to the newly created .env
file
Run the app
$ npm run dev
The console shows:
➜ openai-quickstart-node git:(master) ✗ npm run dev
> openai-quickstart-node@0.1.0 dev
> next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info - Loaded env from /Users/casianorodriguezleon/campus-virtual/2223/learning/openai-learning/openai-quickstart-node/.env
wait - compiling...
event - compiled client and server successfully in 1174 ms (113 modules)
You should now be able to access the app at http://localhost:3000!
For the context behind this example app, check out the Open AI tutorial.
Single Page Applications (SPAs) and Multi-Page Applications (MPAs) are architectural patterns for building web applications.
SPAs are reactive web applications that give a native look and feel without page loads. SPAs owe this to AJAX+Client-Side Rendering typically provided by a client-side framework such as react/vue/angular.
AJAX stands for Asynchronous JavaScript And XML. It is the use of the XMLHttpRequest object to communicate with servers. It can send and receive information in various formats, including JSON, XML, HTML, and text files.
Many in the industry refer to more traditional web applications as Multi-Page Applications (MPAs). Such applications are comprised of multiple webpages that the user navigates between.
Rendering is the conversion of the source code written in a reactive framework (React/Vue/AngularJS) into the HTML representation of your User Interface. Rendering can take place
It can happen either
With Next.js, three types of rendering methods are available:
Server-Side Rendering,
On the client, the HTML is used to show a fast non-interactive page, while React uses the JSON data and JavaScript instructions to make components interactive. This process is called hydration.
Client-Side Rendering.
Server-Side Rendering and Static Site Generation are also referred to as Pre-Rendering because the fetching of external data and transformation of components into HTML happens before the result is sent to the client.
The origin server refers to the main computer that stores and runs the original version of your application code. When an origin server receives a request, it does some computation before sending a response. The result of this computation work can be moved to a CDN (Content Delivery Network).
CDNs store static content (such as HTML and image files) in multiple locations around the world and are placed between the client and the origin server. When a new request comes in, the closest CDN location to the user can respond with the cached result.
Edge servers are distributed to multiple locations around the world. Unlike CDNs, which store static content, Edge servers can run small snippets of code. Edge computing supports not only transmitting cached data but other types of computing like live streaming and gaming.
In Next.js, a page is a
.js
, .jsx
, .ts
, or .tsx
file andpages
directory.Each page is associated with a route based on its file name.
The index.js
file is the main page /
for the app.
Pages can also be added under src/pages
as an alternative to the root pages
directory.
The src
directory is very common in many apps and Next.js supports it by default.
Example: If you create pages/about.js
that exports a React component like below, it will be accessible at /about
.
export default function About() {
return <div>About</div>
}
See https://nextjs.org/docs/basic-features/pages
Next.js also supports pages with dynamic routes.
For example, if you create a file called pages/posts/[id].js
, then it will be accessible at posts/1
, posts/2
, etc.
See the full code of the examples used in this section in the repo ULL-MII-SYTWS/nextjs-dynamic-routes
➜ nextjs-dynamic-routes tree -I node_modules
.
├── README.md
├── package-lock.json
├── package.json
└── pages
├── index.jsx
└── post
├── [pid]
│ └── [comment].js
└── [pid].js
Consider the following page pages/post/[pid].js
:
import { useRouter } from 'next/router'
const Post = () => {
const router = useRouter()
console.log(router.query)
const { pid } = router.query
return <p>Post: {pid}</p>
}
export default Post
Any route like /post/1
, /post/abc
, etc. will be matched by pages /post/[pid].js
.
The matched path
parameter will be sent as a query parameter to the page, and it will be merged with the other query parameters.
For example, The page pages/post/[pid]/[comment].js
import { useRouter } from 'next/router'
const Comment = () => {
const router = useRouter()
console.log(router.query)
const { pid, comment } = router.query
return <p>pid: {pid} Comment: {comment}</p>
}
export default Comment
will match the route /post/abc/a-comment
and its query object will be:
{ "pid": "abc", "comment": "a-comment" }
➜ openai-quickstart-node git:(main) ✗ npx next --version
Next.js v12.1.6
Client-side navigations to dynamic routes are handled with next/link. If we wanted to have links to the routes used above it will look like this:
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/post/abc">Go to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc?foo=bar">Also goes to pages/post/[pid].js</Link>
</li>
<li>
<Link href="/post/abc/a-comment">
Go to pages/post/[pid]/[comment].js
</Link>
</li>
</ul>
)
}
export default Home
NextJS provides through next/link
a React component called Link
to do client-side route transitions.
See Introduction to Routing in the NextJS docs.
See the code at ULL-MII-SYTWS/nextjs-dynamic-routes
Follow the tutorial Dynamic Routes at https://nextjs.org/learn/basics/dynamic-routes and solve the exercises.
Any file inside the folder pages/api
is mapped to /api/*
and will be treated as an API endpoint instead of a page.
They are server-side only bundles and won’t increase your client-side bundle size. See the section API Routes introduction.
Each page is associated with a route based on its file name.
Since we have the file pages/api/generate.js
, Next.js will make it accessible at the route /api/generate
.
Create a file called hello.js
in pages/api
with the following code:
export default function handler(req, res) {
res.status(200).json({ text: 'Hello' });
}
Try accessing it at http://localhost:3000/api/hello. You should see {"text":"Hello"}
. Note that:
req
is an instance of http.IncomingMessage, plus some pre-built middlewares.res
is an instance of http.ServerResponse, plus some helper functions.Functions exported by files inside the folder pages/api
can be deployed as Serverless Functions (also known as Lambdas).
API Routes can be dynamic, just like regular pages.
A serverless function is a piece of code that is executed in response to a specific event, such as an HTTP request or a message being added to a queue. It is called “serverless” because the developer does not have to worry about the underlying infrastructure that the code will be running on. Instead, the infrastructure is managed by a third party, such as AWS Lambda or Google Cloud Functions.
The developer only needs to provide the code and specify the events that should trigger the code to be executed. The serverless provider takes care of the rest, including automatically scaling the function to handle a large number of requests.
See https://www.npmjs.com/package/openai. Check out the full API documentation for examples of all the available functions in openai
.
import { Configuration, OpenAIApi } from "openai";
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
export default async function (req, res) {
const completion = await openai.createCompletion({
model: "text-davinci-002",
prompt: generatePrompt(req.body.animal),
temperature: 0.6,
});
res.status(200).json({ result: completion.data.choices[0].text });
}
function generatePrompt(animal) {
const capitalizedAnimal =
animal[0].toUpperCase() + animal.slice(1).toLowerCase();
return `Suggest three names for an animal that is a superhero.
Animal: Cat
Names: Captain Sharpclaw, Agent Fluffball, The Incredible Feline
Animal: Dog
Names: Ruff the Protector, Wonder Canine, Sir Barks-a-Lot
Animal: ${capitalizedAnimal}
Names:`;
}
For a Nextjs API route to work, you need to export a function as default
(a.k.a request handler), which then receives the following parameters:
req
: An instance of http.IncomingMessage
, plus some pre-built middlewaresres
: An instance of http.ServerResponse
, plus some helper functionsprocess.env.OPENAI_API_KEY
Next.js allows you to set environment variables in
.env
(all environments),.env.development
(development environment), and.env.production
(production environment)..env.local
always overrides the defaults set.The variables are accesible into process.env
.
By default environment variables are only available in the Node.js environment, meaning they won’t be exposed to the browser.
In order to expose a variable to the browser you have to prefix the variable with NEXT_PUBLIC_
.
When deploying your Next.js application to Vercel, Environment Variables can be configured in the Project Settings.
In Netlify you can use the Netlify UI. Head over to the Build & Deploy settings in your Site Settings, and then plug your values in under “Environment variables” or alternatively, use the Netlify CLI
export default async function (req, res) { ... }
The Server Request Object (req
) includes a set of
Express.js-like helper methods to improve the developer experience and increase the speed of creating new API endpoints:
req.cookies
- An object containing the cookies sent by the request. Defaults to {}
req.query
- An object containing the query string. Defaults to {}
req.body
- An object containing the body
parsed by content-type
, or null
if no body was sent
See the code fragment generatePrompt(req.body.animal)
The Server Response object, ( abbreviated as res
) includes a set of
Express.js-like helper methods to improve the developer experience and increase the speed of creating new API endpoints:
res.status(code)
- A function to set the status code. code must be a valid HTTP status coderes.json(body)
- Sends a JSON response. body must be a serializable objectres.send(body)
- Sends the HTTP response. body can be a string, an object or a Buffer
See the code fragment res.status(200).json({ result: completion.data.choices[0].text });
res.redirect([status,] path)
- Redirects to a specified path
or URL. status
must be a valid HTTP status code.
status
defaults to “307” “Temporary redirect”.path
starts with “http” or “//”, the redirect is external.const completion = await openai.createCompletion({ ... })
See the documentation at https://beta.openai.com/docs/api-reference/completions/create
It makes a POST request to https://api.openai.com/v1/completions:
In the JSON body goes:
model
: ID
of the model to use. You can use the List models API to see all of your available modelsprompt
: string to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.temperature
: Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 for ones with a well-defined answer.The response is a JSON object with the following fields:
{
"id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
"object": "text_completion",
"created": 1589478378,
"model": "text-davinci-003",
"choices": [
{
"text": "\n\nThis is indeed a test",
"index": 0,
"logprobs": null,
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": 5,
"completion_tokens": 7,
"total_tokens": 12
}
}
The index.js
file inside the pages
directory is the page that is rendered when the user visits the root of your application.
It exports a React component that renders the home page.
There are three core concepts of React that you’ll need to be familiar with to start building React applications. These are:
In Next.js, a page is a React Component exported from a .js, .jsx, .ts, or .tsx file in the pages
directory.
Each page is associated with a route based on its file name.
thus, index.js
is a page and index.module.css
is a module.
import Head from "next/head";
import { useState } from "react";
import styles from "./index.module.css";
export default function Home() {
const [animalInput, setAnimalInput] = useState("");
const [result, setResult] = useState();
async function onSubmit(event) {
event.preventDefault();
const response = await fetch("/api/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ animal: animalInput }),
});
const data = await response.json();
setResult(data.result);
setAnimalInput("");
}
return (
<div>
<Head>
<title>OpenAI Quickstart</title>
<link rel="icon" href="/dog.png" />
</Head>
<main className={styles.main}>
<img src="/dog.png" className={styles.icon} />
<h3>Name my pet</h3>
<form onSubmit={onSubmit}>
<input
type="text"
name="animal"
placeholder="Enter an animal"
value={animalInput}
onChange={(e) => setAnimalInput(e.target.value)}
/>
<input type="submit" value="Generate names" />
</form>
<div className={styles.result}>{result}</div>
</main>
</div>
);
}
JSX stands for JavaScript eXtended markup language. JSX is a syntax extension for JavaScript that allows you to describe your UI in a familiar HTML-like syntax. This code is JSX:
(
<div>
<Head>
<title>OpenAI Quickstart</title>
<link rel="icon" href="/dog.png" />
</Head>
<main className={styles.main}>
<img src="/dog.png" className={styles.icon} />
<h3>Name my pet</h3>
<form onSubmit={onSubmit}>
<input
type="text"
name="animal"
placeholder="Enter an animal"
value={animalInput}
onChange={(e) => setAnimalInput(e.target.value)}
/>
<input type="submit" value="Generate names" />
</form>
<div className={styles.result}>{result}</div>
</main>
</div>
);
It’s possible to use JavaScript inside JSX using curly braces.
The example below sets the value
property of the input
to the variable animalInput
and the onChange
property to a function that updates the animalInput
variable to the value written by the user inside the input box:
<input type="text" name="animal" placeholder="Enter an animal"
value={animalInput}
onChange={(e) => setAnimalInput(e.target.value)}
/>
The nice thing about JSX is that apart from following three JSX rules, you don’t need to learn any new symbols or syntax outside of HTML and JavaScript:
<img>
must become <img />
stroke-width
you use strokeWidth
. Since class
is a reserved word, in React you write className
instead. Event names are also camelCasedimport Head from "next/head"
nextjs provides a built-in component for appending elements to the head
of the page so that it can
be used in the JSX of the page:
<Head>
<title>OpenAI Quickstart</title>
<link rel="icon" href="/dog.png" />
</Head>
import styles from "./index.module.css"
This is a CSS module.
Next.js supports CSS Modules using the [name].module.css
file naming convention.
CSS Modules locally scope CSS by automatically creating a unique class name.
This allows you to use the same CSS class name in different files without worrying about collisions.
We can then use the styles
object like in:
<img src="/dog.png" className={styles.icon} />
or in this:
<div className={styles.result}>{result}</div>
<link rel="icon" href="/dog.png" />
This line in the <Head>
component adds a favicon to the page.
You’ll find the image in the public
directory.
Next.js can serve static files, like images, under a folder called public
in the root directory.
Files inside public
can then be referenced by your code starting from the base URL (/
).
<main className={styles.main}>
Curly Braces in JSXJSX lets you write HTML-like markup inside a JavaScript file, keeping rendering logic and content in the same place.
Sometimes you will want to add a little JavaScript logic or reference a dynamic property inside that markup.
In this situation, you can use curly braces in your JSX to open a window to JavaScript: <main className={styles.main}>
The only reason behind the fact that JSX uses className
over class
is that the class
is a reserved keyword in JavaScript.
We are specifying that the <main>
element should use the main
class from the index.module.css
file.
<form onSubmit={onSubmit}> ...</form>
The onSubmit
prop is a special React prop that lets you specify a function that will be called when the form is submitted.
The async function onSubmit
is called when the form is submitted:
async function onSubmit(event) {
event.preventDefault();
const response = await fetch("/api/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ animal: animalInput }),
});
const data = await response.json();
setResult(data.result);
setAnimalInput("");
}
const response = await fetch("/api/generate", { ... })
The fetch
function makes a request to the /api/generate
endpoint of the nextjs server.
Thus, the exported function in pages/api/generate.js
is called and the JSON returned at line
res.status(200).json({ result: completion.data.choices[0].text });
will be received in data
after the second await
:
const data = await response.json()
React states are used to store data that can be changed over time.
In that sense, they are similar to variables declared with the let
keyword.
The difference between a React state and a normal variable is that when a React state variable changes, the component is rendered again and some other things happens, but when a normal variable changes, this does not happen.
The useState
hook allows us to create state variables for our components.
const [state, setState] = useState(initialState);
useState
takes in an initial value as an argument and returns an array containing
setState
to mutate itDuring the initial render, the returned state (state
) is the same as the value passed as the first argument (initialState
).
The setState
function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
setState(newState);
During subsequent re-renders, the first value returned by useState
will always be the most recent state after applying updates.
It is common practice to de-structure this array and set its contents to be const
.
This is because
The setter
function accepts either
setState
. The function will receive the previous value, and return an updated value. Here’s an example of a counter component that uses both forms of setState:function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
In our code we have two states: animalInput
and result
.
Therefore,
animalInput
state changes, the component is rendered again.
animalInput
changes when the user types in the input field and when the API returns the result.result
state changes, the component is rendered again.
result
changes when the API returns the result.Let’s review the code fragment:
async function onSubmit(event) {
// ...
const data = await response.json();
setResult(data.result);
setAnimalInput("");
}
Since data
has arrived from the API, we can set the result
state to the value of data.result
:
setResult(data.result); // data just arrived from the API
The component will be rendered showing the new result
.
Also, the effect of
setAnimalInput("");
is that the input component
<input type="text" name="animal" placeholder="Enter an animal"
value={animalInput}
onChange={(e) => setAnimalInput(e.target.value)}
/>
will be rendered again with an empty value.
To understand the line onChange={(e) => setAnimalInput(e.target.value)
we need to know
onchange
event occurs when the value of the <input>
has been changed.target
property of the Event e
is a reference to the object onto which the event was dispatched, that is, the <input>
.setAnimalInput(e.target.value)
will be executed each time the user types a character in the input
field and will change the value of the animalInput
state.Check out these other React topics:
useEffect()
A web application code must be in one of these different stages:
The build stage is the stage where the code is compiled and optimized for production. Among other thins the JSX code is converted to JavaScript.
Another task is to minify the code. Minification refers to the process of removing unnecessary or redundant data without affecting how the resource is processed by the browser
and so on. The goal is to reduce the size of the code and improve the performance of the application.
Another stage of the Next.js compiler is Bundling. Bundling is the process of resolving the web of dependencies and merging (or ‘packaging’) the files (or modules) into optimized bundles for the browser, with the goal of reducing the number of requests for files when a user visits a web page:
Developers usually split their applications into multiple pages that can be accessed from different URLs. Each of these pages becomes a unique entry point into the application.
Code-splitting is the process of splitting the application’s bundle into smaller chunks required by each entry point. The goal is to improve the application’s initial load time by only loading the code required to run that page.
Next.js has built-in support for code splitting. Each file inside your pages/
directory will be automatically code split into its own JavaScript bundle during the build step.
Other optimizations:
The command next build
creates an optimized production build of your application.
You can enable more verbose build output with the --debug
flag in next build.
The output displays information about each route:
➜✗ npx next build --debug
info - Loaded env from /Users/casianorodriguezleon/campus-virtual/2223/learning/openai-learning/openai-quickstart-node/.env
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (3/3)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 998 B 73.8 kB
├ └ css/fc2c832f265f4111.css 522 B
├ ○ /404 193 B 73 kB
└ λ /api/generate 0 B 72.8 kB
+ First Load JS shared by all 72.8 kB
├ chunks/framework-e70c6273bfe3f237.js 42 kB
├ chunks/main-f65e66e62fc5ca80.js 28.6 kB
├ chunks/pages/_app-02d0f4839caa4a8e.js 1.36 kB
└ chunks/webpack-69bfa6990bb9e155.js 769 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
Redirects
┌ source: /:path+/
├ destination: /:path+
└ permanent: true
This generates the site in the .next
directory.
next start
starts the application in production mode.
The application should be compiled with next build
before starting.
npx next start -p 4000
ready - started server on 0.0.0.0:4000, url: http://localhost:4000
info - Loaded env from /Users/casianorodriguezleon/campus-virtual/2223/learning/openai-learning/openai-quickstart-node/.env
Netlify’s Next.js Runtime configures your site on Netlify to enable key Next.js functionality.
It automatically generates serverless functions that handle
See section Next.js on Netlify for more details.
Regarding prices and restrictions on deployment you can check the FAQ Organization-owned private repository FAQ
In the Netlify UI, choose new deploy and import your GitHub repo to Netlify
Click on show advanced. Go to Environment Variables and add the secret:
Finally, click on deploy site and after a while your site will be deployed and running:
Environment variables are set and securely stored on Netlify and provided to the Next.JS server. This means we can avoid committing any sensitive values to our repository.
There are three ways to create site environment variables:
env:set
to create a site environment variable, and env:import
to import from a .env
file.createEnvVars
to create a new site environment variable.The Netlify UI reflects any changes made using the CLI (ntl) or the API and vice versa.
The Netlify CLI is a command line interface that allows you to manage your Netlify sites from the terminal.
You can install it with npm:
➜ explorers-up-and-running-with-serverless-functions git:(main) npm install netlify-cli -g
added 1438 packages, and audited 1439 packages in 26s
➜ explorers-up-and-running-with-serverless-functions git:(main) npm ls -g netlify-cli
/Users/casianorodriguezleon/.nvm/versions/node/v16.0.0/lib
└── netlify-cli@12.2.8
➜ explorers-up-and-running-with-serverless-functions git:(main) ✗ ntl --version
netlify-cli/12.2.8 darwin-x64 node-v16.0.0
➜ explorers-up-and-running-with-serverless-functions git:(main) ✗ node --version
v16.0.0
The first step is to link the repo to Netlify. Here we are using the --gitRemoteName
option to specify the remote name sytws
of the repo.
➜ nextjs-solution git:(main) ✗ ntl link --gitRemoteName sytws
netlify link will connect this folder to a site on Netlify
? How do you want to link this folder to a site? Use current git remote origin (https://github.com/ULL-MII-SYTWS/nextjs-solution)
Looking for sites connected to 'https://github.com/ULL-MII-SYTWS/nextjs-solution'...
Directory Linked
Admin url: https://app.netlify.com/sites/nextjs-oai
Site url: https://nextjs-oai.netlify.app
You can now run other `netlify` cli commands in this directory
Once we have linked the repo to Netlify, we can check the status:
➜ nextjs-solution git:(main) ✗ ntl status
──────────────────────┐
Current Netlify User │
──────────────────────┘
Name: Casiano Rodriguez-Leon
Email: crguezl@ull.edu.es
GitHub: crguezl
Teams:
Casiano Rodriguez-Leon's team: Collaborator
────────────────────┐
Netlify Site Info │
────────────────────┘
Current site: nextjs-oai
Admin URL: https://app.netlify.com/sites/nextjs-oai
Site URL: https://nextjs-oai.netlify.app
Site Id: blah-blah-blah-blah-blah
We can use ntl env:<subcommand>
syntax to list
, set
or get
environment variables:
➜ nextjs-solution git:(main) ✗ ntl env:list
1 environment variable for site nextjs-oai
.---------------------------------------------------------------------.
| Environment variables |
|---------------------------------------------------------------------|
| Key | Value |
|----------------|----------------------------------------------------|
| OPENAI_API_KEY | ************************************************** |
'---------------------------------------------------------------------'
We can see the site locally using the dev
command of ntl
:
openai-quickstart-node git:(main) ✗ ntl dev
Since ntl
knows about Next.js, it will choose a suitable default configuration, it will create a .netlify
folder:
✗ tree -I node_modules .netlify
.netlify
├── edge-functions
│ ├── edge-shared
│ │ ├── next-utils.ts
│ │ ├── nextConfig.json
│ │ └── utils.ts
│ ├── manifest.json
│ └── next-dev
│ └── index.js
├── edge-functions-import-map.json
├── plugins
│ ├── package-lock.json
│ └── package.json
└── state.json
and it will start the Next.js server injecting the environment variables .
We can change the settings adding a netlify.toml configuration file.
[[plugins]]
package = "@netlify/plugin-nextjs"
[build]
command = "npm run build"
publish = ".next"
Netlify deploys are atomic, and your site is never in an inconsistent state while you’re uploading a new deploy.
With File Transfer Protocol (FTP) or Amazon Simple Storage Service (S3) uploads, each file is pushed live one after the other, so you can get into situations where a new HTML page is live before the supporting assets (images, scripts, CSS) have been uploaded. And if your connection cuts out in the middle of an upload, your site could get stuck in a broken state for a long time.
Here is an example of a deploy:
➜ openai-quickstart-node git:(main) ✗ ntl deploy --prod
Deploy path: /Users/casianorodriguezleon/campus-virtual/2223/learning/openai-learning/openai-quickstart-node/.next
Deploying to main site URL...
✔ Finished hashing
✔ CDN requesting 27 files
✔ Finished uploading 27 assets
✔ Deploy is live!
Logs: https://app.netlify.com/sites/nextjs-oai/deploys/6395e63207648c16d41001b7
Unique Deploy URL: https://6395e63207648c16d41001b7--nextjs-oai.netlify.app
Website URL: https://nextjs-oai.netlify.app
The --prod
option deploys to production, the --open
flag opens the site after deploy
Follow the instructions at the tutorial Deploying Your Next.js App
If you have trouble with this or other Next.js steps use GitHub Discussions: next.js
You can also use the Vercel CLI to interact with and configure your projects, enabling you to retrieve logs, manage certificates, replicate your deployment environment locally, manage Domain Name System (DNS) records, etc. It is a wrapper around the Vercel API.
Here is an example of use:
➜ nextjs-blog git:(main) vercel login
Vercel CLI 28.10.1
> Log in to Vercel github
> Success! GitHub authentication complete for crguezl@ull.edu.es
Congratulations! You are now logged in. In order to deploy something, run `vercel`.
💡 Connect your Git Repositories to deploy every branch push automatically (https://vercel.link/git).
➜ nextjs-blog git:(main) vercel ls
Vercel CLI 28.10.1
? Set up “~/campus-virtual/2223/learning/nextjs-learning/nextjs-tutorial/nextjs-blog”? [Y/n] y
? Which scope should contain your project? crguezl
? Found project “crguezl/nextjs-blog”. Link to it? [Y/n] y
🔗 Linked to crguezl/nextjs-blog (created .vercel and added it to .gitignore)
> Deployments for nextjs-blog under crguezl [645ms]
> To list deployments for a project, run `vercel ls [project]`.
Age Deployment Status Duration
22m https://nextjs-blog-sfbadq2kr-crguezl.vercel.app ● Ready 38s
Watch the video Vercel - Create a Next.js App and Deploy with Vercel CLI:
Add routes to the app:
/pet
leave the current service/image
add functionality to get an image from OpenAI and display it in the page.The youtube video Build An AI Image Generator With OpenAI & Node.js can help: