Skip to content

Commit d09dffa

Browse files
authored
Merge pull request #1 from upstash/dx-634-sqs-example
DX: 634 Add Next.js Example
2 parents 8201dfb + d2504a3 commit d09dffa

22 files changed

+435
-8
lines changed

bun.lockb

703 Bytes
Binary file not shown.

examples/nextjs14/.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

examples/nextjs14/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
# or
14+
bun dev
15+
```
16+
17+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18+
19+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20+
21+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22+
23+
## Learn More
24+
25+
To learn more about Next.js, take a look at the following resources:
26+
27+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29+
30+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31+
32+
## Deploy on Vercel
33+
34+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35+
36+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Queue } from "../../../../../src";
2+
import { Redis } from "@upstash/redis";
3+
4+
const redis = new Redis({
5+
url: "https://fit-ape-37405.upstash.io",
6+
token:
7+
"AZIdASQgYTU1NjE1MTQtYjIxNS00NDIwLWJlYTItYWYzOGNmNGYwMWE5ZDA0MDExY2UxOGFjNDI2ZGE4MzliYjQyY2ZkNTRkZjI=",
8+
});
9+
10+
type MessageBody = {
11+
message: string;
12+
};
13+
export async function POST(req: Request, res: Response) {
14+
const { queueName } = await req.json();
15+
16+
const queue = new Queue({
17+
redis: redis,
18+
concurrencyLimit: 5,
19+
queueName: queueName,
20+
});
21+
22+
const receiveResponse = await queue.receiveMessage<MessageBody>();
23+
24+
return Response.json({
25+
id: receiveResponse?.streamId,
26+
message: receiveResponse?.body.message,
27+
});
28+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Queue } from "../../../../../src";
2+
import { Redis } from "@upstash/redis";
3+
4+
const redis = new Redis({
5+
url: "https://fit-ape-37405.upstash.io",
6+
token:
7+
"AZIdASQgYTU1NjE1MTQtYjIxNS00NDIwLWJlYTItYWYzOGNmNGYwMWE5ZDA0MDExY2UxOGFjNDI2ZGE4MzliYjQyY2ZkNTRkZjI=",
8+
});
9+
10+
export async function POST(req: Request, res: Response) {
11+
const { queueName, message } = await req.json();
12+
13+
const queue = new Queue({
14+
redis: redis,
15+
concurrencyLimit: 5,
16+
queueName: queueName,
17+
});
18+
19+
const messageId = await queue.sendMessage({ message: message });
20+
21+
return Response.json({ messageId });
22+
}

examples/nextjs14/app/favicon.ico

25.3 KB
Binary file not shown.

examples/nextjs14/app/globals.css

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
.message-enter {
6+
transform: translateX(-100%);
7+
opacity: 0;
8+
}
9+
.message-enter-active {
10+
transform: translateX(0);
11+
opacity: 1;
12+
transition: transform 500ms ease-in-out, opacity 500ms ease-in-out;
13+
}
14+
.message-exit {
15+
transform: translateX(0);
16+
opacity: 1;
17+
}
18+
.message-exit-active {
19+
transform: translateX(100%);
20+
opacity: 0;
21+
transition: transform 500ms ease-in-out, opacity 500ms ease-in-out;
22+
}

examples/nextjs14/app/layout.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Metadata } from "next";
2+
import { Inter } from "next/font/google";
3+
import "./globals.css";
4+
5+
const inter = Inter({ subsets: ["latin"] });
6+
7+
export const metadata: Metadata = {
8+
title: "Create Next App",
9+
description: "Generated by create next app",
10+
};
11+
12+
export default function RootLayout({
13+
children,
14+
}: Readonly<{
15+
children: React.ReactNode;
16+
}>) {
17+
return (
18+
<html lang="en">
19+
<body className={`${inter.className}`}>
20+
<div className="w-full max-w-3xl px-6 mx-auto py-14 ">{children}</div>
21+
</body>
22+
</html>
23+
);
24+
}

examples/nextjs14/app/page.tsx

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"use client";
2+
import React, { useState, useEffect } from "react";
3+
4+
import { Queue } from "./../../../src";
5+
import * as uuid from "uuid";
6+
import { Redis } from "@upstash/redis";
7+
import { TransitionGroup, CSSTransition } from "react-transition-group";
8+
9+
type MessageBody = {
10+
content: string;
11+
};
12+
13+
type MessageWithId = {
14+
id: string;
15+
body: MessageBody;
16+
};
17+
18+
const queueName = `sqs-${uuid.v4().substring(0, 18)}`;
19+
20+
export default function Home() {
21+
const [messageInput, setMessageInput] = useState<string>(
22+
uuid.v4().substring(0, 18)
23+
);
24+
const [queueMessages, setQueueMessages] = useState<MessageWithId[]>([]);
25+
26+
const send = async () => {
27+
fetch("/api/send", {
28+
method: "POST",
29+
body: JSON.stringify({ message: messageInput, queueName: queueName }),
30+
}).then(async (res) => {
31+
const data = await res.json();
32+
if (data.messageId) {
33+
setQueueMessages([
34+
...queueMessages,
35+
{
36+
id: data.messageId,
37+
body: { content: messageInput },
38+
} as MessageWithId,
39+
]);
40+
}
41+
});
42+
};
43+
44+
const receive = async () => {
45+
fetch("/api/receive", {
46+
method: "POST",
47+
body: JSON.stringify({ queueName: queueName }),
48+
}).then(async (res) => {
49+
const data = await res.json();
50+
51+
if (data.id && data.message) {
52+
setQueueMessages(queueMessages.filter((msg) => msg.id !== data.id));
53+
}
54+
});
55+
};
56+
return (
57+
<main>
58+
<header>
59+
<h1 className="text-4xl font-bold">
60+
Welcome to <span className="text-primary-500">@upstash/sqs</span>
61+
</h1>
62+
63+
<p className="mt-4">
64+
This is an example of how to use @upstash/sqs as a FIFO queue in your
65+
Next.js application.
66+
</p>
67+
68+
<p className="mt-4">
69+
You can create and consume messages from the queue using the buttons.
70+
</p>
71+
</header>
72+
73+
<hr className="my-10" />
74+
<div className="h-[55vh]">
75+
<div className="p-4 bg-zinc-100 flex flex-col gap-2 rounded-xl w-96 mx-auto max-h-full overflow-y-scroll">
76+
<p className="text-lg font-bold">Queue</p>
77+
<TransitionGroup className="flex flex-col gap-2 h-min">
78+
{queueMessages.length > 0 &&
79+
queueMessages.map((message, index) => {
80+
return (
81+
<CSSTransition
82+
key={message.id}
83+
timeout={250}
84+
classNames="message"
85+
>
86+
<div className="flex flex-row items-center justify-between gap-4 w-full transform transition-all duration-500 ease-in-out">
87+
<div className="bg-white px-2 h-full rounded-xl w-10 flex items-center justify-center text-sm">
88+
{index}
89+
</div>
90+
91+
<div className="bg-white p-4 rounded-xl text-slate-900 shadow-sm flex flex-col gap-2 w-80">
92+
<p className="font-semibold text-sm">
93+
ID:{" "}
94+
<span className="text-zinc-400 font-normal">
95+
{message.id}
96+
</span>
97+
</p>
98+
99+
<p className="font-semibold">
100+
Content:{" "}
101+
<span className="text-zinc-800 font-normal">
102+
{message.body.content}
103+
</span>
104+
</p>
105+
</div>
106+
</div>
107+
</CSSTransition>
108+
);
109+
})}
110+
</TransitionGroup>
111+
{queueMessages.length === 0 && <p>No messages in the queue...</p>}
112+
</div>
113+
</div>
114+
<hr className="my-10" />
115+
116+
<div className="flex flex-row gap-6 items-end w-full">
117+
<div className="flex flex-col w-full gap-2">
118+
<div className="w-full flex flex-row justify-between gap-2">
119+
<input
120+
type="text"
121+
className=" shadow-sm border border-zinc-400 rounded-xl px-4 py-2 w-full"
122+
placeholder="Enter message..."
123+
value={messageInput}
124+
onChange={(e) => setMessageInput(e.target.value)}
125+
/>
126+
<button
127+
className="bg-zinc-400 text-white px-4 py-2 rounded-xl hover:bg-zinc-300"
128+
onClick={() => {
129+
const randomWord = uuid.v4().substring(0, 18);
130+
setMessageInput(randomWord);
131+
}}
132+
>
133+
Random
134+
</button>
135+
</div>
136+
<div className="flex justify-center w-full gap-2">
137+
<button
138+
className="bg-emerald-500 text-white px-4 py-2 rounded-xl hover:bg-emerald-400 transition-all ease-in-out w-full"
139+
onClick={() => {
140+
send();
141+
}}
142+
>
143+
Produce
144+
</button>
145+
</div>
146+
</div>
147+
<button
148+
className="bg-blue-500 text-white px-4 py-2 rounded-xl hover:bg-blue-400 transition-all ease-in-out w-full h-1/2"
149+
onClick={() => {
150+
receive();
151+
}}
152+
>
153+
Consume
154+
</button>
155+
</div>
156+
</main>
157+
);
158+
}

examples/nextjs14/bun.lockb

58.1 KB
Binary file not shown.

examples/nextjs14/next.config.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {};
3+
4+
export default nextConfig;

examples/nextjs14/package.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "nextjs14",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint"
10+
},
11+
"dependencies": {
12+
"@types/react-transition-group": "^4.4.10",
13+
"next": "14.1.0",
14+
"react": "^18",
15+
"react-dom": "^18",
16+
"react-transition-group": "^4.4.5"
17+
},
18+
"devDependencies": {
19+
"typescript": "^5",
20+
"@types/node": "^20",
21+
"@types/react": "^18",
22+
"@types/react-dom": "^18",
23+
"autoprefixer": "^10.0.1",
24+
"postcss": "^8",
25+
"tailwindcss": "^3.3.0"
26+
}
27+
}

examples/nextjs14/postcss.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
};

examples/nextjs14/public/next.svg

+1
Loading

examples/nextjs14/public/vercel.svg

+1
Loading

examples/nextjs14/tailwind.config.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Config } from "tailwindcss";
2+
3+
const config: Config = {
4+
content: [
5+
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
6+
"./components/**/*.{js,ts,jsx,tsx,mdx}",
7+
"./app/**/*.{js,ts,jsx,tsx,mdx}",
8+
],
9+
theme: {
10+
extend: {
11+
backgroundImage: {
12+
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
13+
"gradient-conic":
14+
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
15+
},
16+
},
17+
},
18+
plugins: [],
19+
};
20+
export default config;

0 commit comments

Comments
 (0)