Criando uma aplicação
Nessa página, vamos construir um site para a nossa livraria que consome o conteúdo que acabamos de criar no Starlight.
O Starlight te dá liberdade de construir aplicações com as linguagens e ferramentas que você preferir. Nesse exemplo, vamos criar nossa aplicação utilizando o Next.js, um framework para aplicações web simples e flexível, e o SDK do Starlight para requisitar o conteúdo que criamos.
Você pode visualizar o código completo do que criaremos nessa página acessando esse repositório do GitHub. O código do repositório foi escrito em TypeScript, mas você verá exemplos em JavaScript e TypeScript nessa página.
Se preferir, você pode simplesmente clonar o repositório acima, seguir as instruções do arquivo README.md para configurar a área de trabalho que a aplicação usa para requisitar dados, e simplesmente continuar para a página sobre deploy desse guia. Porém, recomendamos a leitura dessa página caso queira aprender como utilizar o SDK do Starlight para fazer requisições às suas APIs. A gente promete que o processo é super simples.
Criando um projeto com o Next.js
Para começar, abra seu terminal favorito, entre na pasta onde você quer criar seu projeto e rode o seguinte comando:
npx create-next-app@latest
Esse comando rodará o create-next-app
, a aplicação CLI oficial do Next.js para criar projetos. Você será apresentado
com algumas perguntas como o nome do projeto e algumas escolhas de configuração:
$ cd Projects
$ npx create-next-app@latest
Need to install the following packages:
create-next-app@latest
Ok to proceed? (y)
✔ What is your project named? … quick-start-bookstore
✔ Would you like to use TypeScript with this project? … Yes
✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use Tailwind CSS with this project? … Yes
✔ Would you like to use `src/` directory with this project? … Yes
✔ Use App Router (recommended)? … No
✔ Would you like to customize the default import alias? … No
Creating a new Next.js app in /Users/camila/Projects/quick-start-bookstore.
Configure seu projeto da maneira que preferir, mas recomendamos que você responda não para a pergunta Use App Router?, já que não utilizamos o App Router no nosso projeto de exemplo. O App Router ainda está em fase de testes, e por isso decidimos não utilizá-lo nos exemplos desse guia. Além disso, escolhemos instalar o Tailwind CSS para adicionar alguns estilos simples nos exemplos abaixo, mas isso não é obrigatório.
Com o projeto criado, precisamos apenas configurar o Next.js para que ele aceite mostrar imagens providas pelo
Starlight. Para isso, altere o arquivo next.config.js
na raíz do seu projeto para ter esse conteúdo:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'media.starlightcms.io',
},
],
},
}
module.exports = nextConfig
Finalmente, para rodar o servidor de desenvolvimento, entre na pasta da sua aplicação e utilize o seguinte comando:
- npm
- Yarn
- pnpm
npm run dev
yarn dev
pnpm run dev
Configurando o SDK do Starlight
Com o conteúdo salvo no Starlight, temos duas opções para requisitá-lo: usando as APIs do Starlight ou um SDK oficial. Os SDKs do Starlight simplificam o processo de requisição de conteúdo às suas APIs, e nós vamos utilizar o SDK para Next.js nesse exemplo. Para isso, precisamos instalá-lo:
- npm
- Yarn
- pnpm
# Não esqueça de reiniciar o servidor (npm run dev) depois da instalação.
npm install @starlightcms/next-sdk
# Não esqueça de reiniciar o servidor (yarn dev) depois da instalação.
yarn add @starlightcms/next-sdk
# Não esqueça de reiniciar o servidor (pnpm run dev) depois da instalação.
pnpm add @starlightcms/next-sdk
Após a instalação, precisamos configurar qual área de trabalho deve ser utilizada ao requisitar dados, informando ao SDK o ID dessa área de trabalho. Você pode visualizar (e copiar) o ID de uma área de trabalho ao entrar nela pela interface do Starlight. O ID fica visível abaixo do nome da área de trabalho 1, no menu de navegação à esquerda.
Com o ID em mãos, precisamos utilizar o método configure
do SDK do Starlight no arquivo que inicializa nossa
aplicação. No Next.js, esse arquivo se chama _app.jsx
(ou _app.tsx
em projetos TypeScript). Crie esse arquivo dentro
da pasta pages
do seu projeto com o seguinte conteúdo:
- JavaScript
- TypeScript
import Starlight from '@starlightcms/next-sdk'
// Remova essa linha se você escolheu não utilizar o Tailwind CSS.
import '@/styles/globals.css'
Starlight.configure({
// Substitua o ID abaixo pelo ID da sua área de trabalho.
workspace: '1234567890',
})
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
import Starlight from '@starlightcms/next-sdk'
import type { AppProps } from 'next/app'
// Remova essa linha se você escolheu não utilizar o Tailwind CSS.
import '@/styles/globals.css'
Starlight.configure({
// Substitua o ID abaixo pelo ID da sua área de trabalho.
workspace: '1234567890',
})
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
"Tipando" conteúdo no TypeScript
Caso esteja utilizando TypeScript, você pode "tipar" o conteúdo retornado pelo SDK do Starlight. Para isso, crie um arquivo chamado starlight.d.ts
dentro da pasta src
da sua aplicação com o seguinte conteúdo:
{' '}
import '@starlightcms/next-sdk'
import {
MediaField,
VisualField,
StringField,
BooleanField,
} from '@starlightcms/next-sdk'
export type Book = {
title: StringField
slug: StringField
cover_picture: MediaField
isbn: StringField
excerpt: VisualField
is_available: BooleanField
}
declare module '@starlightcms/next-sdk' {
export interface DefaultModelDefinition {
// Use o slug do modelo que você criou caso não tenha usado o slug "books".
books: Book
}
}
Agora, quando você precisar "tipar" o conteúdo de um livro em qualquer lugar da sua aplicação, você pode importar o tipo Book
desse arquivo.
E pronto! Agora você pode utilizar o SDK do Starlight no seu projeto.
Escrevendo as rotas da aplicação
Vamos criar duas páginas na nossa aplicação para mostrar o conteúdo que criamos no Starlight:
- A página inicial (
/
), que mostra a lista de livros na nossa livraria; e - A página de um livro (
/livro/slug-de-um-livro
), que mostra as informações de um livro específico.
Para isso, precisamos criar dois arquivos que representam essas páginas no nosso projeto:
- JavaScript
- TypeScript
src/pages/index.js
src/pages/livro/[slug].js
src/pages/index.tsx
src/pages/livro/[slug].tsx
No Next.js, as rotas da nossa aplicação são definidas criando arquivos com a nomeclatura que usamos acima dentro da
pasta pages
. Se quiser aprender mais sobre como esses arquivos funcionam,
dê uma olhada na documentação do roteador do Next.js.
Listagem de livros na página inicial
No Next.js, uma página é um arquivo que exporta um componente React que renderiza conteúdo. Opcionalmente, páginas também podem exportar uma função que requisita dados de APIs externas e os repassa para esse componente.
Sendo assim, vamos começar criando essa função, onde vamos usar o SDK do Starlight para requisitar nossos livros e repassarmos essa lista para o componente que criaremos no próximo passo:
- JavaScript
- TypeScript
import Starlight, { Image } from '@starlightcms/next-sdk'
import Link from 'next/link'
// Essa função rodará sempre que acessarmos a rota "/", que é a página inicial do nosso site.
export const getServerSideProps = async () => {
// Podemos requisitar conteúdo de um modelo pelo SDK utilizando apenas seu slug.
// Aqui, note que acessamos uma propriedade chamada "books", que é o slug do nosso modelo.
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.list()
// O objeto "props" abaixo será repassado pelo Next.js para o
// componente React que vamos criar na próxima etapa.
return {
props: {
// "response" é a resposta completa do Starlight, incluindo metadados.
// Nós só precisamos da lista de livros, que está na propriedade "data".
books: response.data,
}
}
}
import Starlight, { Entry, Image } from '@starlightcms/next-sdk'
import { GetServerSideProps } from 'next'
import Link from 'next/link'
import { Book } from '../starlight'
// Esse é o tipo do objeto "props" que o componente da nossa rota receberá.
// Ele também "tipa" o que a função "getServerSideProps" abaixo deve retornar.
type HomeProps = {
// "Entry" representa uma entrada de um modelo do Starlight.
// "Book" é o tipo do conteúdo que criamos no arquivo "src/starlight.d.ts".
books: Entry<Book>[]
}
// Essa função rodará sempre que acessarmos a rota "/", que é a página inicial do nosso site.
export const getServerSideProps: GetServerSideProps<HomeProps> = async () => {
// Podemos requisitar conteúdo de um modelo pelo SDK utilizando apenas seu slug.
// Aqui, note que acessamos uma propriedade chamada "books", que é o slug do nosso modelo.
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.list()
// O objeto "props" abaixo será repassado pelo Next.js para o
// componente React que vamos criar na próxima etapa.
return {
props: {
// "response" é a resposta completa do Starlight, incluindo metadados.
// Nós só precisamos da lista de livros, que está na propriedade "data".
books: response.data,
}
}
}
Agora, só precisamos adicionar o componente React que vai renderizar nossa página no mesmo arquivo:
Esse exemplo contém o código completo do arquivo. Ganhe tempo copiando e colando esse conteúdo no seu arquivo local.
- JavaScript
- TypeScript
import Starlight, { Image } from '@starlightcms/next-sdk'
import Link from 'next/link'
const Home = ({ books }) => (
// Usamos classes do Tailwind CSS para deixar o resultado final mais bonito.
// Se você não escolheu usar o Tailwind CSS, essas classes não terão nenhum efeito.
<main className="container mx-auto p-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
{books.map((book) => (
<Link
key={book.id}
href={`/livro/${book.slug}`}
className="w-full max-w-lg p-5 mx-auto bg-white rounded-lg shadow-md"
>
<article className="h-full flex flex-col items-center justify-center">
{/* Capa do livro */}
<figure className="w-32 mb-4">
{/* O componente Image renderiza uma imagem com todos os metadados (height, width e alt)
automaticamente preenchidos utilizando dados retornados pela API do Starlight. */}
<Image media={book.data.cover_picture} className="rounded"/>
</figure>
{/* Título do livro */}
<h2 className="text-xl font-bold mb-1">{book.title}</h2>
{/* Disponibilidade */}
<p className="text-gray-600 text-center">
{book.data.is_available ? 'Disponível para compra.' : 'Indisponível.'}
</p>
</article>
</Link>
))}
</main>
)
// Essa função rodará sempre que acessarmos a rota "/", que é a página inicial do nosso site.
export const getServerSideProps = async () => {
// Podemos requisitar conteúdo de um modelo pelo SDK utilizando apenas seu slug.
// Aqui, note que acessamos uma propriedade chamada "books", que é o slug do nosso modelo.
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.list()
// O objeto "props" abaixo será repassado pelo Next.js para o
// componente React que vamos criar na próxima etapa.
return {
props: {
// "response" é a resposta completa do Starlight, incluindo metadados.
// Nós só precisamos da lista de livros, que está na propriedade "data".
books: response.data,
}
}
}
export default Home
import Starlight, { Entry, Image } from '@starlightcms/next-sdk'
import { GetServerSideProps } from 'next'
import Link from 'next/link'
import { Book } from '../starlight'
// Esse é o tipo do objeto "props" que o componente da nossa rota receberá.
// Ele também "tipa" o que a função "getServerSideProps" abaixo deve retornar.
type HomeProps = {
// "Entry" representa uma entrada de um modelo do Starlight.
// "Book" é o tipo do conteúdo que criamos no arquivo "src/starlight.d.ts".
books: Entry<Book>[]
}
const Home = ({ books }: HomeProps) => (
// Usamos classes do Tailwind CSS para deixar o resultado final mais bonito.
// Se você não escolheu usar o Tailwind CSS, essas classes não terão nenhum efeito.
<main className="container mx-auto p-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
{books.map((book) => (
<Link
key={book.id}
href={`/livro/${book.slug}`}
className="w-full max-w-lg p-5 mx-auto bg-white rounded-lg shadow-md"
>
<article className="h-full flex flex-col items-center justify-center">
{/* Capa do livro */}
<figure className="w-32 mb-4">
{/* O componente Image renderiza uma imagem com todos os metadados (height, width e alt)
automaticamente preenchidos utilizando dados retornados pela API do Starlight. */}
<Image className="rounded" media={book.data.cover_picture}/>
</figure>
{/* Título do livro */}
<h2 className="text-xl font-bold mb-1">{book.title}</h2>
{/* Disponibilidade */}
<p className="text-gray-600 text-center">
{book.data.is_available ? 'Disponível para compra.' : 'Indisponível.'}
</p>
</article>
</Link>
))}
</main>
)
// Essa função rodará sempre que acessarmos a rota "/", que é a página inicial do nosso site.
export const getServerSideProps: GetServerSideProps<HomeProps> = async () => {
// Podemos requisitar conteúdo de um modelo pelo SDK utilizando apenas seu slug.
// Aqui, note que acessamos uma propriedade chamada "books", que é o slug do nosso modelo.
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.list()
// O objeto "props" abaixo será repassado pelo Next.js para o
// componente React que vamos criar na próxima etapa.
return {
props: {
// "response" é a resposta completa do Starlight, incluindo metadados.
// Nós só precisamos da lista de livros, que está na propriedade "data".
books: response.data,
}
}
}
export default Home
Note que estamos exportando o componente na última linha utilizando export default Home
. É assim que o Next.js sabe
o que renderizar quando acessamos essa rota.
E pronto! Você pode acessar a listagem de livros na sua aplicação acessando a página inicial pelo endereço http://localhost:3000.
Página de detalhes de um livro
Requisitar os dados de um livro específico usando o Starlight não é muito diferente da requisição da lista de livros que fizemos no passo anterior. Vamos criar a segunda página da nossa aplicação, começando pela requisição dos dados:
- JavaScript
- TypeScript
import Starlight, { VisualContent, Image } from '@starlightcms/next-sdk'
import Link from 'next/link'
// Essa função rodará sempre que acessarmos a rota "/livro/slug-de-um-livro".
export const getServerSideProps = async ({ params }) => {
// Agora, estamos requisitando o conteúdo de um livro com um slug específico.
// A propriedade "params.slug" é populada pelo Next.js. É por isso que nosso arquivo se chama "[slug].js".
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.get(params?.slug)
return {
props: {
// Assim como na página inicial, só precisamos do conteúdo do livro, que está na propriedade "data".
book: response.data,
}
}
}
import Starlight, { VisualContent, Image, Entry } from '@starlightcms/next-sdk'
import { GetServerSideProps } from 'next'
import Link from 'next/link'
import { Book } from '../../starlight'
type BookProps = {
book: Entry<Book>
}
// Essa função rodará sempre que acessarmos a rota "/livro/slug-de-um-livro".
export const getServerSideProps: GetServerSideProps<BookProps> = async ({ params }) => {
// Agora, estamos requisitando o conteúdo de um livro com um slug específico.
// A propriedade "params.slug" é populada pelo Next.js. É por isso que nosso arquivo se chama "[slug].tsx".
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.get(params?.slug as string)
return {
props: {
// Assim como na página inicial, só precisamos do conteúdo do livro, que está na propriedade "data".
books: response.data,
}
}
}
Note que a grande diferença é que nós estamos usando o método get()
ao invés de list()
do SDK do Starlight para
requisitar um livro em específico. Agora, só precisamos criar o componente React que renderiza a página:
Esse exemplo contém o código completo do arquivo. Ganhe tempo copiando e colando esse conteúdo no seu arquivo local.
- JavaScript
- TypeScript
import Starlight, { VisualContent, Image } from '@starlightcms/next-sdk'
import Link from 'next/link'
const Book = ({ book }) => (
<main className="container mx-auto">
<article className="flex flex-col items-center p-5 m-5 bg-white rounded-lg shadow-md">
{/* Capa do livro */}
<figure className="w-32 mb-4">
{/* O componente Image renderiza uma imagem com todos os metadados (height, width e alt)
automaticamente preenchidos utilizando dados retornados pela API do Starlight. */}
<Image media={book.data.cover_picture} className="rounded" />
</figure>
<div className="flex flex-col items-center">
{/* Título do livro */}
<h1 className="text-3xl font-bold mb-2">{book.title}</h1>
{/* Número ISBN */}
<p className="text-gray-500 mb-2">{book.data.isbn}</p>
{/* Disponibilidade */}
<p className="mb-5">{book.data.is_available ? 'Disponível para compra.' : 'Indisponível.'}</p>
{/* O campo "Resumo" do nosso livro é um campo de editor visual, que é retornado pela API
do Starlight como um objeto JSON. O componente VisualContent transforma esse JSON em HTML. */}
<VisualContent content={book.data.excerpt} />
<hr className="w-full border-1 border-gray-200 my-5"/>
<Link href="/">⬅️ Voltar à lista de livros</Link>
</div>
</article>
</main>
)
// Essa função rodará sempre que acessarmos a rota "/livro/slug-de-um-livro".
export const getServerSideProps = async ({ params }) => {
// Agora, estamos requisitando o conteúdo de um livro com um slug específico.
// A propriedade "params.slug" é populada pelo Next.js. É por isso que nosso arquivo se chama "[slug].js".
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.get(params?.slug)
return {
props: {
// Assim como na página inicial, só precisamos do conteúdo do livro, que está na propriedade "data".
book: response.data,
}
}
}
export default Book
import Starlight, { VisualContent, Image, Entry } from '@starlightcms/next-sdk'
import { GetServerSideProps } from 'next'
import Link from 'next/link'
import { Book } from '../../starlight'
type BookProps = {
book: Entry<Book>
}
const Book = ({ book }: BookProps) => (
<main className="container mx-auto">
<article className="flex flex-col items-center p-5 m-5 bg-white rounded-lg shadow-md">
{/* Capa do livro */}
<figure className="w-32 mb-4">
{/* O componente Image renderiza uma imagem com todos os metadados (height, width e alt)
automaticamente preenchidos utilizando dados retornados pela API do Starlight. */}
<Image media={book.data.cover_picture} className="rounded" />
</figure>
<div className="flex flex-col items-center">
{/* Título do livro */}
<h1 className="text-3xl font-bold mb-2">{book.title}</h1>
{/* Número ISBN */}
<p className="text-gray-500 mb-2">{book.data.isbn}</p>
{/* Disponibilidade */}
<p className="mb-5">{book.data.is_available ? 'Disponível para compra.' : 'Indisponível.'}</p>
{/* O campo "Resumo" do nosso livro é um campo de editor visual, que é retornado pela API
do Starlight como um objeto JSON. O componente VisualContent transforma esse JSON em HTML. */}
<VisualContent content={book.data.excerpt} />
<hr className="w-full border-1 border-gray-200 my-5"/>
<Link href="/">⬅️ Voltar à lista de livros</Link>
</div>
</article>
</main>
)
// Essa função rodará sempre que acessarmos a rota "/livro/slug-de-um-livro".
export const getServerSideProps: GetServerSideProps<BookProps> = async ({ params }) => {
// Agora, estamos requisitando o conteúdo de um livro com um slug específico.
// A propriedade "params.slug" é populada pelo Next.js. É por isso que nosso arquivo se chama "[slug].tsx".
// Se você utilizou outro slug no seu modelo, coloque-o no lugar de "books" na linha abaixo.
const response = await Starlight.books.entries.get(params?.slug as string)
return {
props: {
// Assim como na página inicial, só precisamos do conteúdo do livro, que está na propriedade "data".
books: response.data,
}
}
}
export default Book
E é só isso que precisamos fazer. Você pode acessar essa página ao clicar em qualquer livro na página inicial da sua aplicação.
Isso conclui o processo de criar uma aplicação simples que exibe o conteúdo que salvamos no Starlight. Se quiser, você pode criar ou editar conteúdo no Starlight para fins de teste. Para ver as mudanças na sua aplicação, basta recarregar a página no seu navegador.
Na próxima página, vamos enviar nossa aplicação para a nuvem em poucos minutos, e poderemos mostrar nossa aplicação funcionando ao vivo para qualquer pessoa na internet.