Building a To-Do List Application with Ion SST and Next.js
Lets go
In this tutorial, we'll walk through the process of creating a simple yet functional To-Do List application using Ion SST and Next.js. This project will demonstrate how to set up a serverless backend with a DynamoDB database and connect it to a Next.js frontend.
Prerequisites
Before we begin, make sure you have the following installed:
Node.js (v14 or later)
npm or yarn
AWS CLI configured with your credentials
Step 1: Set up the project
First, let's create a new Next.js project and add SST:
npx create-next-app@latest todo-sst
cd todo-sst
npm install sst @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
Step 2: Configure SST
Create an sst.config.ts
file in the root of your project:
import { SSTConfig } from "sst";
import { NextjsSite, Table } from "sst/constructs";
export default {
config(_input) {
return {
name: "todo-sst",
region: "us-east-1",
};
},
stacks(app) {
app.stack(function Site({ stack }) {
const table = new Table(stack, "Todos", {
fields: {
id: "string",
content: "string",
completed: "boolean",
},
primaryIndex: { partitionKey: "id" },
});
const site = new NextjsSite(stack, "Site", {
path: ".",
environment: {
TABLE_NAME: table.tableName,
},
});
site.attachPermissions([table]);
stack.addOutputs({
URL: site.url,
});
});
},
} satisfies SSTConfig;
This configuration sets up a DynamoDB table for our todos and a Next.js site with the necessary permissions to access the table.
Step 3: Create API routes
Create a new file pages/api/todos.ts
:
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
import { NextApiRequest, NextApiResponse } from "next";
import { v4 as uuidv4 } from "uuid";
const client = new DynamoDB({});
const docClient = DynamoDBDocument.from(client);
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req;
switch (method) {
case "GET":
try {
const result = await docClient.scan({
TableName: process.env.TABLE_NAME,
});
res.status(200).json(result.Items);
} catch (error) {
res.status(500).json({ error: "Failed to fetch todos" });
}
break;
case "POST":
try {
const { content } = req.body;
const newTodo = {
id: uuidv4(),
content,
completed: false,
};
await docClient.put({
TableName: process.env.TABLE_NAME,
Item: newTodo,
});
res.status(201).json(newTodo);
} catch (error) {
res.status(500).json({ error: "Failed to create todo" });
}
break;
default:
res.setHeader("Allow", ["GET", "POST"]);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
Step 4: Create the frontend
Replace the content of pages/index.tsx
with:
import { useState, useEffect } from "react";
import styles from "../styles/Home.module.css";
interface Todo {
id: string;
content: string;
completed: boolean;
}
export default function Home() {
const [todos, setTodos] = useState<Todo[]>([]);
const [newTodo, setNewTodo] = useState("");
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
const response = await fetch("/api/todos");
const data = await response.json();
setTodos(data);
};
const addTodo = async (e: React.FormEvent) => {
e.preventDefault();
if (!newTodo.trim()) return;
const response = await fetch("/api/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ content: newTodo }),
});
const data = await response.json();
setTodos([...todos, data]);
setNewTodo("");
};
return (
<div className={styles.container}>
<h1>Todo List</h1>
<form onSubmit={addTodo}>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add a new todo"
/>
<button type="submit">Add</button>
</form>
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.content}</li>
))}
</ul>
</div>
);
}
Step 5: Run the application locally
To run the application locally, use the following command:
npx sst dev
This will start the SST development environment and your Next.js application.
Step 6: Deploy to AWS
When you're ready to deploy your application to AWS, run:
npx sst deploy --stage prod
This command will deploy your application to AWS using the production stage.
Conclusion
In this tutorial, we've built a simple To-Do List application using Ion SST and Next.js. We've set up a serverless backend with DynamoDB, created API routes to handle CRUD operations, and built a frontend to interact with our todos.
This example demonstrates the power and simplicity of using SST to deploy serverless applications. You can further enhance this application by adding features like updating and deleting todos, user authentication, and more complex data structures.
Remember to clean up your AWS resources when you're done experimenting to avoid unnecessary charges. You can do this by running npx sst remove --stage prod
.
Happy coding!
Citations: [1] https://sst.dev/blog/moving-away-from-cdk.html [2] https://ion.sst.dev/docs/ [3] https://ion.sst.dev/docs/component/aws/nextjs/ [4] https://ion.sst.dev/docs/start/aws/nextjs/ [5] https://github.com/sst/ion/pulls
Subscribe to my newsletter
Read articles from Osvald Berner directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by